diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index af233a5..b896ef1 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -39,7 +39,10 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: ${{ matrix.dotnet-version }}.x + dotnet-version: | + 6 + 7 + 8 - name: Restore dependencies run: dotnet restore diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d86e5b..f9fb216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,122 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] - yyyy-mm-dd +### Added +- Added RcBinaryMinHeap ([@Sarofc](https://github.com/Sarofc)) +- Added DotRecast.Benchmark ([@Sarofc](https://github.com/Sarofc)) + +### Fixed +- Fix raycast shortcuts ([@Sarofc](https://github.com/Sarofc)) [#72](https://github.com/ikpil/DotRecast/issues/72) +- Fix dynamic mesh bounds calculation ([@ppiastucki](https://github.com/ppiastucki)) [#77](https://github.com/ikpil/DotRecast/issues/77) + - issuer : [@OhJeongrok](https://github.com/OhJeongrok) +- Fix Support non-tiled dynamic nav meshes ([@ppiastucki](https://github.com/ppiastucki)) + +### Changed +- Changed data structure of 'neis' from List to byte[] for optimized memory usage and improved access speed in `DtLayerMonotoneRegion` +- Changed new RcVec3f[3] to stackalloc RcVec3f[3] in DtNavMesh.GetPolyHeight() to reduce heap allocation +- Changed memory handling to use stackalloc in DtNavMeshQuery.GetPolyWallSegments for reducing SOH +- Changed DtNavMeshQuery.GetPolyWallSegments() to use Span for enhanced performance, memory efficiency. +- Changed bmin/bmax from int[] to RcVec3i for improved memory efficiency + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.3.1] - 2024-07-09 + +### Added +- Nothing + +### Fixed +- Fixed bug where the dynamic voxel save file browser doesn't appear in `Recast.Demo` + +### Changed +- Changed to reuse samples and edges list in `BuildPolyDetail()` +- Changed `heights`, `areas`, `cons`, and `regs` arrays to byte arrays for uniformity and efficiency in `DtTileCacheLayer` +- Changed `reg`, `area` arrays to byte arrays for uniformity and efficiency in `DtTileCacheContour` +- Changed `RcChunkyTriMesh` to separate the function and variable. +- Changed to consolidate vector-related functions into one place. +- Changed stack handling from List to a fixed-size array with manual index management for optimization in `RcLayers.BuildHeightfieldLayers()` +- Changed to use Span and stackalloc for improved performance and memory management in `RcLayers.BuildHeightfieldLayers()` +- Changed vertCount and triCount to byte in `DtPolyDetail` +- Changed `new float[]` to `stackalloc float[]` in `DtConvexConvexIntersections.Intersect()` +- Changed agents management from list to dictionary in `DtCrowd` +- Changed to efficiently stack nearby DtCrowdAgents in `DtCrowd.GetNeighbours()` +- Changed to limit neighbor search to a maximum count and use array for memory efficiency in `DtCrowd.AddNeighbour()` + +### Removed +- Removed RcMeshDetails.VdistSq2(float[], float[]) +- Removed RcVecUtils.Dot() +- Removed RcVecUtils.Scale() +- Removed RcVecUtils.Subtract(RcVec3f i, float[] verts, int j) +- Removed RcVecUtils.Subtract(float[] verts, int i, int j) +- Removed RcVecUtils.Min(), RcVecUtils.Max() +- Removed RcVecUtils.Create(float[] values) +- Removed RcVecUtils.Dot2D(this RcVec3f @this, Span v, int vi) + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + +## [2024.2.3] - 2024-06-03 + +### Added +- Added `DtCollectPolysQuery` and `FindCollectPolyTest` + +### Fixed +- Nothing + +### Changed +- Changed `IDtPolyQuery` interface to make `Process()` more versatile +- Changed `PolyQueryInvoker` to `DtActionPolyQuery` +- Changed `DtTileCacheBuilder` to a static class +- Changed `DtTileCacheLayerHeaderReader` to a static class +- Changed `Dictionary>` to `DtMeshTile[]` to optimize memory usage +- Changed `MAX_STEER_POINTS` from class constant to local. +- Changed `List` to `Span` for enhanced memory efficiency +- Changed `DtWriter` to a static class and renamed it to `RcIO` +- Changed class `Trajectory` to interface `ITrajectory` + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.2.2] - 2024-05-18 + +### Added +- Added RcSpans UnitTest + +### Fixed +- Nothing + +### Changed +- Changed class name of static functions to RcRecast and DtDetour +- Changed DtLink class member variable type from int to byte +- Changed initialization in DtNavMesh constructor to Init() function. + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.2.1] - 2024-05-04 + ### Added - Added RcCircularBuffer [@ikpil](https://github.com/ikpil) - Added struct DtCrowdScopedTimer to avoid allocations in scoped timer calls. [@wrenge](https://github.com/wrenge) - Added struct RcScopedTimer to avoid allocations in RcContext scoped timer [@ikpil](https://github.com/ikpil) +- Added RcSpans [@ikpil](https://github.com/ikpil) ### Fixed +- SOH issue [#14](https://github.com/ikpil/DotRecast/issues/41) +- Optimization: reduce number of allocations on hot path. [@awgil](https://github.com/awgil) ### Changed - Changed DtPathCorridor.Init(int maxPath) function to allow setting the maximum path [@ikpil](https://github.com/ikpil) diff --git a/DotRecast.sln b/DotRecast.sln index 8130233..006fc1f 100644 --- a/DotRecast.sln +++ b/DotRecast.sln @@ -39,6 +39,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.Extras.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.TileCache.Test", "test\DotRecast.Detour.TileCache.Test\DotRecast.Detour.TileCache.Test.csproj", "{3CAA7306-088E-4373-A406-99755CC2B605}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Benchmark", "test\DotRecast.Benchmark\DotRecast.Benchmark.csproj", "{D1EFC625-D095-4208-98A2-112B73CB40B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tool", "tool", "{9C213609-BF13-4024-816E-A6ADD641DF24}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Tool.PublishToUniRecast", "tool\DotRecast.Tool.PublishToUniRecast\DotRecast.Tool.PublishToUniRecast.csproj", "{1822BBA8-FE4E-4C5F-B9C0-3E20E6353446}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -112,23 +118,33 @@ Global {10395C8F-DFBD-4263-8A20-EA3500A6E55A}.Debug|Any CPU.Build.0 = Debug|Any CPU {10395C8F-DFBD-4263-8A20-EA3500A6E55A}.Release|Any CPU.ActiveCfg = Release|Any CPU {10395C8F-DFBD-4263-8A20-EA3500A6E55A}.Release|Any CPU.Build.0 = Release|Any CPU + {D1EFC625-D095-4208-98A2-112B73CB40B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1EFC625-D095-4208-98A2-112B73CB40B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1EFC625-D095-4208-98A2-112B73CB40B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1EFC625-D095-4208-98A2-112B73CB40B0}.Release|Any CPU.Build.0 = Release|Any CPU + {1822BBA8-FE4E-4C5F-B9C0-3E20E6353446}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1822BBA8-FE4E-4C5F-B9C0-3E20E6353446}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1822BBA8-FE4E-4C5F-B9C0-3E20E6353446}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1822BBA8-FE4E-4C5F-B9C0-3E20E6353446}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {FFE40BBF-843B-41FA-8504-F4ABD166762E} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {38933A87-4568-40A5-A3DA-E2445E8C2B99} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {C19E4BFA-63A0-4815-9815-869A9DC52DBC} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} - {88754FE2-A05A-4D4D-A81A-90418AD32362} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} - {554CB5BD-D58A-4856-BFE1-666A62C9BEA3} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {FA7EF26A-BA47-43FD-86F8-0A33CFDF643F} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} - {F9C5B52E-C01D-4514-94E9-B1A6895352E2} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {53AF87DA-37F8-4504-B623-B2113F4438CA} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} - {67C68B34-118A-439C-88E1-D6D1ED78DC59} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {17E4F2F0-FC27-416E-9CB6-9F2CAAC49C9D} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} - {7BAA69B2-EDC7-4603-B16F-BC7B24353F81} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {DEB16B90-CCD4-497E-A2E9-4CC66FD7EF47} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} - {3CAA7306-088E-4373-A406-99755CC2B605} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {023E1E6A-4895-4573-89AE-3D5D8E0B39C8} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {DF987948-8C23-4337-AF83-D87D6407518D} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} + {88754FE2-A05A-4D4D-A81A-90418AD32362} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {554CB5BD-D58A-4856-BFE1-666A62C9BEA3} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {F9C5B52E-C01D-4514-94E9-B1A6895352E2} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {67C68B34-118A-439C-88E1-D6D1ED78DC59} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {7BAA69B2-EDC7-4603-B16F-BC7B24353F81} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {3CAA7306-088E-4373-A406-99755CC2B605} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {10395C8F-DFBD-4263-8A20-EA3500A6E55A} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {D1EFC625-D095-4208-98A2-112B73CB40B0} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {1822BBA8-FE4E-4C5F-B9C0-3E20E6353446} = {9C213609-BF13-4024-816E-A6ADD641DF24} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 143e8dd..8cc246e 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,22 @@ -

DotRecast

-

-DotRecast is C# Recast & Detour, a port of recastnavigation and recast4j to the C# language. -

-

-If you'd like to support the project, we'd appreciate starring(⭐) our repos on Github for more visibility. -

+# DotRecast + +*DotRecast is C# Recast & Detour, a port of [recastnavigation](https://github.com/recastnavigation/recastnavigation) and [recast4j](https://github.com/ppiastucki/recast4j) to the C# language.* +*If you'd like to support the project, we'd appreciate starring(⭐) our repos on Github for more visibility.* --- -

-![GitHub License] -Languages -GitHub repo size -GitHub Repo stars -GitHub Actions Workflow Status -GitHub Actions Workflow Status -GitHub commit activity -GitHub issues -GitHub closed issues -NuGet Version -NuGet Downloads -Visitors -GitHub Sponsors -

+![GitHub License](https://img.shields.io/github/license/ikpil/DotRecast?style=for-the-badge) +![Languages](https://img.shields.io/github/languages/top/ikpil/DotRecast?style=for-the-badge) +![GitHub repo size](https://img.shields.io/github/repo-size/ikpil/DotRecast?style=for-the-badge) +[![GitHub Repo stars](https://img.shields.io/github/stars/ikpil/DotRecast?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/dotnet.yml?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast/actions/workflows/dotnet.yml) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/codeql.yml?style=for-the-badge&logo=github&label=CODEQL)](https://github.com/ikpil/DotRecast/actions/workflows/codeql.yml) +[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ikpil/DotRecast?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast/commits) +[![GitHub issues](https://img.shields.io/github/issues-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=44cc11)](https://github.com/ikpil/DotRecast/issues) +[![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=a371f7)](https://github.com/ikpil/DotRecast/issues) +[![NuGet Version](https://img.shields.io/nuget/vpre/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core) +[![NuGet Downloads](https://img.shields.io/nuget/dt/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core) +[![GitHub Sponsors](https://img.shields.io/github/sponsors/ikpil?style=for-the-badge&logo=GitHub-Sponsors&link=https%3A%2F%2Fgithub.com%2Fsponsors%2Fikpil)](https://github.com/sponsors/ikpil) --- diff --git a/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs b/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs index 343e821..579c7e3 100644 --- a/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs +++ b/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Net.Security; diff --git a/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs b/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs index fc94285..8f807d2 100644 --- a/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs +++ b/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using System.Runtime.InteropServices; diff --git a/src/DotRecast.Core/Buffers/RcRentedArray.cs b/src/DotRecast.Core/Buffers/RcRentedArray.cs index 85800c5..68f12c7 100644 --- a/src/DotRecast.Core/Buffers/RcRentedArray.cs +++ b/src/DotRecast.Core/Buffers/RcRentedArray.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.Runtime.CompilerServices; diff --git a/src/DotRecast.Core/Collections/CollectionExtensions.cs b/src/DotRecast.Core/Collections/CollectionExtensions.cs index 24d01fc..82f5f5c 100644 --- a/src/DotRecast.Core/Collections/CollectionExtensions.cs +++ b/src/DotRecast.Core/Collections/CollectionExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs b/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs new file mode 100644 index 0000000..900fd54 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public sealed class RcBinaryMinHeap + { + private readonly List _items; + private readonly Comparison _comparision; + + public int Count => _items.Count; + public int Capacity => _items.Capacity; + + public RcBinaryMinHeap(Comparison comparision) + { + _items = new List(); + _comparision = comparision; + } + + public RcBinaryMinHeap(int capacity, Comparison comparison) : this(comparison) + { + if (capacity <= 0) + throw new ArgumentException("capacity must greater than zero"); + + _items = new List(capacity); + _comparision = comparison; + } + + public void Push(T val) + { + _items.Add(val); + SiftUp(_items.Count - 1); + } + + public T Pop() + { + var min = Peek(); + RemoveMin(); + return min; + } + + private void RemoveMin() + { + if (_items.Count == 0) + { + Throw(); + static void Throw() => throw new InvalidOperationException("no element to pop"); + } + + int last = _items.Count - 1; + Swap(0, last); + _items.RemoveAt(last); + + MinHeapify(0, last - 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Top() + { + return _items[0]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek() + { + if (IsEmpty()) + { + throw new Exception("Heap is empty."); + } + + return _items[0]; + } + + + public bool Modify(T node) + { + for (int i = 0; i < _items.Count; i++) + { + if (_items[i].Equals(node)) + { + SiftUp(i); + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _items.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsEmpty() + { + return 0 == _items.Count; + } + + private void SiftUp(int nodeIndex) + { + int parent = (nodeIndex - 1) / 2; + while (_comparision.Invoke(_items[nodeIndex], _items[parent]) < 0) + { + Swap(parent, nodeIndex); + nodeIndex = parent; + parent = (nodeIndex - 1) / 2; + } + } + + + private void MinHeapify(int nodeIndex, int lastIndex) + { + int left = (nodeIndex * 2) + 1; + int right = left + 1; + int smallest = nodeIndex; + + if (left <= lastIndex && _comparision.Invoke(_items[left], _items[nodeIndex]) < 0) + smallest = left; + + if (right <= lastIndex && _comparision.Invoke(_items[right], _items[smallest]) < 0) + smallest = right; + + if (smallest == nodeIndex) + return; + + Swap(nodeIndex, smallest); + MinHeapify(smallest, lastIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Swap(int x, int y) + { + if (x == y) + return; + + (_items[y], _items[x]) = (_items[x], _items[y]); + } + + + public T[] ToArray() + { + return _items.ToArray(); + } + + public List ToList() + { + return new List(_items); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs index c9ba186..be4a11f 100644 --- a/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs index 253a5e1..f917dc9 100644 --- a/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs index 335f6c1..5e51680 100644 --- a/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Core.Collections +namespace DotRecast.Core.Collections { public readonly partial struct RcImmutableArray { diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.cs b/src/DotRecast.Core/Collections/RcImmutableArray.cs index 65fa86c..33f7443 100644 --- a/src/DotRecast.Core/Collections/RcImmutableArray.cs +++ b/src/DotRecast.Core/Collections/RcImmutableArray.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DotRecast.Core.Collections { diff --git a/src/DotRecast.Core/Collections/RcSortedQueue.cs b/src/DotRecast.Core/Collections/RcSortedQueue.cs index 60cc406..0c06aad 100644 --- a/src/DotRecast.Core/Collections/RcSortedQueue.cs +++ b/src/DotRecast.Core/Collections/RcSortedQueue.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/Collections/RcStackArray128.cs b/src/DotRecast.Core/Collections/RcStackArray128.cs index d80afad..f1eeeb0 100644 --- a/src/DotRecast.Core/Collections/RcStackArray128.cs +++ b/src/DotRecast.Core/Collections/RcStackArray128.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray16.cs b/src/DotRecast.Core/Collections/RcStackArray16.cs index df3cb4f..45b5aa6 100644 --- a/src/DotRecast.Core/Collections/RcStackArray16.cs +++ b/src/DotRecast.Core/Collections/RcStackArray16.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray2.cs b/src/DotRecast.Core/Collections/RcStackArray2.cs index 30affc0..b04374b 100644 --- a/src/DotRecast.Core/Collections/RcStackArray2.cs +++ b/src/DotRecast.Core/Collections/RcStackArray2.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray256.cs b/src/DotRecast.Core/Collections/RcStackArray256.cs index 8dec9b9..07d2048 100644 --- a/src/DotRecast.Core/Collections/RcStackArray256.cs +++ b/src/DotRecast.Core/Collections/RcStackArray256.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray32.cs b/src/DotRecast.Core/Collections/RcStackArray32.cs index acd5e06..ea5d5d9 100644 --- a/src/DotRecast.Core/Collections/RcStackArray32.cs +++ b/src/DotRecast.Core/Collections/RcStackArray32.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray4.cs b/src/DotRecast.Core/Collections/RcStackArray4.cs index 38d5133..40182e7 100644 --- a/src/DotRecast.Core/Collections/RcStackArray4.cs +++ b/src/DotRecast.Core/Collections/RcStackArray4.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray512.cs b/src/DotRecast.Core/Collections/RcStackArray512.cs index 95b59ae..e8c4be5 100644 --- a/src/DotRecast.Core/Collections/RcStackArray512.cs +++ b/src/DotRecast.Core/Collections/RcStackArray512.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray64.cs b/src/DotRecast.Core/Collections/RcStackArray64.cs index 5aa88a3..55dbd08 100644 --- a/src/DotRecast.Core/Collections/RcStackArray64.cs +++ b/src/DotRecast.Core/Collections/RcStackArray64.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/Collections/RcStackArray8.cs b/src/DotRecast.Core/Collections/RcStackArray8.cs index 75e1b15..bdaf0cc 100644 --- a/src/DotRecast.Core/Collections/RcStackArray8.cs +++ b/src/DotRecast.Core/Collections/RcStackArray8.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Collections diff --git a/src/DotRecast.Core/IRcCompressor.cs b/src/DotRecast.Core/IRcCompressor.cs index 2eda567..ef0bea7 100644 --- a/src/DotRecast.Core/IRcCompressor.cs +++ b/src/DotRecast.Core/IRcCompressor.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/IRcRand.cs b/src/DotRecast.Core/IRcRand.cs index 15e810a..723eb65 100644 --- a/src/DotRecast.Core/IRcRand.cs +++ b/src/DotRecast.Core/IRcRand.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Core +namespace DotRecast.Core { public interface IRcRand { diff --git a/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs b/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs index 2a09dec..648cad6 100644 --- a/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs +++ b/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Numerics diff --git a/src/DotRecast.Core/Numerics/RcVecUtils.cs b/src/DotRecast.Core/Numerics/RcVec.cs similarity index 75% rename from src/DotRecast.Core/Numerics/RcVecUtils.cs rename to src/DotRecast.Core/Numerics/RcVec.cs index bbe9efd..69aae7d 100644 --- a/src/DotRecast.Core/Numerics/RcVecUtils.cs +++ b/src/DotRecast.Core/Numerics/RcVec.cs @@ -1,20 +1,15 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core.Numerics { - public static class RcVecUtils + public static class RcVec { public const float EPSILON = 1e-6f; + public static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Create(float[] values) - { - return Create(values, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Create(float[] values, int n) + public static RcVec3f Create(Span values, int n) { return new RcVec3f(values[n + 0], values[n + 1], values[n + 2]); } @@ -42,10 +37,73 @@ namespace DotRecast.Core.Numerics } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Scale(this RcVec3f v, float scale) + /// Performs a 'sloppy' colocation check of the specified points. + /// @param[in] p0 A point. [(x, y, z)] + /// @param[in] p1 A point. [(x, y, z)] + /// @return True if the points are considered to be at the same location. + /// + /// Basically, this function will return true if the specified points are + /// close enough to eachother to be considered colocated. + public static bool Equal(RcVec3f p0, RcVec3f p1) { - return v * scale; + float d = RcVec3f.DistanceSquared(p0, p1); + return d < EQUAL_THRESHOLD; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot2(RcVec3f a, RcVec3f b) + { + return a.X * b.X + a.Z * b.Z; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistSq2(float[] verts, int p, int q) + { + float dx = verts[q + 0] - verts[p + 0]; + float dy = verts[q + 2] - verts[p + 2]; + return dx * dx + dy * dy; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dist2(float[] verts, int p, int q) + { + return MathF.Sqrt(DistSq2(verts, p, q)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistSq2(RcVec3f p, RcVec3f q) + { + float dx = q.X - p.X; + float dy = q.Z - p.Z; + return dx * dx + dy * dy; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dist2(RcVec3f p, RcVec3f q) + { + return MathF.Sqrt(DistSq2(p, q)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cross2(float[] verts, int p1, int p2, int p3) + { + float u1 = verts[p2 + 0] - verts[p1 + 0]; + float v1 = verts[p2 + 2] - verts[p1 + 2]; + float u2 = verts[p3 + 0] - verts[p1 + 0]; + float v2 = verts[p3 + 2] - verts[p1 + 2]; + return u1 * v2 - v1 * u2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Cross2(RcVec3f p1, RcVec3f p2, RcVec3f p3) + { + float u1 = p2.X - p1.X; + float v1 = p2.Z - p1.Z; + float u2 = p3.X - p1.X; + float v2 = p3.Z - p1.Z; + return u1 * v2 - v1 * u2; } /// Derives the dot product of two vectors on the xz-plane. (@p u . @p v) @@ -62,46 +120,6 @@ namespace DotRecast.Core.Numerics @this.Z * v.Z; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot2D(this RcVec3f @this, float[] v, int vi) - { - return @this.X * v[vi] + - @this.Z * v[vi + 2]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Add(RcVec3f a, float[] verts, int i) - { - return new RcVec3f( - a.X + verts[i], - a.Y + verts[i + 1], - a.Z + verts[i + 2] - ); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Subtract(float[] verts, int i, int j) - { - return new RcVec3f( - verts[i] - verts[j], - verts[i + 1] - verts[j + 1], - verts[i + 2] - verts[j + 2] - ); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Subtract(RcVec3f i, float[] verts, int j) - { - return new RcVec3f( - i.X - verts[j], - i.Y - verts[j + 1], - i.Z - verts[j + 2] - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Cross(float[] dest, float[] v1, float[] v2) { @@ -117,21 +135,13 @@ namespace DotRecast.Core.Numerics @out[n + 1] = @in[m + 1]; @out[n + 2] = @in[m + 2]; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(float[] v1, float[] v2) + public static void Copy(Span @out, int n, Span @in, int m) { - return v1[0] * v2[0] + - v1[1] * v2[1] + - v1[2] * v2[2]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(float[] v1, RcVec3f vector2) - { - return v1[0] * vector2.X + - v1[1] * vector2.Y + - v1[2] * vector2.Z; + @out[n + 0] = @in[m + 0]; + @out[n + 1] = @in[m + 1]; + @out[n + 2] = @in[m + 2]; } /// Returns the distance between two points. @@ -167,26 +177,6 @@ namespace DotRecast.Core.Numerics return v; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Min(RcVec3f v, float[] @in, int i) - { - return new RcVec3f( - (v.X < @in[i + 0]) ? v.X : @in[i + 0], - (v.Y < @in[i + 1]) ? v.Y : @in[i + 1], - (v.Z < @in[i + 2]) ? v.Z : @in[i + 2] - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Max(RcVec3f v, float[] @in, int i) - { - return new RcVec3f( - (v.X > @in[i + 0]) ? v.X : @in[i + 0], - (v.Y > @in[i + 1]) ? v.Y : @in[i + 1], - (v.Z > @in[i + 2]) ? v.Z : @in[i + 2] - ); - } - /// Derives the distance between the specified points on the xz-plane. /// @param[in] v1 A point. [(x, y, z)] /// @param[in] v2 A point. [(x, y, z)] @@ -202,6 +192,10 @@ namespace DotRecast.Core.Numerics return (float)MathF.Sqrt(dx * dx + dz * dz); } + /// Derives the square of the distance between the specified points on the xz-plane. + /// @param[in] v1 A point. [(x, y, z)] + /// @param[in] v2 A point. [(x, y, z)] + /// @return The square of the distance between the point on the xz-plane. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Dist2DSqr(RcVec3f v1, RcVec3f v2) { @@ -262,7 +256,7 @@ namespace DotRecast.Core.Numerics /// @param[in] v2 The destination vector. /// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Lerp(float[] verts, int v1, int v2, float t) + public static RcVec3f Lerp(Span verts, int v1, int v2, float t) { return new RcVec3f( verts[v1 + 0] + (verts[v2 + 0] - verts[v1 + 0]) * t, diff --git a/src/DotRecast.Core/Numerics/RcVec3f.cs b/src/DotRecast.Core/Numerics/RcVec3f.cs index 5e8d86a..d928945 100644 --- a/src/DotRecast.Core/Numerics/RcVec3f.cs +++ b/src/DotRecast.Core/Numerics/RcVec3f.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -49,6 +50,19 @@ namespace DotRecast.Core.Numerics Z = f; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RcVec3f(ReadOnlySpan values) + { + if (values.Length < 3) + { + RcThrowHelper.ThrowArgumentOutOfRangeException(nameof(values)); + } + + X = values[0]; + Y = values[1]; + Z = values[2]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly float Length() diff --git a/src/DotRecast.Core/Numerics/RcVec3i.cs b/src/DotRecast.Core/Numerics/RcVec3i.cs new file mode 100644 index 0000000..673edf2 --- /dev/null +++ b/src/DotRecast.Core/Numerics/RcVec3i.cs @@ -0,0 +1,9 @@ +namespace DotRecast.Core.Numerics +{ + public struct RcVec3i + { + public int X; + public int Y; + public int Z; + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcArrays.cs b/src/DotRecast.Core/RcArrays.cs index e8c291a..af5452d 100644 --- a/src/DotRecast.Core/RcArrays.cs +++ b/src/DotRecast.Core/RcArrays.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core diff --git a/src/DotRecast.Core/RcAtomicBoolean.cs b/src/DotRecast.Core/RcAtomicBoolean.cs index a2bb9f3..b8f6d37 100644 --- a/src/DotRecast.Core/RcAtomicBoolean.cs +++ b/src/DotRecast.Core/RcAtomicBoolean.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcAtomicFloat.cs b/src/DotRecast.Core/RcAtomicFloat.cs index 21e7886..3c92490 100644 --- a/src/DotRecast.Core/RcAtomicFloat.cs +++ b/src/DotRecast.Core/RcAtomicFloat.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcAtomicInteger.cs b/src/DotRecast.Core/RcAtomicInteger.cs index 46596ef..468ea13 100644 --- a/src/DotRecast.Core/RcAtomicInteger.cs +++ b/src/DotRecast.Core/RcAtomicInteger.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcAtomicLong.cs b/src/DotRecast.Core/RcAtomicLong.cs index f224ffa..5eb22e8 100644 --- a/src/DotRecast.Core/RcAtomicLong.cs +++ b/src/DotRecast.Core/RcAtomicLong.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; namespace DotRecast.Core diff --git a/src/DotRecast.Core/RcByteOrder.cs b/src/DotRecast.Core/RcByteOrder.cs index 0313a63..ef16ae2 100644 --- a/src/DotRecast.Core/RcByteOrder.cs +++ b/src/DotRecast.Core/RcByteOrder.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Core +namespace DotRecast.Core { public enum RcByteOrder { diff --git a/src/DotRecast.Core/RcByteUtils.cs b/src/DotRecast.Core/RcByteUtils.cs index ec525f0..0113526 100644 --- a/src/DotRecast.Core/RcByteUtils.cs +++ b/src/DotRecast.Core/RcByteUtils.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/RcContext.cs b/src/DotRecast.Core/RcContext.cs index 8d707d4..e9a8ce9 100644 --- a/src/DotRecast.Core/RcContext.cs +++ b/src/DotRecast.Core/RcContext.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/RcConvexUtils.cs b/src/DotRecast.Core/RcConvexUtils.cs index e1a300b..4fa71b2 100644 --- a/src/DotRecast.Core/RcConvexUtils.cs +++ b/src/DotRecast.Core/RcConvexUtils.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/RcDirectory.cs b/src/DotRecast.Core/RcDirectory.cs index 46a5362..f26eb1f 100644 --- a/src/DotRecast.Core/RcDirectory.cs +++ b/src/DotRecast.Core/RcDirectory.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Linq; namespace DotRecast.Core diff --git a/src/DotRecast.Core/RcEdge.cs b/src/DotRecast.Core/RcEdge.cs index ebfd7d8..6838252 100644 --- a/src/DotRecast.Core/RcEdge.cs +++ b/src/DotRecast.Core/RcEdge.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Core +namespace DotRecast.Core { public class RcEdge { diff --git a/src/DotRecast.Core/RcFrequency.cs b/src/DotRecast.Core/RcFrequency.cs index eae3a32..9a8533b 100644 --- a/src/DotRecast.Core/RcFrequency.cs +++ b/src/DotRecast.Core/RcFrequency.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; namespace DotRecast.Core diff --git a/src/DotRecast.Core/RcHashCodes.cs b/src/DotRecast.Core/RcHashCodes.cs index 3202c9a..c86426c 100644 --- a/src/DotRecast.Core/RcHashCodes.cs +++ b/src/DotRecast.Core/RcHashCodes.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcIO.cs b/src/DotRecast.Core/RcIO.cs new file mode 100644 index 0000000..f08f89c --- /dev/null +++ b/src/DotRecast.Core/RcIO.cs @@ -0,0 +1,165 @@ +/* +Recast4J Copyright (c) 2015 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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.IO; + +namespace DotRecast.Core +{ + public static class RcIO + { + public static RcByteBuffer ToByteBuffer(BinaryReader br, bool direct) + { + byte[] data = ToByteArray(br); + if (direct) + { + Array.Reverse(data); + } + + return new RcByteBuffer(data); + } + + public static byte[] ToByteArray(BinaryReader br) + { + using var ms = new MemoryStream(); + Span buffer = stackalloc byte[4096]; + int l; + while ((l = br.Read(buffer)) > 0) + { + ms.Write(buffer.Slice(0, l)); + } + + return ms.ToArray(); + } + + + public static RcByteBuffer ToByteBuffer(BinaryReader br) + { + var bytes = ToByteArray(br); + return new RcByteBuffer(bytes); + } + + public static int SwapEndianness(int i) + { + var s = (((uint)i >> 24) & 0xFF) | (((uint)i >> 8) & 0xFF00) | (((uint)i << 8) & 0xFF0000) | ((i << 24) & 0xFF000000); + return (int)s; + } + + public static byte[] ReadFileIfFound(string filename) + { + if (string.IsNullOrEmpty(filename)) + return null; + + string filePath = filename; + + if (!File.Exists(filePath)) + { + var searchFilePath = RcDirectory.SearchFile($"{filename}"); + if (!File.Exists(searchFilePath)) + { + searchFilePath = RcDirectory.SearchFile($"resources/{filename}"); + } + + if (File.Exists(searchFilePath)) + { + filePath = searchFilePath; + } + } + + using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + byte[] buffer = new byte[fs.Length]; + var read = fs.Read(buffer, 0, buffer.Length); + if (read != buffer.Length) + return null; + + return buffer; + } + + public static void Write(BinaryWriter ws, float value, RcByteOrder order) + { + byte[] bytes = BitConverter.GetBytes(value); + int i = BitConverter.ToInt32(bytes, 0); + Write(ws, i, order); + } + + public static void Write(BinaryWriter ws, short value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)(value & 0xFF)); + } + else + { + ws.Write((byte)(value & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + } + } + + public static void Write(BinaryWriter ws, long value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + Write(ws, (int)((ulong)value >> 32), order); + Write(ws, (int)(value & 0xFFFFFFFF), order); + } + else + { + Write(ws, (int)(value & 0xFFFFFFFF), order); + Write(ws, (int)((ulong)value >> 32), order); + } + } + + public static void Write(BinaryWriter ws, int value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + ws.Write((byte)((value >> 24) & 0xFF)); + ws.Write((byte)((value >> 16) & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)(value & 0xFF)); + } + else + { + ws.Write((byte)(value & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)((value >> 16) & 0xFF)); + ws.Write((byte)((value >> 24) & 0xFF)); + } + } + + public static void Write(BinaryWriter ws, bool value) + { + Write(ws, (byte)(value ? 1 : 0)); + } + + public static void Write(BinaryWriter ws, byte value) + { + ws.Write(value); + } + + public static void Write(BinaryWriter ws, MemoryStream ms) + { + ms.Position = 0; + byte[] buffer = new byte[ms.Length]; + ms.Read(buffer, 0, buffer.Length); + ws.Write(buffer); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcIntersections.cs b/src/DotRecast.Core/RcIntersections.cs index 9c5928d..55cf2b3 100644 --- a/src/DotRecast.Core/RcIntersections.cs +++ b/src/DotRecast.Core/RcIntersections.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/RcMath.cs b/src/DotRecast.Core/RcMath.cs index dd21cd0..b981bd1 100644 --- a/src/DotRecast.Core/RcMath.cs +++ b/src/DotRecast.Core/RcMath.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -29,5 +29,11 @@ namespace DotRecast.Core { return f * f; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Lerp(float value1, float value2, float amount) + { + return (value1 * (1.0f - amount)) + (value2 * amount); + } } } \ No newline at end of file diff --git a/src/DotRecast.Core/RcObjImporter.cs b/src/DotRecast.Core/RcObjImporter.cs index 761255c..43d7045 100644 --- a/src/DotRecast.Core/RcObjImporter.cs +++ b/src/DotRecast.Core/RcObjImporter.cs @@ -1,6 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Core/RcObjImporterContext.cs b/src/DotRecast.Core/RcObjImporterContext.cs index a08deaf..d79750e 100644 --- a/src/DotRecast.Core/RcObjImporterContext.cs +++ b/src/DotRecast.Core/RcObjImporterContext.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcProcess.cs b/src/DotRecast.Core/RcProcess.cs index 05987ae..00df319 100644 --- a/src/DotRecast.Core/RcProcess.cs +++ b/src/DotRecast.Core/RcProcess.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Runtime.InteropServices; diff --git a/src/DotRecast.Core/RcRand.cs b/src/DotRecast.Core/RcRand.cs index 6111aff..b523e13 100644 --- a/src/DotRecast.Core/RcRand.cs +++ b/src/DotRecast.Core/RcRand.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcResources.cs b/src/DotRecast.Core/RcResources.cs deleted file mode 100644 index 2b33f7c..0000000 --- a/src/DotRecast.Core/RcResources.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.IO; - -namespace DotRecast.Core -{ - public static class RcResources - { - public static byte[] Load(string filename) - { - var filepath = filename; - if (!File.Exists(filepath)) - { - filepath = RcDirectory.SearchFile($"resources/{filename}"); - } - - using var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read); - byte[] buffer = new byte[fs.Length]; - fs.Read(buffer, 0, buffer.Length); - - return buffer; - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Core/RcScopedTimer.cs b/src/DotRecast.Core/RcScopedTimer.cs index 6b84e28..5848c75 100644 --- a/src/DotRecast.Core/RcScopedTimer.cs +++ b/src/DotRecast.Core/RcScopedTimer.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcSegmentVert.cs b/src/DotRecast.Core/RcSegmentVert.cs index 7cab398..10dca98 100644 --- a/src/DotRecast.Core/RcSegmentVert.cs +++ b/src/DotRecast.Core/RcSegmentVert.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcSpans.cs b/src/DotRecast.Core/RcSpans.cs index 474f492..3222a1a 100644 --- a/src/DotRecast.Core/RcSpans.cs +++ b/src/DotRecast.Core/RcSpans.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; namespace DotRecast.Core @@ -6,17 +6,31 @@ namespace DotRecast.Core public static class RcSpans { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(Span source, Span destination) + public static void Copy(Span src, Span dst) { - Copy(source, 0, destination, 0, source.Length); + src.CopyTo(dst); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(Span src, int srcIdx, Span dst, int dstIdx, int length) + { + var slicedSrc = src.Slice(srcIdx, length); + var slicedDst = dst.Slice(dstIdx); + slicedSrc.CopyTo(slicedDst); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Move(Span src, int srcIdx, int dstIdx, int length) + { + var slicedSrc = src.Slice(srcIdx, length); + var slicedDst = src.Slice(dstIdx, length); + slicedSrc.CopyTo(slicedDst); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(Span source, int sourceIdx, Span destination, int destinationIdx, int length) + public static void Fill(Span span, T value, int start, int count) { - var src = source.Slice(sourceIdx, length); - var dst = destination.Slice(destinationIdx); - src.CopyTo(dst); + span.Slice(start, count).Fill(value); } } } \ No newline at end of file diff --git a/src/DotRecast.Core/RcTelemetryTick.cs b/src/DotRecast.Core/RcTelemetryTick.cs index 535f086..06ccadc 100644 --- a/src/DotRecast.Core/RcTelemetryTick.cs +++ b/src/DotRecast.Core/RcTelemetryTick.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DotRecast.Core { diff --git a/src/DotRecast.Core/RcThrowHelper.cs b/src/DotRecast.Core/RcThrowHelper.cs index 4eb6526..d69c433 100644 --- a/src/DotRecast.Core/RcThrowHelper.cs +++ b/src/DotRecast.Core/RcThrowHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using DotRecast.Core.Collections; @@ -15,6 +15,12 @@ namespace DotRecast.Core } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowArgumentOutOfRangeException(string argument) + { + throw new ArgumentOutOfRangeException(argument); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void StackOverflow() { diff --git a/src/DotRecast.Core/RcTimerLabel.cs b/src/DotRecast.Core/RcTimerLabel.cs index 4492cf0..0fbe4ef 100644 --- a/src/DotRecast.Core/RcTimerLabel.cs +++ b/src/DotRecast.Core/RcTimerLabel.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Core +namespace DotRecast.Core { /// Recast performance timer categories. /// @see rcContext diff --git a/src/DotRecast.Detour.Crowd/DtCrowd.cs b/src/DotRecast.Detour.Crowd/DtCrowd.cs index d908d1a..84bf558 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowd.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowd.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -20,7 +20,7 @@ freely, subject to the following restrictions: using System; using System.Collections.Generic; -using System.Threading.Tasks; +using System.Diagnostics; using DotRecast.Core; using DotRecast.Core.Buffers; using DotRecast.Core.Collections; @@ -121,8 +121,9 @@ namespace DotRecast.Detour.Crowd /// @ingroup crowd public class DtCrowd { - private readonly RcAtomicInteger _agentId = new RcAtomicInteger(); - private readonly List _agents; + private readonly RcAtomicInteger _agentIdx; + private readonly Dictionary _agents; + private readonly List _activeAgents; private readonly DtPathQueue _pathQ; @@ -171,7 +172,9 @@ namespace DotRecast.Detour.Crowd // Allocate temp buffer for merging paths. _maxPathResult = DtCrowdConst.MAX_PATH_RESULT; _pathQ = new DtPathQueue(config); - _agents = new List(); + _agentIdx = new RcAtomicInteger(0); + _agents = new Dictionary(); + _activeAgents = new List(); // The navQuery is mostly used for local searches, no need for large node pool. SetNavMesh(nav); @@ -236,11 +239,10 @@ namespace DotRecast.Detour.Crowd /// @return The index of the agent in the agent pool. Or -1 if the agent could not be added. public DtCrowdAgent AddAgent(RcVec3f pos, DtCrowdAgentParams option) { - int idx = _agentId.GetAndIncrement(); + int idx = _agentIdx.GetAndIncrement(); DtCrowdAgent ag = new DtCrowdAgent(idx); ag.corridor.Init(_maxPathResult); - _agents.Add(ag); - + AddAgent(ag); UpdateAgentParameters(ag, option); // Find nearest position on navmesh and place the agent there. @@ -257,6 +259,7 @@ namespace DotRecast.Detour.Crowd ag.topologyOptTime = 0; ag.targetReplanTime = 0; + ag.nneis = 0; ag.dvel = RcVec3f.Zero; ag.nvel = RcVec3f.Zero; @@ -279,15 +282,27 @@ namespace DotRecast.Detour.Crowd return ag; } - /** - * Removes the agent from the crowd. - * - * @param agent - * Agent to be removed - */ + public DtCrowdAgent GetAgent(int idx) + { + return _agents.GetValueOrDefault(idx); + } + + // Add the agent from the crowd. + public void AddAgent(DtCrowdAgent agent) + { + if (_agents.TryAdd(agent.idx, agent)) + { + _activeAgents.Add(agent); + } + } + + // Removes the agent from the crowd. public void RemoveAgent(DtCrowdAgent agent) { - _agents.Remove(agent); + if (_agents.Remove(agent.idx)) + { + _activeAgents.Remove(agent); + } } private bool RequestMoveTargetReplan(DtCrowdAgent ag, long refs, RcVec3f pos) @@ -359,7 +374,7 @@ namespace DotRecast.Detour.Crowd */ public IList GetActiveAgents() { - return _agents; + return _activeAgents; } public RcVec3f GetQueryExtents() @@ -590,7 +605,7 @@ namespace DotRecast.Detour.Crowd if (ag.targetReplan) // && npath > 10) { // Try to use existing steady path during replan if possible. - status = _navQuery.FinalizeSlicedFindPathPartial(path, ref reqPath); + status = _navQuery.FinalizeSlicedFindPathPartial(path, path.Count, ref reqPath); } else { @@ -876,7 +891,7 @@ namespace DotRecast.Detour.Crowd // Update the collision boundary after certain distance has been passed or // if it has become invalid. float updateThr = ag.option.collisionQueryRange * 0.25f; - if (RcVecUtils.Dist2DSqr(ag.npos, ag.boundary.GetCenter()) > RcMath.Sqr(updateThr) + if (RcVec.Dist2DSqr(ag.npos, ag.boundary.GetCenter()) > RcMath.Sqr(updateThr) || !ag.boundary.IsValid(_navQuery, _filters[ag.option.queryFilterType])) { ag.boundary.Update(ag.corridor.GetFirstPoly(), ag.npos, ag.option.collisionQueryRange, _navQuery, @@ -884,21 +899,66 @@ namespace DotRecast.Detour.Crowd } // Query neighbour agents - GetNeighbours(ag.npos, ag.option.height, ag.option.collisionQueryRange, ag, ref ag.neis, _grid); + ag.nneis = GetNeighbours(ag.npos, ag.option.height, ag.option.collisionQueryRange, ag, ag.neis, DtCrowdConst.DT_CROWDAGENT_MAX_NEIGHBOURS, _grid); } } - - private int GetNeighbours(RcVec3f pos, float height, float range, DtCrowdAgent skip, ref List result, DtProximityGrid grid) + public static int AddNeighbour(DtCrowdAgent idx, float dist, Span neis, int nneis, int maxNeis) { - result.Clear(); + // Insert neighbour based on the distance. + int nei = 0; + if (0 == nneis) + { + nei = nneis; + } + else if (dist >= neis[nneis - 1].dist) + { + if (nneis >= maxNeis) + return nneis; + nei = nneis; + } + else + { + int i; + for (i = 0; i < nneis; ++i) + { + if (dist <= neis[i].dist) + { + break; + } + } + + int tgt = i + 1; + int n = Math.Min(nneis - i, maxNeis - tgt); + + Debug.Assert(tgt + n <= maxNeis); + + if (n > 0) + { + RcSpans.Move(neis, i, tgt, n); + } + + nei = i; + } + + neis[nei] = new DtCrowdNeighbour(idx, dist); + + return Math.Min(nneis + 1, maxNeis); + } + + private int GetNeighbours(RcVec3f pos, float height, float range, DtCrowdAgent skip, DtCrowdNeighbour[] result, int maxResult, DtProximityGrid grid) + { + int n = 0; + + const int MAX_NEIS = 32; + Span ids = stackalloc int[MAX_NEIS]; + int nids = grid.QueryItems(pos.X - range, pos.Z - range, + pos.X + range, pos.Z + range, + ids, ids.Length); - int MAX_NEIS = 32; - var ids = new DtCrowdAgent[MAX_NEIS]; - int nids = grid.QueryItems(pos.X - range, pos.Z - range, pos.X + range, pos.Z + range, ids, ids.Length); for (int i = 0; i < nids; ++i) { - var ag = ids[i]; + var ag = GetAgent(ids[i]); if (ag == skip) { continue; @@ -918,11 +978,10 @@ namespace DotRecast.Detour.Crowd continue; } - result.Add(new DtCrowdNeighbour(ag, distSqr)); + n = AddNeighbour(ag, distSqr, result, n, maxResult); } - result.Sort((o1, o2) => o1.dist.CompareTo(o2.dist)); - return result.Count; + return n; } private void FindCorners(IList agents, DtCrowdAgentDebugInfo debug) @@ -945,13 +1004,13 @@ namespace DotRecast.Detour.Crowd } // Find corners for steering - ag.corridor.FindCorners(ref ag.corners, DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]); + ag.ncorners = ag.corridor.FindCorners(ag.corners, DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]); // Check to see if the corner after the next corner is directly visible, // and short cut to there. - if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OPTIMIZE_VIS) != 0 && ag.corners.Count > 0) + if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OPTIMIZE_VIS) != 0 && ag.ncorners > 0) { - RcVec3f target = ag.corners[Math.Min(1, ag.corners.Count - 1)].pos; + RcVec3f target = ag.corners[Math.Min(1, ag.ncorners - 1)].pos; ag.corridor.OptimizePathVisibility(target, ag.option.pathOptimizationRange, _navQuery, _filters[ag.option.queryFilterType]); @@ -1001,18 +1060,18 @@ namespace DotRecast.Detour.Crowd // Adjust the path over the off-mesh connection. long[] refs = new long[2]; - if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.corners.Count - 1].refs, refs, ref anim.startPos, + if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.ncorners - 1].refs, refs, ref anim.startPos, ref anim.endPos, _navQuery)) { anim.initPos = ag.npos; anim.polyRef = refs[1]; anim.active = true; anim.t = 0.0f; - anim.tmax = (RcVecUtils.Dist2D(anim.startPos, anim.endPos) / ag.option.maxSpeed) * 0.5f; + anim.tmax = (RcVec.Dist2D(anim.startPos, anim.endPos) / ag.option.maxSpeed) * 0.5f; ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH; - ag.corners.Clear(); - ag.neis.Clear(); + ag.ncorners = 0; + ag.nneis = 0; continue; } else @@ -1064,7 +1123,7 @@ namespace DotRecast.Detour.Crowd float speedScale = ag.GetDistanceToGoal(slowDownRadius) / slowDownRadius; ag.desiredSpeed = ag.option.maxSpeed; - dvel = dvel.Scale(ag.desiredSpeed * speedScale); + dvel = dvel * (ag.desiredSpeed * speedScale); } // Separation @@ -1077,7 +1136,7 @@ namespace DotRecast.Detour.Crowd float w = 0; RcVec3f disp = new RcVec3f(); - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; @@ -1098,20 +1157,20 @@ namespace DotRecast.Detour.Crowd float dist = MathF.Sqrt(distSqr); float weight = separationWeight * (1.0f - RcMath.Sqr(dist * invSeparationDist)); - disp = RcVecUtils.Mad(disp, diff, weight / dist); + disp = RcVec.Mad(disp, diff, weight / dist); w += 1.0f; } if (w > 0.0001f) { // Adjust desired velocity. - dvel = RcVecUtils.Mad(dvel, disp, 1.0f / w); + dvel = RcVec.Mad(dvel, disp, 1.0f / w); // Clamp desired velocity to desired speed. float speedSqr = dvel.LengthSquared(); float desiredSqr = RcMath.Sqr(ag.desiredSpeed); if (speedSqr > desiredSqr) { - dvel = dvel.Scale(desiredSqr / speedSqr); + dvel = dvel * (desiredSqr / speedSqr); } } } @@ -1139,7 +1198,7 @@ namespace DotRecast.Detour.Crowd _obstacleQuery.Reset(); // Add neighbours as obstacles. - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; if(!nei.option.contributeObstacleAvoidance || nei.option.obstacleAvoidanceWeight < ag.option.obstacleAvoidanceWeight) @@ -1230,7 +1289,7 @@ namespace DotRecast.Detour.Crowd float w = 0; - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; long idx1 = nei.idx; @@ -1264,7 +1323,7 @@ namespace DotRecast.Detour.Crowd pen = (1.0f / dist) * (pen * 0.5f) * _config.collisionResolveFactor; } - ag.disp = RcVecUtils.Mad(ag.disp, diff, pen); + ag.disp = RcVec.Mad(ag.disp, diff, pen); w += 1.0f; } @@ -1272,7 +1331,7 @@ namespace DotRecast.Detour.Crowd if (w > 0.0001f) { float iw = 1.0f / w; - ag.disp = ag.disp.Scale(iw); + ag.disp = ag.disp * iw; } } diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs index 0e2e3f8..608b830 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -28,7 +28,7 @@ namespace DotRecast.Detour.Crowd /// @ingroup crowd public class DtCrowdAgent { - public readonly long idx; + public readonly int idx; /// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState) public DtCrowdAgentState state; @@ -37,16 +37,19 @@ namespace DotRecast.Detour.Crowd public bool partial; /// The path corridor the agent is using. - public DtPathCorridor corridor; + public readonly DtPathCorridor corridor; /// The local boundary data for the agent. - public DtLocalBoundary boundary; + public readonly DtLocalBoundary boundary; /// Time since the agent's path corridor was optimized. public float topologyOptTime; /// The known neighbors of the agent. - public List neis = new List(); + public readonly DtCrowdNeighbour[] neis = new DtCrowdNeighbour[DtCrowdConst.DT_CROWDAGENT_MAX_NEIGHBOURS]; + + /// The number of neighbors. + public int nneis; /// The desired speed. public float desiredSpeed; @@ -61,7 +64,10 @@ namespace DotRecast.Detour.Crowd public DtCrowdAgentParams option; /// The local path corridor corners for the agent. - public List corners = new List(); + public DtStraightPath[] corners = new DtStraightPath[DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS]; + + /// The number of corners. + public int ncorners; public DtMoveRequestState targetState; // < State of the movement request. public long targetRef; // < Target polyref of the movement request. @@ -88,28 +94,28 @@ namespace DotRecast.Detour.Crowd RcVec3f dv = RcVec3f.Subtract(nvel, vel); float ds = dv.Length(); if (ds > maxDelta) - dv = dv.Scale(maxDelta / ds); + dv = dv * (maxDelta / ds); vel = RcVec3f.Add(vel, dv); // Integrate if (vel.Length() > 0.0001f) - npos = RcVecUtils.Mad(npos, vel, dt); + npos = RcVec.Mad(npos, vel, dt); else vel = RcVec3f.Zero; } public bool OverOffmeshConnection(float radius) { - if (0 == corners.Count) + if (0 == ncorners) return false; - bool offMeshConnection = ((corners[corners.Count - 1].flags + bool offMeshConnection = ((corners[ncorners - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) ? true : false; if (offMeshConnection) { - float distSq = RcVecUtils.Dist2DSqr(npos, corners[corners.Count - 1].pos); + float distSq = RcVec.Dist2DSqr(npos, corners[ncorners - 1].pos); if (distSq < radius * radius) return true; } @@ -119,12 +125,12 @@ namespace DotRecast.Detour.Crowd public float GetDistanceToGoal(float range) { - if (0 == corners.Count) + if (0 == ncorners) return range; - bool endOfPath = ((corners[corners.Count - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_END) != 0) ? true : false; + bool endOfPath = ((corners[ncorners - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_END) != 0) ? true : false; if (endOfPath) - return Math.Min(RcVecUtils.Dist2D(npos, corners[corners.Count - 1].pos), range); + return Math.Min(RcVec.Dist2D(npos, corners[ncorners - 1].pos), range); return range; } @@ -132,10 +138,10 @@ namespace DotRecast.Detour.Crowd public RcVec3f CalcSmoothSteerDirection() { RcVec3f dir = new RcVec3f(); - if (0 < corners.Count) + if (0 < ncorners) { int ip0 = 0; - int ip1 = Math.Min(1, corners.Count - 1); + int ip1 = Math.Min(1, ncorners - 1); var p0 = corners[ip0].pos; var p1 = corners[ip1].pos; @@ -147,7 +153,7 @@ namespace DotRecast.Detour.Crowd float len0 = dir0.Length(); float len1 = dir1.Length(); if (len1 > 0.001f) - dir1 = dir1.Scale(1.0f / len1); + dir1 = dir1 * (1.0f / len1); dir.X = dir0.X - dir1.X * len0 * 0.5f; dir.Y = 0; @@ -161,7 +167,7 @@ namespace DotRecast.Detour.Crowd public RcVec3f CalcStraightSteerDirection() { RcVec3f dir = new RcVec3f(); - if (0 < corners.Count) + if (0 < ncorners) { dir = RcVec3f.Subtract(corners[0].pos, npos); dir.Y = 0; diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentAnimation.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentAnimation.cs index 21a5252..e35712f 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentAnimation.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentAnimation.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentConfig.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentConfig.cs index d22a053..fa1db0f 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentConfig.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentConfig.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { public class DtCrowdAgentConfig { diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentDebugInfo.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentDebugInfo.cs index cf204a0..0ba23e4 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentDebugInfo.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentDebugInfo.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentParams.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentParams.cs index 2c18ca0..1aa56e6 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentParams.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentParams.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,24 +24,16 @@ namespace DotRecast.Detour.Crowd /// @ingroup crowd public class DtCrowdAgentParams { - /// < Agent radius. [Limit: >= 0] - public float radius; + public float radius; // < Agent radius. [Limit: >= 0] + public float height; // < Agent height. [Limit: > 0] + public float maxAcceleration; // < Maximum allowed acceleration. [Limit: >= 0] + public float maxSpeed; // < Maximum allowed speed. [Limit: >= 0] - /// < Agent height. [Limit: > 0] - public float height; - - /// < Maximum allowed acceleration. [Limit: >= 0] - public float maxAcceleration; - - /// < Maximum allowed speed. [Limit: >= 0] - public float maxSpeed; - /// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0] public float collisionQueryRange; - /// < The path visibility optimization range. [Limit: > 0] - public float pathOptimizationRange; - + public float pathOptimizationRange; // < The path visibility optimization range. [Limit: > 0] + /// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0] public float separationWeight; diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentState.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentState.cs index eda9251..8e407a3 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentState.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentState.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { /// The type of navigation mesh polygon the agent is currently traversing. /// @ingroup crowd diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgentUpdateFlags.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgentUpdateFlags.cs index 6a69b1b..706be0c 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgentUpdateFlags.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgentUpdateFlags.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { /// Crowd agent update flags. /// @ingroup crowd diff --git a/src/DotRecast.Detour.Crowd/DtCrowdConfig.cs b/src/DotRecast.Detour.Crowd/DtCrowdConfig.cs index 4424e24..63161aa 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdConfig.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdConfig.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtCrowdConst.cs b/src/DotRecast.Detour.Crowd/DtCrowdConst.cs index 596a4a9..3b26b04 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdConst.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdConst.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { public static class DtCrowdConst { diff --git a/src/DotRecast.Detour.Crowd/DtCrowdNeighbour.cs b/src/DotRecast.Detour.Crowd/DtCrowdNeighbour.cs index 5f19093..a790fa5 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdNeighbour.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdNeighbour.cs @@ -1,16 +1,13 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { /// Provides neighbor data for agents managed by the crowd. /// @ingroup crowd /// @see dtCrowdAgent::neis, dtCrowd public readonly struct DtCrowdNeighbour { - public readonly DtCrowdAgent agent; - - /// < The index of the neighbor in the crowd. - public readonly float dist; - - /// < The distance between the current agent and the neighbor. + public readonly DtCrowdAgent agent; // < The index of the neighbor in the crowd. + public readonly float dist; // < The distance between the current agent and the neighbor. + public DtCrowdNeighbour(DtCrowdAgent agent, float dist) { this.agent = agent; diff --git a/src/DotRecast.Detour.Crowd/DtCrowdScopedTimer.cs b/src/DotRecast.Detour.Crowd/DtCrowdScopedTimer.cs index a78e69b..967f518 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdScopedTimer.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdScopedTimer.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace DotRecast.Detour.Crowd { diff --git a/src/DotRecast.Detour.Crowd/DtCrowdTelemetry.cs b/src/DotRecast.Detour.Crowd/DtCrowdTelemetry.cs index b7b88d4..8da7269 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdTelemetry.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdTelemetry.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtCrowdTimerLabel.cs b/src/DotRecast.Detour.Crowd/DtCrowdTimerLabel.cs index f7246c1..0121f80 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdTimerLabel.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdTimerLabel.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { public class DtCrowdTimerLabel { diff --git a/src/DotRecast.Detour.Crowd/DtLocalBoundary.cs b/src/DotRecast.Detour.Crowd/DtLocalBoundary.cs index 6defc51..2b235ba 100644 --- a/src/DotRecast.Detour.Crowd/DtLocalBoundary.cs +++ b/src/DotRecast.Detour.Crowd/DtLocalBoundary.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,6 +18,7 @@ freely, subject to the following restrictions: 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.Numerics; @@ -90,6 +91,8 @@ namespace DotRecast.Detour.Crowd public void Update(long startRef, RcVec3f pos, float collisionQueryRange, DtNavMeshQuery navquery, IDtQueryFilter filter) { + const int MAX_SEGS_PER_POLY = DtDetour.DT_VERTS_PER_POLYGON * 3; + if (startRef == 0) { Reset(); @@ -104,18 +107,17 @@ namespace DotRecast.Detour.Crowd { // Secondly, store all polygon edges. m_segs.Clear(); - - var segmentVerts = new List(); - var segmentRefs = new List(); + Span segs = stackalloc RcSegmentVert[MAX_SEGS_PER_POLY]; + int nsegs = 0; for (int j = 0; j < m_polys.Count; ++j) { - var result = navquery.GetPolyWallSegments(m_polys[j], false, filter, ref segmentVerts, ref segmentRefs); + var result = navquery.GetPolyWallSegments(m_polys[j], filter, segs, null, ref nsegs, MAX_SEGS_PER_POLY); if (result.Succeeded()) { - for (int k = 0; k < segmentRefs.Count; ++k) + for (int k = 0; k < nsegs; ++k) { - RcSegmentVert s = segmentVerts[k]; + ref RcSegmentVert s = ref segs[k]; var s0 = s.vmin; var s3 = s.vmax; diff --git a/src/DotRecast.Detour.Crowd/DtMoveRequestState.cs b/src/DotRecast.Detour.Crowd/DtMoveRequestState.cs index 5bfc329..e9fee25 100644 --- a/src/DotRecast.Detour.Crowd/DtMoveRequestState.cs +++ b/src/DotRecast.Detour.Crowd/DtMoveRequestState.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { public enum DtMoveRequestState { diff --git a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceDebugData.cs b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceDebugData.cs index a4cd86b..83fb35c 100644 --- a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceDebugData.cs +++ b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceDebugData.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceParams.cs b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceParams.cs index d35ee82..81747df 100644 --- a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceParams.cs +++ b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceParams.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.Crowd +namespace DotRecast.Detour.Crowd { public class DtObstacleAvoidanceParams { @@ -8,18 +8,11 @@ public float weightSide; public float weightToi; public float horizTime; - public int gridSize; + public int gridSize; // < grid + public int adaptiveDivs; // < adaptive + public int adaptiveRings; // < adaptive + public int adaptiveDepth; // < adaptive - /// < grid - public int adaptiveDivs; - - /// < adaptive - public int adaptiveRings; - - /// < adaptive - public int adaptiveDepth; - - /// < adaptive public DtObstacleAvoidanceParams() { velBias = 0.4f; diff --git a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceQuery.cs b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceQuery.cs index c06b210..2203118 100644 --- a/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceQuery.cs +++ b/src/DotRecast.Detour.Crowd/DtObstacleAvoidanceQuery.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -19,8 +19,8 @@ freely, subject to the following restrictions: */ using System; +using System.Runtime.CompilerServices; using DotRecast.Core; -using DotRecast.Core.Buffers; using DotRecast.Core.Numerics; @@ -28,11 +28,9 @@ namespace DotRecast.Detour.Crowd { public class DtObstacleAvoidanceQuery { - public const int DT_MAX_PATTERN_DIVS = 32; - - /// < Max numver of adaptive divs. + public const int DT_MAX_PATTERN_DIVS = 32; // < Max numver of adaptive divs. public const int DT_MAX_PATTERN_RINGS = 4; - + public const float DT_PI = 3.14159265f; private DtObstacleAvoidanceParams m_params; private float m_invHorizTime; @@ -188,16 +186,16 @@ namespace DotRecast.Detour.Crowd { RcVec3f v = RcVec3f.Subtract(bq, bp); RcVec3f w = RcVec3f.Subtract(ap, bp); - float d = RcVecUtils.Perp2D(u, v); + float d = RcVec.Perp2D(u, v); if (MathF.Abs(d) < 1e-6f) return false; d = 1.0f / d; - t = RcVecUtils.Perp2D(v, w) * d; + t = RcVec.Perp2D(v, w) * d; if (t < 0 || t > 1) return false; - float s = RcVecUtils.Perp2D(u, w) * d; + float s = RcVec.Perp2D(u, w) * d; if (s < 0 || s > 1) return false; @@ -218,8 +216,8 @@ namespace DotRecast.Detour.Crowd float minPenalty, DtObstacleAvoidanceDebugData debug) { // penalty for straying away from the desired and current velocities - float vpen = m_params.weightDesVel * (RcVecUtils.Dist2D(vcand, dvel) * m_invVmax); - float vcpen = m_params.weightCurVel * (RcVecUtils.Dist2D(vcand, vel) * m_invVmax); + float vpen = m_params.weightDesVel * (RcVec.Dist2D(vcand, dvel) * m_invVmax); + float vcpen = m_params.weightCurVel * (RcVec.Dist2D(vcand, vel) * m_invVmax); // find the threshold hit time to bail out based on the early out penalty // (see how the penalty is calculated below to understand) @@ -238,7 +236,7 @@ namespace DotRecast.Detour.Crowd DtObstacleCircle cir = m_circles[i]; // RVO - RcVec3f vab = vcand.Scale(2); + RcVec3f vab = vcand * 2; vab = RcVec3f.Subtract(vab, vel); vab = RcVec3f.Subtract(vab, cir.vel); @@ -363,7 +361,8 @@ namespace DotRecast.Detour.Crowd } // vector normalization that ignores the y-component. - void DtNormalize2D(float[] v) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void DtNormalize2D(Span v) { float d = MathF.Sqrt(v[0] * v[0] + v[2] * v[2]); if (d == 0) @@ -374,7 +373,8 @@ namespace DotRecast.Detour.Crowd } // vector normalization that ignores the y-component. - RcVec3f DtRotate2D(float[] v, float ang) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + RcVec3f DtRotate2D(Span v, float ang) { RcVec3f dest = new RcVec3f(); float c = MathF.Cos(ang); @@ -385,7 +385,6 @@ namespace DotRecast.Detour.Crowd return dest; } - static readonly float DT_PI = 3.14159265f; public int SampleVelocityAdaptive(RcVec3f pos, float rad, float vmax, RcVec3f vel, RcVec3f dvel, out RcVec3f nvel, DtObstacleAvoidanceParams option, @@ -403,7 +402,7 @@ namespace DotRecast.Detour.Crowd debug.Reset(); // Build sampling pattern aligned to desired velocity. - using var pat = RcRentedArray.Rent((DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2); + Span pat = stackalloc float[(DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2]; int npat = 0; int ndivs = m_params.adaptiveDivs; @@ -417,7 +416,7 @@ namespace DotRecast.Detour.Crowd float sa = MathF.Sin(da); // desired direction - float[] ddir = new float[6]; + Span ddir = stackalloc float[6]; ddir[0] = dvel.X; ddir[1] = dvel.Y; ddir[2] = dvel.Z; diff --git a/src/DotRecast.Detour.Crowd/DtObstacleCircle.cs b/src/DotRecast.Detour.Crowd/DtObstacleCircle.cs index ba90aaf..f333a90 100644 --- a/src/DotRecast.Detour.Crowd/DtObstacleCircle.cs +++ b/src/DotRecast.Detour.Crowd/DtObstacleCircle.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour.Crowd { diff --git a/src/DotRecast.Detour.Crowd/DtObstacleSegment.cs b/src/DotRecast.Detour.Crowd/DtObstacleSegment.cs index 6eab18c..4a9c805 100644 --- a/src/DotRecast.Detour.Crowd/DtObstacleSegment.cs +++ b/src/DotRecast.Detour.Crowd/DtObstacleSegment.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour.Crowd { diff --git a/src/DotRecast.Detour.Crowd/DtPathCorridor.cs b/src/DotRecast.Detour.Crowd/DtPathCorridor.cs index 2e1d00c..37787ae 100644 --- a/src/DotRecast.Detour.Crowd/DtPathCorridor.cs +++ b/src/DotRecast.Detour.Crowd/DtPathCorridor.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -34,6 +34,7 @@ namespace DotRecast.Detour.Crowd private RcVec3f m_target; private List m_path; + private int m_npath; private int m_maxPath; /** @@ -88,7 +89,8 @@ namespace DotRecast.Detour.Crowd /// @return True if the initialization succeeded. public bool Init(int maxPath) { - m_path = new List(); + m_path = new List(maxPath); + m_npath = 0; m_maxPath = maxPath; return true; } @@ -107,6 +109,7 @@ namespace DotRecast.Detour.Crowd m_target = pos; m_path.Clear(); m_path.Add(refs); + m_npath = 1; } /** @@ -130,42 +133,42 @@ namespace DotRecast.Detour.Crowd /// @param[in] navquery The query object used to build the corridor. /// @param[in] filter The filter to apply to the operation. /// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners] - public int FindCorners(ref List corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter) + public int FindCorners(Span corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter) { const float MIN_TARGET_DIST = 0.01f; - var result = navquery.FindStraightPath(m_pos, m_target, m_path, ref corners, maxCorners, 0); - if (result.Succeeded()) + int ncorners = 0; + navquery.FindStraightPath(m_pos, m_target, m_path, m_npath, corners, out ncorners, maxCorners, 0); + + // Prune points in the beginning of the path which are too close. + while (0 < ncorners) { - // Prune points in the beginning of the path which are too close. - int start = 0; - foreach (DtStraightPath spi in corners) + if ((corners[0].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 || + RcVec.Dist2DSqr(corners[0].pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST)) { - if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 - || RcVecUtils.Dist2DSqr(spi.pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST)) - { - break; - } - - start++; + break; } - int end = corners.Count; - // Prune points after an off-mesh connection. - for (int i = start; i < corners.Count; i++) + ncorners--; + if (0 < ncorners) { - DtStraightPath spi = corners[i]; - if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) - { - end = i + 1; - break; - } + RcSpans.Move(corners, 1, 0, 3); } - - corners = corners.GetRange(start, end - start); } - return corners.Count; + + // Prune points after an off-mesh connection. + for (int i = 0; i < ncorners; ++i) + { + if ((corners[i].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) + { + ncorners = i + 1; + break; + } + } + + + return ncorners; } /** @@ -194,7 +197,7 @@ namespace DotRecast.Detour.Crowd public void OptimizePathVisibility(RcVec3f next, float pathOptimizationRange, DtNavMeshQuery navquery, IDtQueryFilter filter) { // Clamp the ray to max distance. - float dist = RcVecUtils.Dist2D(m_pos, next); + float dist = RcVec.Dist2D(m_pos, next); // If too close to the goal, do not try to optimize. if (dist < 0.01f) @@ -207,7 +210,7 @@ namespace DotRecast.Detour.Crowd // Adjust ray length. var delta = RcVec3f.Subtract(next, m_pos); - RcVec3f goal = RcVecUtils.Mad(m_pos, delta, pathOptimizationRange / dist); + RcVec3f goal = RcVec.Mad(m_pos, delta, pathOptimizationRange / dist); var res = new List(); var status = navquery.Raycast(m_path[0], m_pos, goal, filter, out var t, out var norm, ref res); @@ -215,7 +218,7 @@ namespace DotRecast.Detour.Crowd { if (res.Count > 1 && t > 0.99f) { - m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_path.Count, m_maxPath, res); + m_npath = DtPathUtils.MergeCorridorStartShortcut(ref m_path, m_npath, m_maxPath, res, res.Count); } } } @@ -235,7 +238,7 @@ namespace DotRecast.Detour.Crowd /// @param[in] filter The filter to apply to the operation. public bool OptimizePathTopology(DtNavMeshQuery navquery, IDtQueryFilter filter, int maxIterations) { - if (m_path.Count < 3) + if (m_npath < 3) { return false; } @@ -243,11 +246,11 @@ namespace DotRecast.Detour.Crowd var res = new List(); navquery.InitSlicedFindPath(m_path[0], m_path[^1], m_pos, m_target, filter, 0); navquery.UpdateSlicedFindPath(maxIterations, out var _); - var status = navquery.FinalizeSlicedFindPathPartial(m_path, ref res); + var status = navquery.FinalizeSlicedFindPathPartial(m_path, m_npath, ref res); if (status.Succeeded() && res.Count > 0) { - m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_path.Count, m_maxPath, res); + m_npath = DtPathUtils.MergeCorridorStartShortcut(ref m_path, m_npath, m_maxPath, res, res.Count); return true; } @@ -259,21 +262,23 @@ namespace DotRecast.Detour.Crowd // Advance the path up to and over the off-mesh connection. long prevRef = 0, polyRef = m_path[0]; int npos = 0; - while (npos < m_path.Count && polyRef != offMeshConRef) + while (npos < m_npath && polyRef != offMeshConRef) { prevRef = polyRef; polyRef = m_path[npos]; npos++; } - if (npos == m_path.Count) + if (npos == m_npath) { // Could not find offMeshConRef return false; } // Prune path - m_path = m_path.GetRange(npos, m_path.Count - npos); + m_path = m_path.GetRange(npos, m_npath - npos); + m_npath -= npos; + refs[0] = prevRef; refs[1] = polyRef; @@ -312,11 +317,12 @@ namespace DotRecast.Detour.Crowd public bool MovePosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) { // Move along navmesh and update new position. - var visited = new List(); - var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, ref visited); + const int MAX_VISITED = 16; + Span visited = stackalloc long[MAX_VISITED]; + var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, visited, out var nvisited, MAX_VISITED); if (status.Succeeded()) { - m_path = DtPathUtils.MergeCorridorStartMoved(m_path, m_path.Count, m_maxPath, visited); + m_npath = DtPathUtils.MergeCorridorStartMoved(ref m_path, m_npath, m_maxPath, visited, nvisited); // Adjust the position to stay on top of the navmesh. m_pos = result; @@ -354,11 +360,14 @@ namespace DotRecast.Detour.Crowd public bool MoveTargetPosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) { // Move along navmesh and update new position. - var visited = new List(); - var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, ref visited); + const int MAX_VISITED = 16; + Span visited = stackalloc long[MAX_VISITED]; + int nvisited = 0; + var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, visited, out nvisited, MAX_VISITED); if (status.Succeeded()) { - m_path = DtPathUtils.MergeCorridorEndMoved(m_path, m_path.Count, m_maxPath, visited); + m_npath = DtPathUtils.MergeCorridorEndMoved(ref m_path, m_npath, m_maxPath, visited, nvisited); + // TODO: should we do that? // Adjust the position to stay on top of the navmesh. /* @@ -386,24 +395,27 @@ namespace DotRecast.Detour.Crowd { m_target = target; m_path = new List(path); + m_npath = path.Count; } public void FixPathStart(long safeRef, RcVec3f safePos) { m_pos = safePos; - if (m_path.Count < 3 && m_path.Count > 0) + if (m_npath < 3 && m_npath > 0) { - long p = m_path[m_path.Count - 1]; + long p = m_path[m_npath - 1]; m_path.Clear(); m_path.Add(safeRef); m_path.Add(0L); m_path.Add(p); + m_npath = 3; } else { m_path.Clear(); m_path.Add(safeRef); m_path.Add(0L); + m_npath = 2; } } @@ -411,12 +423,12 @@ namespace DotRecast.Detour.Crowd { // Keep valid path as far as possible. int n = 0; - while (n < m_path.Count && navquery.IsValidPolyRef(m_path[n], filter)) + while (n < m_npath && navquery.IsValidPolyRef(m_path[n], filter)) { n++; } - if (m_path.Count == n) + if (m_npath == n) { // All valid, no need to fix. return true; @@ -424,18 +436,20 @@ namespace DotRecast.Detour.Crowd else if (n == 0) { // The first polyref is bad, use current safe values. - m_pos = RcVecUtils.Create(safePos); + m_pos = new RcVec3f(safePos); m_path.Clear(); m_path.Add(safeRef); + m_npath = 1; } - else if (n < m_path.Count) + else if (n < m_npath) { // The path is partially usable. m_path = m_path.GetRange(0, n); + m_npath = n; } // Clamp target pos to last poly - navquery.ClosestPointOnPolyBoundary(m_path[m_path.Count - 1], m_target, out m_target); + navquery.ClosestPointOnPolyBoundary(m_path[m_npath - 1], m_target, out m_target); return true; } @@ -451,7 +465,7 @@ namespace DotRecast.Detour.Crowd public bool IsValid(int maxLookAhead, DtNavMeshQuery navquery, IDtQueryFilter filter) { // Check that all polygons still pass query filter. - int n = Math.Min(m_path.Count, maxLookAhead); + int n = Math.Min(m_npath, maxLookAhead); for (int i = 0; i < n; ++i) { if (!navquery.IsValidPolyRef(m_path[i], filter)) @@ -481,14 +495,14 @@ namespace DotRecast.Detour.Crowd /// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.) public long GetFirstPoly() { - return 0 == m_path.Count ? 0 : m_path[0]; + return 0 == m_npath ? 0 : m_path[0]; } /// The polygon reference id of the last polygon in the corridor, the polygon containing the target. /// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.) public long GetLastPoly() { - return 0 == m_path.Count ? 0 : m_path[m_path.Count - 1]; + return 0 == m_npath ? 0 : m_path[m_npath - 1]; } /// The corridor's path. @@ -502,7 +516,7 @@ namespace DotRecast.Detour.Crowd /// @return The number of polygons in the current corridor path. public int GetPathCount() { - return m_path.Count; + return m_npath; } } } \ No newline at end of file diff --git a/src/DotRecast.Detour.Crowd/DtPathQuery.cs b/src/DotRecast.Detour.Crowd/DtPathQuery.cs index b8d13dd..a5b4b21 100644 --- a/src/DotRecast.Detour.Crowd/DtPathQuery.cs +++ b/src/DotRecast.Detour.Crowd/DtPathQuery.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtPathQueryResult.cs b/src/DotRecast.Detour.Crowd/DtPathQueryResult.cs index a63ae75..72200ee 100644 --- a/src/DotRecast.Detour.Crowd/DtPathQueryResult.cs +++ b/src/DotRecast.Detour.Crowd/DtPathQueryResult.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtPathQueue.cs b/src/DotRecast.Detour.Crowd/DtPathQueue.cs index fed69d9..5ebf359 100644 --- a/src/DotRecast.Detour.Crowd/DtPathQueue.cs +++ b/src/DotRecast.Detour.Crowd/DtPathQueue.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Crowd/DtProximityGrid.cs b/src/DotRecast.Detour.Crowd/DtProximityGrid.cs index 36ffd0e..3cd182c 100644 --- a/src/DotRecast.Detour.Crowd/DtProximityGrid.cs +++ b/src/DotRecast.Detour.Crowd/DtProximityGrid.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -83,7 +83,7 @@ namespace DotRecast.Detour.Crowd } } - public int QueryItems(float minx, float miny, float maxx, float maxy, DtCrowdAgent[] ids, int maxIds) + public int QueryItems(float minx, float miny, float maxx, float maxy, Span ids, int maxIds) { int iminx = (int)MathF.Floor(minx * _invCellSize); int iminy = (int)MathF.Floor(miny * _invCellSize); @@ -110,7 +110,7 @@ namespace DotRecast.Detour.Crowd // Check if the id exists already. int end = n; int i = 0; - while (i != end && ids[i] != item) + while (i != end && ids[i] != item.idx) { ++i; } @@ -118,7 +118,7 @@ namespace DotRecast.Detour.Crowd // Item not found, add it. if (i == n) { - ids[n++] = item; + ids[n++] = item.idx; if (n >= maxIds) return n; diff --git a/src/DotRecast.Detour.Crowd/DtSegment.cs b/src/DotRecast.Detour.Crowd/DtSegment.cs index a7e3a95..fada549 100644 --- a/src/DotRecast.Detour.Crowd/DtSegment.cs +++ b/src/DotRecast.Detour.Crowd/DtSegment.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour.Crowd { diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtBoxCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtBoxCollider.cs index 24ffa3d..52fe92b 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtBoxCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtBoxCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtCapsuleCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtCapsuleCollider.cs index 5b0ed35..712eb8d 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtCapsuleCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtCapsuleCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtCollider.cs index 5f964e1..311be48 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtCompositeCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtCompositeCollider.cs index eceff0f..989a7af 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtCompositeCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtCompositeCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtConvexTrimeshCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtConvexTrimeshCollider.cs index 09fce01..a8957a3 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtConvexTrimeshCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtConvexTrimeshCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtCylinderCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtCylinderCollider.cs index 36dc374..ddce6c9 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtCylinderCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtCylinderCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtSphereCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtSphereCollider.cs index a5f2be1..5b73a0a 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtSphereCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtSphereCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/DtTrimeshCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/DtTrimeshCollider.cs index 9c76a5d..ccac8f4 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/DtTrimeshCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/DtTrimeshCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Colliders/IDtCollider.cs b/src/DotRecast.Detour.Dynamic/Colliders/IDtCollider.cs index 0010bb2..880222c 100644 --- a/src/DotRecast.Detour.Dynamic/Colliders/IDtCollider.cs +++ b/src/DotRecast.Detour.Dynamic/Colliders/IDtCollider.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicNavMesh.cs b/src/DotRecast.Detour.Dynamic/DtDynamicNavMesh.cs index dc40c41..07b5870 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicNavMesh.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicNavMesh.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DotRecast.Core; +using DotRecast.Core.Collections; using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Detour.Dynamic.Io; using DotRecast.Recast; @@ -62,8 +63,8 @@ namespace DotRecast.Detour.Dynamic navMeshParams.orig.X = voxelFile.bounds[0]; navMeshParams.orig.Y = voxelFile.bounds[1]; navMeshParams.orig.Z = voxelFile.bounds[2]; - navMeshParams.tileWidth = voxelFile.cellSize * voxelFile.tileSizeX; - navMeshParams.tileHeight = voxelFile.cellSize * voxelFile.tileSizeZ; + navMeshParams.tileWidth = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeX : voxelFile.bounds[3] - voxelFile.bounds[0]; + navMeshParams.tileHeight = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeZ : voxelFile.bounds[5] - voxelFile.bounds[2]; navMeshParams.maxTiles = voxelFile.tiles.Count; navMeshParams.maxPolys = 0x8000; foreach (var t in voxelFile.tiles) @@ -167,7 +168,7 @@ namespace DotRecast.Detour.Dynamic { foreach (var tile in tiles) Rebuild(tile); - + return UpdateNavMesh(); } @@ -188,17 +189,17 @@ namespace DotRecast.Detour.Dynamic return _tiles.Values; } - int minx = (int)MathF.Floor((bounds[0] - navMeshParams.orig.X) / navMeshParams.tileWidth); - int minz = (int)MathF.Floor((bounds[2] - navMeshParams.orig.Z) / navMeshParams.tileHeight); - int maxx = (int)MathF.Floor((bounds[3] - navMeshParams.orig.X) / navMeshParams.tileWidth); - int maxz = (int)MathF.Floor((bounds[5] - navMeshParams.orig.Z) / navMeshParams.tileHeight); + int minx = (int)MathF.Floor((bounds[0] - navMeshParams.orig.X) / navMeshParams.tileWidth) - 1; + int minz = (int)MathF.Floor((bounds[2] - navMeshParams.orig.Z) / navMeshParams.tileHeight) - 1; + int maxx = (int)MathF.Floor((bounds[3] - navMeshParams.orig.X) / navMeshParams.tileWidth) + 1; + int maxz = (int)MathF.Floor((bounds[5] - navMeshParams.orig.Z) / navMeshParams.tileHeight) + 1; List tiles = new List(); for (int z = minz; z <= maxz; ++z) { for (int x = minx; x <= maxx; ++x) { DtDynamicTile tile = GetTileAt(x, z); - if (tile != null) + if (tile != null && IntersectsXZ(tile, bounds)) { tiles.Add(tile); } @@ -208,6 +209,12 @@ namespace DotRecast.Detour.Dynamic return tiles; } + private bool IntersectsXZ(DtDynamicTile tile, float[] bounds) + { + return tile.voxelTile.boundsMin.X <= bounds[3] && tile.voxelTile.boundsMax.X >= bounds[0] && + tile.voxelTile.boundsMin.Z <= bounds[5] && tile.voxelTile.boundsMax.Z >= bounds[2]; + } + private List GetTilesByCollider(long cid) { return _tiles.Values.Where(t => t.ContainsCollider(cid)).ToList(); @@ -224,12 +231,17 @@ namespace DotRecast.Detour.Dynamic { if (_dirty) { - DtNavMesh navMesh = new DtNavMesh(navMeshParams, MAX_VERTS_PER_POLY); + _dirty = false; + + DtNavMesh navMesh = new DtNavMesh(); + navMesh.Init(navMeshParams, MAX_VERTS_PER_POLY); + foreach (var t in _tiles.Values) + { t.AddTo(navMesh); + } _navMesh = navMesh; - _dirty = false; return true; } @@ -257,5 +269,21 @@ namespace DotRecast.Detour.Dynamic { return _tiles.Values.Select(t => t.recastResult).ToList(); } + + public void NavMesh(DtNavMesh mesh) + { + _tiles.Values.ForEach(t => + { + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = mesh.GetTilesAt(t.voxelTile.tileX, t.voxelTile.tileZ, tiles, MAX_NEIS); + if (0 < nneis) + { + t.SetMeshData(tiles[0].data); + } + }); + _navMesh = mesh; + _dirty = false; + } } } \ No newline at end of file diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicNavMeshConfig.cs b/src/DotRecast.Detour.Dynamic/DtDynamicNavMeshConfig.cs index 8f840f9..707ea37 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicNavMeshConfig.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicNavMeshConfig.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicTile.cs b/src/DotRecast.Detour.Dynamic/DtDynamicTile.cs index 4f31dcd..17a1575 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicTile.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicTile.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -168,12 +168,12 @@ namespace DotRecast.Detour.Dynamic option.buildBvTree = true; option.offMeshConCount = 0; - option.offMeshConVerts = new float[0]; - option.offMeshConRad = new float[0]; - option.offMeshConDir = new int[0]; - option.offMeshConAreas = new int[0]; - option.offMeshConFlags = new int[0]; - option.offMeshConUserID = new int[0]; + option.offMeshConVerts = Array.Empty(); + option.offMeshConRad = Array.Empty(); + option.offMeshConDir = Array.Empty(); + option.offMeshConAreas = Array.Empty(); + option.offMeshConFlags = Array.Empty(); + option.offMeshConUserID = Array.Empty(); return option; } @@ -181,7 +181,7 @@ namespace DotRecast.Detour.Dynamic { if (meshData != null) { - id = navMesh.AddTile(meshData, 0, 0); + navMesh.AddTile(meshData, 0, 0, out var id); } else { @@ -189,5 +189,10 @@ namespace DotRecast.Detour.Dynamic id = 0; } } + + public void SetMeshData(DtMeshData data) + { + this.meshData = data; + } } } \ No newline at end of file diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicTileCheckpoint.cs b/src/DotRecast.Detour.Dynamic/DtDynamicTileCheckpoint.cs index 24a074a..3f77a6f 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicTileCheckpoint.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicTileCheckpoint.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderAdditionJob.cs b/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderAdditionJob.cs index 9957099..e327bf2 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderAdditionJob.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderAdditionJob.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderRemovalJob.cs b/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderRemovalJob.cs index d72c586..16a7a1f 100644 --- a/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderRemovalJob.cs +++ b/src/DotRecast.Detour.Dynamic/DtDynamicTileColliderRemovalJob.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/DtVoxelQuery.cs b/src/DotRecast.Detour.Dynamic/DtVoxelQuery.cs index 48ffdcb..7ac420f 100644 --- a/src/DotRecast.Detour.Dynamic/DtVoxelQuery.cs +++ b/src/DotRecast.Detour.Dynamic/DtVoxelQuery.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/IDtDaynmicTileJob.cs b/src/DotRecast.Detour.Dynamic/IDtDaynmicTileJob.cs index f7ce42c..12d16be 100644 --- a/src/DotRecast.Detour.Dynamic/IDtDaynmicTileJob.cs +++ b/src/DotRecast.Detour.Dynamic/IDtDaynmicTileJob.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFile.cs b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFile.cs index 70b15c6..7f152e4 100644 --- a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFile.cs +++ b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFile.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -78,7 +78,7 @@ namespace DotRecast.Detour.Dynamic.Io walkbableAreaMod, buildMeshDetail); } - public static DtVoxelFile From(RcConfig config, List results) + public static DtVoxelFile From(RcConfig config, IList results) { DtVoxelFile f = new DtVoxelFile(); f.version = 1; @@ -109,13 +109,14 @@ namespace DotRecast.Detour.Dynamic.Io }; foreach (RcBuilderResult r in results) { + float pad = r.SolidHeightfiled.borderSize * r.SolidHeightfiled.cs; f.tiles.Add(new DtVoxelTile(r.TileX, r.TileZ, r.SolidHeightfiled)); - f.bounds[0] = Math.Min(f.bounds[0], r.SolidHeightfiled.bmin.X); + f.bounds[0] = Math.Min(f.bounds[0], r.SolidHeightfiled.bmin.X + pad); f.bounds[1] = Math.Min(f.bounds[1], r.SolidHeightfiled.bmin.Y); - f.bounds[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z); - f.bounds[3] = Math.Max(f.bounds[3], r.SolidHeightfiled.bmax.X); + f.bounds[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z + pad); + f.bounds[3] = Math.Max(f.bounds[3], r.SolidHeightfiled.bmax.X - pad); f.bounds[4] = Math.Max(f.bounds[4], r.SolidHeightfiled.bmax.Y); - f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z); + f.bounds[5] = Math.Max(f.bounds[5], r.SolidHeightfiled.bmax.Z - pad); } return f; @@ -155,12 +156,13 @@ namespace DotRecast.Detour.Dynamic.Io { RcHeightfield heightfield = vt.Heightfield(); f.tiles.Add(new DtVoxelTile(vt.tileX, vt.tileZ, heightfield)); - f.bounds[0] = Math.Min(f.bounds[0], vt.boundsMin.X); + float pad = vt.borderSize * vt.cellSize; + f.bounds[0] = Math.Min(f.bounds[0], vt.boundsMin.X + pad); f.bounds[1] = Math.Min(f.bounds[1], vt.boundsMin.Y); - f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z); - f.bounds[3] = Math.Max(f.bounds[3], vt.boundsMax.X); + f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z + pad); + f.bounds[3] = Math.Max(f.bounds[3], vt.boundsMax.X - pad); f.bounds[4] = Math.Max(f.bounds[4], vt.boundsMax.Y); - f.bounds[5] = Math.Max(f.bounds[5], vt.boundsMax.Z); + f.bounds[5] = Math.Max(f.bounds[5], vt.boundsMax.Z - pad); } return f; diff --git a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileReader.cs b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileReader.cs index c63f285..48af0d3 100644 --- a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileReader.cs +++ b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileReader.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -35,12 +35,12 @@ namespace DotRecast.Detour.Dynamic.Io public DtVoxelFile Read(BinaryReader stream) { - RcByteBuffer buf = IOUtils.ToByteBuffer(stream); + RcByteBuffer buf = RcIO.ToByteBuffer(stream); DtVoxelFile file = new DtVoxelFile(); int magic = buf.GetInt(); if (magic != DtVoxelFile.MAGIC) { - magic = IOUtils.SwapEndianness(magic); + magic = RcIO.SwapEndianness(magic); if (magic != DtVoxelFile.MAGIC) { throw new IOException("Invalid magic"); diff --git a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileWriter.cs b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileWriter.cs index 70ed7b2..f5a1c97 100644 --- a/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileWriter.cs +++ b/src/DotRecast.Detour.Dynamic/Io/DtVoxelFileWriter.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -19,12 +19,11 @@ freely, subject to the following restrictions: using System.IO; using DotRecast.Core; -using DotRecast.Core.Numerics; using DotRecast.Detour.Io; namespace DotRecast.Detour.Dynamic.Io { - public class DtVoxelFileWriter : DtWriter + public class DtVoxelFileWriter { private readonly IRcCompressor _compressor; @@ -40,34 +39,34 @@ namespace DotRecast.Detour.Dynamic.Io public void Write(BinaryWriter stream, DtVoxelFile f, RcByteOrder byteOrder, bool compression) { - Write(stream, DtVoxelFile.MAGIC, byteOrder); - Write(stream, DtVoxelFile.VERSION_EXPORTER_RECAST4J | (compression ? DtVoxelFile.VERSION_COMPRESSION_LZ4 : 0), byteOrder); - Write(stream, f.walkableRadius, byteOrder); - Write(stream, f.walkableHeight, byteOrder); - Write(stream, f.walkableClimb, byteOrder); - Write(stream, f.walkableSlopeAngle, byteOrder); - Write(stream, f.cellSize, byteOrder); - Write(stream, f.maxSimplificationError, byteOrder); - Write(stream, f.maxEdgeLen, byteOrder); - Write(stream, f.minRegionArea, byteOrder); - Write(stream, f.regionMergeArea, byteOrder); - Write(stream, f.vertsPerPoly, byteOrder); - Write(stream, f.buildMeshDetail); - Write(stream, f.detailSampleDistance, byteOrder); - Write(stream, f.detailSampleMaxError, byteOrder); - Write(stream, f.useTiles); - Write(stream, f.tileSizeX, byteOrder); - Write(stream, f.tileSizeZ, byteOrder); - Write(stream, f.rotation.X, byteOrder); - Write(stream, f.rotation.Y, byteOrder); - Write(stream, f.rotation.Z, byteOrder); - Write(stream, f.bounds[0], byteOrder); - Write(stream, f.bounds[1], byteOrder); - Write(stream, f.bounds[2], byteOrder); - Write(stream, f.bounds[3], byteOrder); - Write(stream, f.bounds[4], byteOrder); - Write(stream, f.bounds[5], byteOrder); - Write(stream, f.tiles.Count, byteOrder); + RcIO.Write(stream, DtVoxelFile.MAGIC, byteOrder); + RcIO.Write(stream, DtVoxelFile.VERSION_EXPORTER_RECAST4J | (compression ? DtVoxelFile.VERSION_COMPRESSION_LZ4 : 0), byteOrder); + RcIO.Write(stream, f.walkableRadius, byteOrder); + RcIO.Write(stream, f.walkableHeight, byteOrder); + RcIO.Write(stream, f.walkableClimb, byteOrder); + RcIO.Write(stream, f.walkableSlopeAngle, byteOrder); + RcIO.Write(stream, f.cellSize, byteOrder); + RcIO.Write(stream, f.maxSimplificationError, byteOrder); + RcIO.Write(stream, f.maxEdgeLen, byteOrder); + RcIO.Write(stream, f.minRegionArea, byteOrder); + RcIO.Write(stream, f.regionMergeArea, byteOrder); + RcIO.Write(stream, f.vertsPerPoly, byteOrder); + RcIO.Write(stream, f.buildMeshDetail); + RcIO.Write(stream, f.detailSampleDistance, byteOrder); + RcIO.Write(stream, f.detailSampleMaxError, byteOrder); + RcIO.Write(stream, f.useTiles); + RcIO.Write(stream, f.tileSizeX, byteOrder); + RcIO.Write(stream, f.tileSizeZ, byteOrder); + RcIO.Write(stream, f.rotation.X, byteOrder); + RcIO.Write(stream, f.rotation.Y, byteOrder); + RcIO.Write(stream, f.rotation.Z, byteOrder); + RcIO.Write(stream, f.bounds[0], byteOrder); + RcIO.Write(stream, f.bounds[1], byteOrder); + RcIO.Write(stream, f.bounds[2], byteOrder); + RcIO.Write(stream, f.bounds[3], byteOrder); + RcIO.Write(stream, f.bounds[4], byteOrder); + RcIO.Write(stream, f.bounds[5], byteOrder); + RcIO.Write(stream, f.tiles.Count, byteOrder); foreach (DtVoxelTile t in f.tiles) { WriteTile(stream, t, byteOrder, compression); @@ -76,26 +75,26 @@ namespace DotRecast.Detour.Dynamic.Io public void WriteTile(BinaryWriter stream, DtVoxelTile tile, RcByteOrder byteOrder, bool compression) { - Write(stream, tile.tileX, byteOrder); - Write(stream, tile.tileZ, byteOrder); - Write(stream, tile.width, byteOrder); - Write(stream, tile.depth, byteOrder); - Write(stream, tile.borderSize, byteOrder); - Write(stream, tile.boundsMin.X, byteOrder); - Write(stream, tile.boundsMin.Y, byteOrder); - Write(stream, tile.boundsMin.Z, byteOrder); - Write(stream, tile.boundsMax.X, byteOrder); - Write(stream, tile.boundsMax.Y, byteOrder); - Write(stream, tile.boundsMax.Z, byteOrder); - Write(stream, tile.cellSize, byteOrder); - Write(stream, tile.cellHeight, byteOrder); + RcIO.Write(stream, tile.tileX, byteOrder); + RcIO.Write(stream, tile.tileZ, byteOrder); + RcIO.Write(stream, tile.width, byteOrder); + RcIO.Write(stream, tile.depth, byteOrder); + RcIO.Write(stream, tile.borderSize, byteOrder); + RcIO.Write(stream, tile.boundsMin.X, byteOrder); + RcIO.Write(stream, tile.boundsMin.Y, byteOrder); + RcIO.Write(stream, tile.boundsMin.Z, byteOrder); + RcIO.Write(stream, tile.boundsMax.X, byteOrder); + RcIO.Write(stream, tile.boundsMax.Y, byteOrder); + RcIO.Write(stream, tile.boundsMax.Z, byteOrder); + RcIO.Write(stream, tile.cellSize, byteOrder); + RcIO.Write(stream, tile.cellHeight, byteOrder); byte[] bytes = tile.spanData; if (compression) { bytes = _compressor.Compress(bytes); } - Write(stream, bytes.Length, byteOrder); + RcIO.Write(stream, bytes.Length, byteOrder); stream.Write(bytes); } } diff --git a/src/DotRecast.Detour.Dynamic/Io/DtVoxelTile.cs b/src/DotRecast.Detour.Dynamic/Io/DtVoxelTile.cs index b50addc..2c0e17b 100644 --- a/src/DotRecast.Detour.Dynamic/Io/DtVoxelTile.cs +++ b/src/DotRecast.Detour.Dynamic/Io/DtVoxelTile.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/BVTreeBuilder.cs b/src/DotRecast.Detour.Extras/BVTreeBuilder.cs index 55aedf6..c06cfe8 100644 --- a/src/DotRecast.Detour.Extras/BVTreeBuilder.cs +++ b/src/DotRecast.Detour.Extras/BVTreeBuilder.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -40,20 +40,20 @@ namespace DotRecast.Detour.Extras BVItem it = new BVItem(); items[i] = it; it.i = i; - RcVec3f bmin = RcVecUtils.Create(data.verts, data.polys[i].verts[0] * 3); - RcVec3f bmax = RcVecUtils.Create(data.verts, data.polys[i].verts[0] * 3); + RcVec3f bmin = RcVec.Create(data.verts, data.polys[i].verts[0] * 3); + RcVec3f bmax = RcVec.Create(data.verts, data.polys[i].verts[0] * 3); for (int j = 1; j < data.polys[i].vertCount; j++) { - bmin = RcVecUtils.Min(bmin, data.verts, data.polys[i].verts[j] * 3); - bmax = RcVecUtils.Max(bmax, data.verts, data.polys[i].verts[j] * 3); + bmin = RcVec3f.Min(bmin, RcVec.Create(data.verts, data.polys[i].verts[j] * 3)); + bmax = RcVec3f.Max(bmax, RcVec.Create(data.verts, data.polys[i].verts[j] * 3)); } - it.bmin[0] = Math.Clamp((int)((bmin.X - data.header.bmin.X) * quantFactor), 0, 0x7fffffff); - it.bmin[1] = Math.Clamp((int)((bmin.Y - data.header.bmin.Y) * quantFactor), 0, 0x7fffffff); - it.bmin[2] = Math.Clamp((int)((bmin.Z - data.header.bmin.Z) * quantFactor), 0, 0x7fffffff); - it.bmax[0] = Math.Clamp((int)((bmax.X - data.header.bmin.X) * quantFactor), 0, 0x7fffffff); - it.bmax[1] = Math.Clamp((int)((bmax.Y - data.header.bmin.Y) * quantFactor), 0, 0x7fffffff); - it.bmax[2] = Math.Clamp((int)((bmax.Z - data.header.bmin.Z) * quantFactor), 0, 0x7fffffff); + it.bmin.X = Math.Clamp((int)((bmin.X - data.header.bmin.X) * quantFactor), 0, 0x7fffffff); + it.bmin.Y = Math.Clamp((int)((bmin.Y - data.header.bmin.Y) * quantFactor), 0, 0x7fffffff); + it.bmin.Z = Math.Clamp((int)((bmin.Z - data.header.bmin.Z) * quantFactor), 0, 0x7fffffff); + it.bmax.X = Math.Clamp((int)((bmax.X - data.header.bmin.X) * quantFactor), 0, 0x7fffffff); + it.bmax.Y = Math.Clamp((int)((bmax.Y - data.header.bmin.Y) * quantFactor), 0, 0x7fffffff); + it.bmax.Z = Math.Clamp((int)((bmax.Z - data.header.bmin.Z) * quantFactor), 0, 0x7fffffff); } return DtNavMeshBuilder.Subdivide(items, data.header.polyCount, 0, data.header.polyCount, 0, nodes); diff --git a/src/DotRecast.Detour.Extras/DtPolyUtils.cs b/src/DotRecast.Detour.Extras/DtPolyUtils.cs index 3671be5..08a5a84 100644 --- a/src/DotRecast.Detour.Extras/DtPolyUtils.cs +++ b/src/DotRecast.Detour.Extras/DtPolyUtils.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Jumplink/AbstractGroundSampler.cs b/src/DotRecast.Detour.Extras/Jumplink/AbstractGroundSampler.cs index 8efafd2..375fa75 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/AbstractGroundSampler.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/AbstractGroundSampler.cs @@ -11,7 +11,7 @@ namespace DotRecast.Detour.Extras.Jumplink protected void SampleGround(JumpLinkBuilderConfig acfg, EdgeSampler es, ComputeNavMeshHeight heightFunc) { float cs = acfg.cellSize; - float dist = MathF.Sqrt(RcVecUtils.Dist2DSqr(es.start.p, es.start.q)); + float dist = MathF.Sqrt(RcVec.Dist2DSqr(es.start.p, es.start.q)); int ngsamples = Math.Max(2, (int)MathF.Ceiling(dist / cs)); SampleGroundSegment(heightFunc, es.start, ngsamples); diff --git a/src/DotRecast.Detour.Extras/Jumplink/ClimbTrajectory.cs b/src/DotRecast.Detour.Extras/Jumplink/ClimbTrajectory.cs index e11ac57..978d6ef 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/ClimbTrajectory.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/ClimbTrajectory.cs @@ -1,17 +1,18 @@ using System; +using DotRecast.Core; using DotRecast.Core.Numerics; namespace DotRecast.Detour.Extras.Jumplink { - public class ClimbTrajectory : Trajectory + public class ClimbTrajectory : ITrajectory { - public override RcVec3f Apply(RcVec3f start, RcVec3f end, float u) + public RcVec3f Apply(RcVec3f start, RcVec3f end, float u) { return new RcVec3f() { - X = Lerp(start.X, end.X, Math.Min(2f * u, 1f)), - Y = Lerp(start.Y, end.Y, Math.Max(0f, 2f * u - 1f)), - Z = Lerp(start.Z, end.Z, Math.Min(2f * u, 1f)) + X = RcMath.Lerp(start.X, end.X, Math.Min(2f * u, 1f)), + Y = RcMath.Lerp(start.Y, end.Y, Math.Max(0f, 2f * u - 1f)), + Z = RcMath.Lerp(start.Z, end.Z, Math.Min(2f * u, 1f)) }; } } diff --git a/src/DotRecast.Detour.Extras/Jumplink/EdgeExtractor.cs b/src/DotRecast.Detour.Extras/Jumplink/EdgeExtractor.cs index 3b6f852..0daa7f6 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/EdgeExtractor.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/EdgeExtractor.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using DotRecast.Core.Numerics; using DotRecast.Recast; -using static DotRecast.Recast.RcConstants; - namespace DotRecast.Detour.Extras.Jumplink { + using static RcRecast; + public class EdgeExtractor { public JumpEdge[] ExtractEdges(RcPolyMesh mesh) @@ -20,7 +20,7 @@ namespace DotRecast.Detour.Extras.Jumplink { if (i > 41 || i < 41) { - // continue; + // continue; } int nvp = mesh.nvp; @@ -29,7 +29,7 @@ namespace DotRecast.Detour.Extras.Jumplink { if (j != 1) { - // continue; + // continue; } if (mesh.polys[p + j] == RC_MESH_NULL_IDX) diff --git a/src/DotRecast.Detour.Extras/Jumplink/EdgeSampler.cs b/src/DotRecast.Detour.Extras/Jumplink/EdgeSampler.cs index 9a5a468..044ab83 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/EdgeSampler.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/EdgeSampler.cs @@ -7,13 +7,13 @@ namespace DotRecast.Detour.Extras.Jumplink { public readonly GroundSegment start = new GroundSegment(); public readonly List end = new List(); - public readonly Trajectory trajectory; + public readonly ITrajectory trajectory; public readonly RcVec3f ax = new RcVec3f(); public readonly RcVec3f ay = new RcVec3f(); public readonly RcVec3f az = new RcVec3f(); - public EdgeSampler(JumpEdge edge, Trajectory trajectory) + public EdgeSampler(JumpEdge edge, ITrajectory trajectory) { this.trajectory = trajectory; ax = RcVec3f.Subtract(edge.sq, edge.sp); diff --git a/src/DotRecast.Detour.Extras/Jumplink/EdgeSamplerFactory.cs b/src/DotRecast.Detour.Extras/Jumplink/EdgeSamplerFactory.cs index 3b0b976..01c16d7 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/EdgeSamplerFactory.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/EdgeSamplerFactory.cs @@ -3,7 +3,7 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour.Extras.Jumplink { - class EdgeSamplerFactory + public class EdgeSamplerFactory { public EdgeSampler Get(JumpLinkBuilderConfig acfg, JumpLinkType type, JumpEdge edge) { diff --git a/src/DotRecast.Detour.Extras/Jumplink/GroundSample.cs b/src/DotRecast.Detour.Extras/Jumplink/GroundSample.cs index 41cffc1..3c22759 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/GroundSample.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/GroundSample.cs @@ -4,7 +4,7 @@ namespace DotRecast.Detour.Extras.Jumplink { public class GroundSample { - public RcVec3f p = new RcVec3f(); + public RcVec3f p; public bool validTrajectory; public bool validHeight; } diff --git a/src/DotRecast.Detour.Extras/Jumplink/GroundSegment.cs b/src/DotRecast.Detour.Extras/Jumplink/GroundSegment.cs index 1268bba..dbc8f1a 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/GroundSegment.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/GroundSegment.cs @@ -4,8 +4,8 @@ namespace DotRecast.Detour.Extras.Jumplink { public class GroundSegment { - public RcVec3f p = new RcVec3f(); - public RcVec3f q = new RcVec3f(); + public RcVec3f p; + public RcVec3f q; public GroundSample[] gsamples; public float height; } diff --git a/src/DotRecast.Detour.Extras/Jumplink/ITrajectory.cs b/src/DotRecast.Detour.Extras/Jumplink/ITrajectory.cs new file mode 100644 index 0000000..9b9da62 --- /dev/null +++ b/src/DotRecast.Detour.Extras/Jumplink/ITrajectory.cs @@ -0,0 +1,10 @@ +using System; +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour.Extras.Jumplink +{ + public interface ITrajectory + { + RcVec3f Apply(RcVec3f start, RcVec3f end, float u); + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour.Extras/Jumplink/JumpLink.cs b/src/DotRecast.Detour.Extras/Jumplink/JumpLink.cs index 4f02b83..9482b01 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/JumpLink.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/JumpLink.cs @@ -10,6 +10,6 @@ namespace DotRecast.Detour.Extras.Jumplink public GroundSample[] endSamples; public GroundSegment start; public GroundSegment end; - public Trajectory trajectory; + public ITrajectory trajectory; } } \ No newline at end of file diff --git a/src/DotRecast.Detour.Extras/Jumplink/JumpLinkBuilder.cs b/src/DotRecast.Detour.Extras/Jumplink/JumpLinkBuilder.cs index 8550fa6..8382908 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/JumpLinkBuilder.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/JumpLinkBuilder.cs @@ -59,7 +59,7 @@ namespace DotRecast.Detour.Extras.Jumplink GroundSegment end = es.end[js.groundSegment]; RcVec3f ep = end.gsamples[js.startSample].p; RcVec3f eq = end.gsamples[js.startSample + js.samples - 1].p; - float d = Math.Min(RcVecUtils.Dist2DSqr(sp, sq), RcVecUtils.Dist2DSqr(ep, eq)); + float d = Math.Min(RcVec.Dist2DSqr(sp, sq), RcVec.Dist2DSqr(ep, eq)); if (d >= 4 * acfg.agentRadius * acfg.agentRadius) { JumpLink link = new JumpLink(); diff --git a/src/DotRecast.Detour.Extras/Jumplink/JumpSegmentBuilder.cs b/src/DotRecast.Detour.Extras/Jumplink/JumpSegmentBuilder.cs index e4fc68d..73f5758 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/JumpSegmentBuilder.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/JumpSegmentBuilder.cs @@ -4,7 +4,7 @@ using DotRecast.Core; namespace DotRecast.Detour.Extras.Jumplink { - class JumpSegmentBuilder + public class JumpSegmentBuilder { public JumpSegment[] Build(JumpLinkBuilderConfig acfg, EdgeSampler es) { diff --git a/src/DotRecast.Detour.Extras/Jumplink/JumpTrajectory.cs b/src/DotRecast.Detour.Extras/Jumplink/JumpTrajectory.cs index 07221db..663b558 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/JumpTrajectory.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/JumpTrajectory.cs @@ -1,9 +1,10 @@ using System; +using DotRecast.Core; using DotRecast.Core.Numerics; namespace DotRecast.Detour.Extras.Jumplink { - public class JumpTrajectory : Trajectory + public class JumpTrajectory : ITrajectory { private readonly float jumpHeight; @@ -12,13 +13,13 @@ namespace DotRecast.Detour.Extras.Jumplink this.jumpHeight = jumpHeight; } - public override RcVec3f Apply(RcVec3f start, RcVec3f end, float u) + public RcVec3f Apply(RcVec3f start, RcVec3f end, float u) { return new RcVec3f { - X = Lerp(start.X, end.X, u), + X = RcMath.Lerp(start.X, end.X, u), Y = InterpolateHeight(start.Y, end.Y, u), - Z = Lerp(start.Z, end.Z, u) + Z = RcMath.Lerp(start.Z, end.Z, u) }; } diff --git a/src/DotRecast.Detour.Extras/Jumplink/NavMeshGroundSampler.cs b/src/DotRecast.Detour.Extras/Jumplink/NavMeshGroundSampler.cs index a48e819..b14de89 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/NavMeshGroundSampler.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/NavMeshGroundSampler.cs @@ -1,11 +1,10 @@ using DotRecast.Core; using DotRecast.Core.Numerics; - using DotRecast.Recast; namespace DotRecast.Detour.Extras.Jumplink { - class NavMeshGroundSampler : AbstractGroundSampler + public class NavMeshGroundSampler : AbstractGroundSampler { public override void Sample(JumpLinkBuilderConfig acfg, RcBuilderResult result, EdgeSampler es) { @@ -36,7 +35,14 @@ namespace DotRecast.Detour.Extras.Jumplink option.cs = r.Mesh.cs; option.ch = r.Mesh.ch; option.buildBvTree = true; - return new DtNavMeshQuery(new DtNavMesh(DtNavMeshBuilder.CreateNavMeshData(option), option.nvp, 0)); + var mesh = new DtNavMesh(); + var status = mesh.Init(DtNavMeshBuilder.CreateNavMeshData(option), option.nvp, 0); + if (status.Failed()) + { + return null; + } + + return new DtNavMeshQuery(mesh); } @@ -49,7 +55,7 @@ namespace DotRecast.Detour.Extras.Jumplink RcAtomicBoolean found = new RcAtomicBoolean(); RcAtomicFloat minHeight = new RcAtomicFloat(pt.Y); - navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, new PolyQueryInvoker((tile, poly, refs) => + navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, new DtCallbackPolyQuery((tile, poly, refs) => { var status = navMeshQuery.GetPolyHeight(refs, pt, out var h); if (status.Succeeded()) diff --git a/src/DotRecast.Detour.Extras/Jumplink/PolyQueryInvoker.cs b/src/DotRecast.Detour.Extras/Jumplink/PolyQueryInvoker.cs deleted file mode 100644 index 3ea5b9b..0000000 --- a/src/DotRecast.Detour.Extras/Jumplink/PolyQueryInvoker.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace DotRecast.Detour.Extras.Jumplink -{ - public class PolyQueryInvoker : IDtPolyQuery - { - public readonly Action _callback; - - public PolyQueryInvoker(Action callback) - { - _callback = callback; - } - - public void Process(DtMeshTile tile, DtPoly poly, long refs) - { - _callback?.Invoke(tile, poly, refs); - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour.Extras/Jumplink/Trajectory.cs b/src/DotRecast.Detour.Extras/Jumplink/Trajectory.cs deleted file mode 100644 index 4e3cc7d..0000000 --- a/src/DotRecast.Detour.Extras/Jumplink/Trajectory.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using DotRecast.Core.Numerics; - -namespace DotRecast.Detour.Extras.Jumplink -{ - public class Trajectory - { - public float Lerp(float f, float g, float u) - { - return u * g + (1f - u) * f; - } - - public virtual RcVec3f Apply(RcVec3f start, RcVec3f end, float u) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour.Extras/Jumplink/TrajectorySampler.cs b/src/DotRecast.Detour.Extras/Jumplink/TrajectorySampler.cs index dc2dc6f..9ddd7ee 100644 --- a/src/DotRecast.Detour.Extras/Jumplink/TrajectorySampler.cs +++ b/src/DotRecast.Detour.Extras/Jumplink/TrajectorySampler.cs @@ -5,7 +5,7 @@ using DotRecast.Recast; namespace DotRecast.Detour.Extras.Jumplink { - class TrajectorySampler + public class TrajectorySampler { public void Sample(JumpLinkBuilderConfig acfg, RcHeightfield heightfield, EdgeSampler es) { @@ -32,10 +32,10 @@ namespace DotRecast.Detour.Extras.Jumplink } } - private bool SampleTrajectory(JumpLinkBuilderConfig acfg, RcHeightfield solid, RcVec3f pa, RcVec3f pb, Trajectory tra) + private bool SampleTrajectory(JumpLinkBuilderConfig acfg, RcHeightfield solid, RcVec3f pa, RcVec3f pb, ITrajectory tra) { float cs = Math.Min(acfg.cellSize, acfg.cellHeight); - float d = RcVecUtils.Dist2D(pa, pb) + MathF.Abs(pa.Y - pb.Y); + float d = RcVec.Dist2D(pa, pb) + MathF.Abs(pa.Y - pb.Y); int nsamples = Math.Max(2, (int)MathF.Ceiling(d / cs)); for (int i = 0; i < nsamples; ++i) { diff --git a/src/DotRecast.Detour.Extras/ObjExporter.cs b/src/DotRecast.Detour.Extras/ObjExporter.cs index 526b74a..89ae7b9 100644 --- a/src/DotRecast.Detour.Extras/ObjExporter.cs +++ b/src/DotRecast.Detour.Extras/ObjExporter.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/BVTreeCreator.cs b/src/DotRecast.Detour.Extras/Unity/Astar/BVTreeCreator.cs index 5728dcc..19034ce 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/BVTreeCreator.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/BVTreeCreator.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphConnectionReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphConnectionReader.cs index 1742382..911448c 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphConnectionReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphConnectionReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphData.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphData.cs index b2262db..4ddec03 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphData.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphData.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshData.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshData.cs index f8a8217..9c5b343 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshData.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshData.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshDataReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshDataReader.cs index 7678aed..db7124d 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshDataReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeshDataReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -22,6 +23,8 @@ using DotRecast.Core; namespace DotRecast.Detour.Extras.Unity.Astar { + using static DtDetour; + public class GraphMeshDataReader : ZipBinaryReader { public const float INT_PRECISION_FACTOR = 1000f; @@ -76,7 +79,7 @@ namespace DotRecast.Detour.Extras.Unity.Astar int nodeCount = buffer.GetInt(); DtPoly[] nodes = new DtPoly[nodeCount]; DtPolyDetail[] detailNodes = new DtPolyDetail[nodeCount]; - float[] detailVerts = new float[0]; + float[] detailVerts = Array.Empty(); int[] detailTris = new int[4 * nodeCount]; int vertMask = GetVertMask(vertsCount); float ymin = float.PositiveInfinity; @@ -98,9 +101,9 @@ namespace DotRecast.Detour.Extras.Unity.Astar ymax = Math.Max(ymax, verts[nodes[i].verts[1] * 3 + 1]); ymax = Math.Max(ymax, verts[nodes[i].verts[2] * 3 + 1]); int vertBase = 0; - int vertCount = 0; + byte vertCount = 0; int triBase = i; - int triCount = 1; + byte triCount = 1; detailNodes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); detailTris[4 * i] = 0; detailTris[4 * i + 1] = 1; @@ -116,15 +119,15 @@ namespace DotRecast.Detour.Extras.Unity.Astar tiles[tileIndex].detailVerts = detailVerts; tiles[tileIndex].detailTris = detailTris; DtMeshHeader header = new DtMeshHeader(); - header.magic = DtNavMesh.DT_NAVMESH_MAGIC; - header.version = DtNavMesh.DT_NAVMESH_VERSION; + header.magic = DT_NAVMESH_MAGIC; + header.version = DT_NAVMESH_VERSION; header.x = x; header.y = z; header.polyCount = nodeCount; header.vertCount = vertsCount; header.detailMeshCount = nodeCount; header.detailTriCount = nodeCount; - header.maxLinkCount = nodeCount * 3 * 2; // XXX: Needed by Recast, not needed by recast4j + header.maxLinkCount = nodeCount * 3 * 2; // needed by Recast, not needed by recast4j, needed by DotRecast header.bmin.X = meta.forcedBoundsCenter.x - 0.5f * meta.forcedBoundsSize.x + meta.cellSize * meta.tileSizeX * x; header.bmin.Y = ymin; diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeta.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeta.cs index fe69eb2..ac79071 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeta.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMeta.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMetaReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMetaReader.cs index ce39281..3fd5a9e 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/GraphMetaReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/GraphMetaReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/LinkBuilder.cs b/src/DotRecast.Detour.Extras/Unity/Astar/LinkBuilder.cs index 29be156..280b260 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/LinkBuilder.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/LinkBuilder.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -21,6 +22,8 @@ using System.Collections.Generic; namespace DotRecast.Detour.Extras.Unity.Astar { + using static DtDetour; + public class LinkBuilder { // Process connections and transform them into recast neighbour flags @@ -65,19 +68,19 @@ namespace DotRecast.Detour.Extras.Unity.Astar { if (neighbourTile.header.bmin.X > tile.header.bmin.X) { - node.neis[DtPolyUtils.FindEdge(node, tile, neighbourTile.header.bmin.X, 0)] = DtNavMesh.DT_EXT_LINK; + node.neis[DtPolyUtils.FindEdge(node, tile, neighbourTile.header.bmin.X, 0)] = DT_EXT_LINK; } else if (neighbourTile.header.bmin.X < tile.header.bmin.X) { - node.neis[DtPolyUtils.FindEdge(node, tile, tile.header.bmin.X, 0)] = DtNavMesh.DT_EXT_LINK | 4; + node.neis[DtPolyUtils.FindEdge(node, tile, tile.header.bmin.X, 0)] = DT_EXT_LINK | 4; } else if (neighbourTile.header.bmin.Z > tile.header.bmin.Z) { - node.neis[DtPolyUtils.FindEdge(node, tile, neighbourTile.header.bmin.Z, 2)] = DtNavMesh.DT_EXT_LINK | 2; + node.neis[DtPolyUtils.FindEdge(node, tile, neighbourTile.header.bmin.Z, 2)] = DT_EXT_LINK | 2; } else { - node.neis[DtPolyUtils.FindEdge(node, tile, tile.header.bmin.Z, 2)] = DtNavMesh.DT_EXT_LINK | 6; + node.neis[DtPolyUtils.FindEdge(node, tile, tile.header.bmin.Z, 2)] = DT_EXT_LINK | 6; } } } diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/Meta.cs b/src/DotRecast.Detour.Extras/Unity/Astar/Meta.cs index baa08db..313f818 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/Meta.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/Meta.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/MetaReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/MetaReader.cs index 686e7b7..a53566c 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/MetaReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/MetaReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/NodeIndexReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/NodeIndexReader.cs index 544bf9d..f45b7ff 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/NodeIndexReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/NodeIndexReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2.cs b/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2.cs index 9f89e55..e416ee7 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2Reader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2Reader.cs index 49add07..992b64a 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2Reader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/NodeLink2Reader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/OffMeshLinkCreator.cs b/src/DotRecast.Detour.Extras/Unity/Astar/OffMeshLinkCreator.cs index e19a130..262fb12 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/OffMeshLinkCreator.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/OffMeshLinkCreator.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingImporter.cs b/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingImporter.cs index b65db19..663c566 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingImporter.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingImporter.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -65,10 +66,11 @@ namespace DotRecast.Detour.Extras.Unity.Astar option.orig.X = -0.5f * graphMeta.forcedBoundsSize.x + graphMeta.forcedBoundsCenter.x; option.orig.Y = -0.5f * graphMeta.forcedBoundsSize.y + graphMeta.forcedBoundsCenter.y; option.orig.Z = -0.5f * graphMeta.forcedBoundsSize.z + graphMeta.forcedBoundsCenter.z; - DtNavMesh mesh = new DtNavMesh(option, 3); + DtNavMesh mesh = new DtNavMesh(); + mesh.Init(option, 3); foreach (DtMeshData t in graphMeshData.tiles) { - mesh.AddTile(t, 0, 0); + mesh.AddTile(t, 0, 0, out _); } meshes[graphIndex] = mesh; diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingReader.cs index 05056ff..2e68a58 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/UnityAStarPathfindingReader.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.Extras/Unity/Astar/ZipBinaryReader.cs b/src/DotRecast.Detour.Extras/Unity/Astar/ZipBinaryReader.cs index b7a2f9f..52eb9c1 100644 --- a/src/DotRecast.Detour.Extras/Unity/Astar/ZipBinaryReader.cs +++ b/src/DotRecast.Detour.Extras/Unity/Astar/ZipBinaryReader.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -31,7 +32,7 @@ namespace DotRecast.Detour.Extras.Unity.Astar ZipArchiveEntry graphReferences = file.GetEntry(filename); using var entryStream = graphReferences.Open(); using var br = new BinaryReader(entryStream); - RcByteBuffer buffer = IOUtils.ToByteBuffer(br); + RcByteBuffer buffer = RcIO.ToByteBuffer(br); buffer.Order(RcByteOrder.LITTLE_ENDIAN); return buffer; } diff --git a/src/DotRecast.Detour.TileCache/DtCompressedTile.cs b/src/DotRecast.Detour.TileCache/DtCompressedTile.cs index 4b059d3..288389f 100644 --- a/src/DotRecast.Detour.TileCache/DtCompressedTile.cs +++ b/src/DotRecast.Detour.TileCache/DtCompressedTile.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtCompressedTileFlags.cs b/src/DotRecast.Detour.TileCache/DtCompressedTileFlags.cs index a51dc6a..e45ccd0 100644 --- a/src/DotRecast.Detour.TileCache/DtCompressedTileFlags.cs +++ b/src/DotRecast.Detour.TileCache/DtCompressedTileFlags.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour.TileCache +namespace DotRecast.Detour.TileCache { /// Flags for addTile public class DtCompressedTileFlags diff --git a/src/DotRecast.Detour.TileCache/DtLayerMonotoneRegion.cs b/src/DotRecast.Detour.TileCache/DtLayerMonotoneRegion.cs index 2add1a2..454eeb3 100644 --- a/src/DotRecast.Detour.TileCache/DtLayerMonotoneRegion.cs +++ b/src/DotRecast.Detour.TileCache/DtLayerMonotoneRegion.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Detour.TileCache { @@ -7,8 +7,9 @@ namespace DotRecast.Detour.TileCache public const int DT_LAYER_MAX_NEIS = 16; public int area; - public List neis = new List(DT_LAYER_MAX_NEIS); - public int regId; - public int areaId; + public byte[] neis = new byte[DT_LAYER_MAX_NEIS]; + public byte nneis; + public byte regId; + public byte areaId; }; } \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtObstacleBox.cs b/src/DotRecast.Detour.TileCache/DtObstacleBox.cs new file mode 100644 index 0000000..9c56917 --- /dev/null +++ b/src/DotRecast.Detour.TileCache/DtObstacleBox.cs @@ -0,0 +1,10 @@ +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour.TileCache +{ + public class DtObstacleBox + { + public RcVec3f bmin; + public RcVec3f bmax; + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtObstacleCylinder.cs b/src/DotRecast.Detour.TileCache/DtObstacleCylinder.cs new file mode 100644 index 0000000..ff1168c --- /dev/null +++ b/src/DotRecast.Detour.TileCache/DtObstacleCylinder.cs @@ -0,0 +1,11 @@ +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour.TileCache +{ + public class DtObstacleCylinder + { + public RcVec3f pos; + public float radius; + public float height; + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtObstacleOrientedBox.cs b/src/DotRecast.Detour.TileCache/DtObstacleOrientedBox.cs new file mode 100644 index 0000000..2d2d0c4 --- /dev/null +++ b/src/DotRecast.Detour.TileCache/DtObstacleOrientedBox.cs @@ -0,0 +1,11 @@ +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour.TileCache +{ + public class DtObstacleOrientedBox + { + public RcVec3f center; + public RcVec3f extents; + public readonly float[] rotAux = new float[2]; // { Cos(0.5f*angle)*Sin(-0.5f*angle); Cos(0.5f*angle)*Cos(0.5f*angle) - 0.5 } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtObstacleRequest.cs b/src/DotRecast.Detour.TileCache/DtObstacleRequest.cs index f26b289..d2ecadb 100644 --- a/src/DotRecast.Detour.TileCache/DtObstacleRequest.cs +++ b/src/DotRecast.Detour.TileCache/DtObstacleRequest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtObstacleRequestAction.cs b/src/DotRecast.Detour.TileCache/DtObstacleRequestAction.cs index 92a1aba..f7d59bd 100644 --- a/src/DotRecast.Detour.TileCache/DtObstacleRequestAction.cs +++ b/src/DotRecast.Detour.TileCache/DtObstacleRequestAction.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtObstacleState.cs b/src/DotRecast.Detour.TileCache/DtObstacleState.cs index a28296b..27fc8cd 100644 --- a/src/DotRecast.Detour.TileCache/DtObstacleState.cs +++ b/src/DotRecast.Detour.TileCache/DtObstacleState.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtTempContour.cs b/src/DotRecast.Detour.TileCache/DtTempContour.cs index 6ac055e..208c8e4 100644 --- a/src/DotRecast.Detour.TileCache/DtTempContour.cs +++ b/src/DotRecast.Detour.TileCache/DtTempContour.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Detour.TileCache { diff --git a/src/DotRecast.Detour.TileCache/DtTileCache.cs b/src/DotRecast.Detour.TileCache/DtTileCache.cs index 8d99f18..504247f 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCache.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCache.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -26,6 +26,8 @@ using DotRecast.Detour.TileCache.Io; namespace DotRecast.Detour.TileCache { + using static DtDetour; + public class DtTileCache { private int m_tileLutSize; // < Tile hash lookup size (must be pot). @@ -51,9 +53,6 @@ namespace DotRecast.Detour.TileCache private readonly List m_reqs = new List(); private readonly List m_update = new List(); - private readonly DtTileCacheBuilder builder = new DtTileCacheBuilder(); - private readonly DtTileCacheLayerHeaderReader tileReader = new DtTileCacheLayerHeaderReader(); - public DtTileCache(DtTileCacheParams option, DtTileCacheStorageParams storageParams, DtNavMesh navmesh, IRcCompressor tcomp, IDtTileCacheMeshProcess tmprocs) { m_params = option; @@ -160,7 +159,7 @@ namespace DotRecast.Detour.TileCache List tiles = new List(); // Find tile based on hash. - int h = DtNavMesh.ComputeTileHash(tx, ty, m_tileLutMask); + int h = ComputeTileHash(tx, ty, m_tileLutMask); DtCompressedTile tile = m_posLookup[h]; while (tile != null) { @@ -178,7 +177,7 @@ namespace DotRecast.Detour.TileCache DtCompressedTile GetTileAt(int tx, int ty, int tlayer) { // Find tile based on hash. - int h = DtNavMesh.ComputeTileHash(tx, ty, m_tileLutMask); + int h = ComputeTileHash(tx, ty, m_tileLutMask); DtCompressedTile tile = m_posLookup[h]; while (tile != null) { @@ -243,7 +242,7 @@ namespace DotRecast.Detour.TileCache // Make sure the data is in right format. RcByteBuffer buf = new RcByteBuffer(data); buf.Order(m_storageParams.Order); - DtTileCacheLayerHeader header = tileReader.Read(buf, m_storageParams.Compatibility); + DtTileCacheLayerHeader header = DtTileCacheLayerHeaderReader.Read(buf, m_storageParams.Compatibility); // Make sure the location is free. if (GetTileAt(header.tx, header.ty, header.tlayer) != null) { @@ -266,7 +265,7 @@ namespace DotRecast.Detour.TileCache } // Insert tile into the position lut. - int h = DtNavMesh.ComputeTileHash(header.tx, header.ty, m_tileLutMask); + int h = ComputeTileHash(header.tx, header.ty, m_tileLutMask); tile.next = m_posLookup[h]; m_posLookup[h] = tile; @@ -305,7 +304,7 @@ namespace DotRecast.Detour.TileCache } // Remove tile from hash lookup. - int h = DtNavMesh.ComputeTileHash(tile.header.tx, tile.header.ty, m_tileLutMask); + int h = ComputeTileHash(tile.header.tx, tile.header.ty, m_tileLutMask); DtCompressedTile prev = null; DtCompressedTile cur = m_posLookup[h]; while (cur != null) @@ -349,11 +348,11 @@ namespace DotRecast.Detour.TileCache public long AddObstacle(RcVec3f pos, float radius, float height) { DtTileCacheObstacle ob = AllocObstacle(); - ob.type = DtTileCacheObstacleType.CYLINDER; + ob.type = DtTileCacheObstacleType.DT_OBSTACLE_CYLINDER; - ob.pos = pos; - ob.radius = radius; - ob.height = height; + ob.cylinder.pos = pos; + ob.cylinder.radius = radius; + ob.cylinder.height = height; return AddObstacleRequest(ob).refs; } @@ -362,10 +361,10 @@ namespace DotRecast.Detour.TileCache public long AddBoxObstacle(RcVec3f bmin, RcVec3f bmax) { DtTileCacheObstacle ob = AllocObstacle(); - ob.type = DtTileCacheObstacleType.BOX; + ob.type = DtTileCacheObstacleType.DT_OBSTACLE_BOX; - ob.bmin = bmin; - ob.bmax = bmax; + ob.box.bmin = bmin; + ob.box.bmax = bmax; return AddObstacleRequest(ob).refs; } @@ -374,13 +373,13 @@ namespace DotRecast.Detour.TileCache public long AddBoxObstacle(RcVec3f center, RcVec3f extents, float yRadians) { DtTileCacheObstacle ob = AllocObstacle(); - ob.type = DtTileCacheObstacleType.ORIENTED_BOX; - ob.center = center; - ob.extents = extents; + ob.type = DtTileCacheObstacleType.DT_OBSTACLE_ORIENTED_BOX; + ob.orientedBox.center = center; + ob.orientedBox.extents = extents; float coshalf = MathF.Cos(0.5f * yRadians); float sinhalf = MathF.Sin(-0.5f * yRadians); - ob.rotAux[0] = coshalf * sinhalf; - ob.rotAux[1] = coshalf * coshalf - 0.5f; + ob.orientedBox.rotAux[0] = coshalf * sinhalf; + ob.orientedBox.rotAux[1] = coshalf * coshalf - 0.5f; return AddObstacleRequest(ob).refs; } @@ -613,26 +612,26 @@ namespace DotRecast.Detour.TileCache if (Contains(ob.touched, refs)) { - if (ob.type == DtTileCacheObstacleType.CYLINDER) + if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_CYLINDER) { - builder.MarkCylinderArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.pos, ob.radius, ob.height, 0); + DtTileCacheBuilder.MarkCylinderArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.cylinder.pos, ob.cylinder.radius, ob.cylinder.height, 0); } - else if (ob.type == DtTileCacheObstacleType.BOX) + else if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_BOX) { - builder.MarkBoxArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.bmin, ob.bmax, 0); + DtTileCacheBuilder.MarkBoxArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.box.bmin, ob.box.bmax, 0); } - else if (ob.type == DtTileCacheObstacleType.ORIENTED_BOX) + else if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_ORIENTED_BOX) { - builder.MarkBoxArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.center, ob.extents, ob.rotAux, 0); + DtTileCacheBuilder.MarkBoxArea(layer, tile.header.bmin, m_params.cs, m_params.ch, ob.orientedBox.center, ob.orientedBox.extents, ob.orientedBox.rotAux, 0); } } } // Build navmesh - builder.BuildTileCacheRegions(layer, walkableClimbVx); - DtTileCacheContourSet lcset = builder.BuildTileCacheContours(layer, walkableClimbVx, - m_params.maxSimplificationError); - DtTileCachePolyMesh polyMesh = builder.BuildTileCachePolyMesh(lcset, m_navmesh.GetMaxVertsPerPoly()); + DtTileCacheBuilder.BuildTileCacheRegions(layer, walkableClimbVx); + DtTileCacheContourSet lcset = DtTileCacheBuilder.BuildTileCacheContours(layer, walkableClimbVx, m_params.maxSimplificationError); + DtTileCachePolyMesh polyMesh = DtTileCacheBuilder.BuildTileCachePolyMesh(lcset, m_navmesh.GetMaxVertsPerPoly()); + // Early out if the mesh tile is empty. if (polyMesh.npolys == 0) { @@ -670,13 +669,13 @@ namespace DotRecast.Detour.TileCache // Add new tile, or leave the location empty. if (navData) { // Let the if (meshData != null) { - m_navmesh.AddTile(meshData, 0, 0); + m_navmesh.AddTile(meshData, 0, 0, out var result); } } public DtTileCacheLayer DecompressTile(DtCompressedTile tile) { - DtTileCacheLayer layer = builder.DecompressTileCacheLayer(m_tcomp, tile.data, m_storageParams.Order, m_storageParams.Compatibility); + DtTileCacheLayer layer = DtTileCacheBuilder.DecompressTileCacheLayer(m_tcomp, tile.data, m_storageParams.Order, m_storageParams.Compatibility); return layer; } @@ -693,29 +692,29 @@ namespace DotRecast.Detour.TileCache public void GetObstacleBounds(DtTileCacheObstacle ob, ref RcVec3f bmin, ref RcVec3f bmax) { - if (ob.type == DtTileCacheObstacleType.CYLINDER) + if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_CYLINDER) { - bmin.X = ob.pos.X - ob.radius; - bmin.Y = ob.pos.Y; - bmin.Z = ob.pos.Z - ob.radius; - bmax.X = ob.pos.X + ob.radius; - bmax.Y = ob.pos.Y + ob.height; - bmax.Z = ob.pos.Z + ob.radius; + bmin.X = ob.cylinder.pos.X - ob.cylinder.radius; + bmin.Y = ob.cylinder.pos.Y; + bmin.Z = ob.cylinder.pos.Z - ob.cylinder.radius; + bmax.X = ob.cylinder.pos.X + ob.cylinder.radius; + bmax.Y = ob.cylinder.pos.Y + ob.cylinder.height; + bmax.Z = ob.cylinder.pos.Z + ob.cylinder.radius; } - else if (ob.type == DtTileCacheObstacleType.BOX) + else if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_BOX) { - bmin = ob.bmin; - bmax = ob.bmax; + bmin = ob.box.bmin; + bmax = ob.box.bmax; } - else if (ob.type == DtTileCacheObstacleType.ORIENTED_BOX) + else if (ob.type == DtTileCacheObstacleType.DT_OBSTACLE_ORIENTED_BOX) { - float maxr = 1.41f * Math.Max(ob.extents.X, ob.extents.Z); - bmin.X = ob.center.X - maxr; - bmax.X = ob.center.X + maxr; - bmin.Y = ob.center.Y - ob.extents.Y; - bmax.Y = ob.center.Y + ob.extents.Y; - bmin.Z = ob.center.Z - maxr; - bmax.Z = ob.center.Z + maxr; + float maxr = 1.41f * Math.Max(ob.orientedBox.extents.X, ob.orientedBox.extents.Z); + bmin.X = ob.orientedBox.center.X - maxr; + bmax.X = ob.orientedBox.center.X + maxr; + bmin.Y = ob.orientedBox.center.Y - ob.orientedBox.extents.Y; + bmax.Y = ob.orientedBox.center.Y + ob.orientedBox.extents.Y; + bmin.Z = ob.orientedBox.center.Z - maxr; + bmax.Z = ob.orientedBox.center.Z + maxr; } } diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs b/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs index 6240854..1bb5cd1 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -29,22 +29,21 @@ using DotRecast.Recast; namespace DotRecast.Detour.TileCache { - public class DtTileCacheBuilder + public static class DtTileCacheBuilder { - public const int DT_TILECACHE_NULL_AREA = 0; - public const int DT_TILECACHE_WALKABLE_AREA = 63; + public const byte DT_TILECACHE_NULL_AREA = 0; + public const byte DT_TILECACHE_WALKABLE_AREA = 63; public const int DT_TILECACHE_NULL_IDX = 0xffff; + private static readonly int[] DirOffsetX = { -1, 0, 1, 0, }; private static readonly int[] DirOffsetY = { 0, 1, 0, -1 }; - private readonly DtTileCacheLayerHeaderReader reader = new DtTileCacheLayerHeaderReader(); - - public void BuildTileCacheRegions(DtTileCacheLayer layer, int walkableClimb) + public static void BuildTileCacheRegions(DtTileCacheLayer layer, int walkableClimb) { int w = layer.header.width; int h = layer.header.height; - Array.Fill(layer.regs, (short)0x00FF); + Array.Fill(layer.regs, (byte)0xFF); int nsweeps = w; RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps]; for (int i = 0; i < sweeps.Length; i++) @@ -53,14 +52,14 @@ namespace DotRecast.Detour.TileCache } // Partition walkable area into monotone regions. - int[] prevCount = new int[256]; - int regId = 0; + Span prevCount = stackalloc byte[256]; + byte regId = 0; for (int y = 0; y < h; ++y) { if (regId > 0) { - Array.Fill(prevCount, 0, 0, regId); + RcSpans.Fill(prevCount, 0, 0, regId); } // Memset(prevCount,0,Sizeof(char)*regId); @@ -93,7 +92,7 @@ namespace DotRecast.Detour.TileCache int yidx = x + (y - 1) * w; if (y > 0 && IsConnected(layer, idx, yidx, walkableClimb)) { - int nr = layer.regs[yidx]; + byte nr = layer.regs[yidx]; if (nr != 0xff) { // Set neighbour when first valid neighbour is @@ -147,12 +146,12 @@ namespace DotRecast.Detour.TileCache { int idx = x + y * w; if (layer.regs[idx] != 0xff) - layer.regs[idx] = (short)sweeps[layer.regs[idx]].id; + layer.regs[idx] = sweeps[layer.regs[idx]].id; } } // Allocate and init layer regions. - int nregs = regId; + byte nregs = regId; DtLayerMonotoneRegion[] regs = new DtLayerMonotoneRegion[nregs]; for (int i = 0; i < nregs; ++i) @@ -167,7 +166,7 @@ namespace DotRecast.Detour.TileCache for (int x = 0; x < w; ++x) { int idx = x + y * w; - int ri = layer.regs[idx]; + byte ri = layer.regs[idx]; if (ri == 0xff) continue; @@ -179,17 +178,17 @@ namespace DotRecast.Detour.TileCache int ymi = x + (y - 1) * w; if (y > 0 && IsConnected(layer, idx, ymi, walkableClimb)) { - int rai = layer.regs[ymi]; + byte rai = layer.regs[ymi]; if (rai != 0xff && rai != ri) { - AddUniqueLast(regs[ri].neis, rai); - AddUniqueLast(regs[rai].neis, ri); + AddUniqueLast(regs[ri].neis, ref regs[ri].nneis, rai); + AddUniqueLast(regs[rai].neis, ref regs[rai].nneis, ri); } } } } - for (int i = 0; i < nregs; ++i) + for (byte i = 0; i < nregs; ++i) regs[i].regId = i; for (int i = 0; i < nregs; ++i) @@ -198,8 +197,9 @@ namespace DotRecast.Detour.TileCache int merge = -1; int mergea = 0; - foreach (int nei in reg.neis) + for (int j = 0; j < reg.nneis; ++j) { + byte nei = reg.neis[j]; DtLayerMonotoneRegion regn = regs[nei]; if (reg.regId == regn.regId) continue; @@ -218,7 +218,7 @@ namespace DotRecast.Detour.TileCache if (merge != -1) { int oldId = reg.regId; - int newId = regs[merge].regId; + byte newId = regs[merge].regId; for (int j = 0; j < nregs; ++j) if (regs[j].regId == oldId) regs[j].regId = newId; @@ -226,7 +226,7 @@ namespace DotRecast.Detour.TileCache } // Compact ids. - int[] remap = new int[256]; + Span remap = stackalloc byte[256]; // Find number of unique regions. regId = 0; for (int i = 0; i < nregs; ++i) @@ -243,19 +243,20 @@ namespace DotRecast.Detour.TileCache for (int i = 0; i < w * h; ++i) { if (layer.regs[i] != 0xff) - layer.regs[i] = (short)regs[layer.regs[i]].regId; + layer.regs[i] = regs[layer.regs[i]].regId; } } - void AddUniqueLast(List a, int v) + public static void AddUniqueLast(byte[] a, ref byte an, byte v) { - int n = a.Count; + int n = an; if (n > 0 && a[n - 1] == v) return; - a.Add(v); + a[an] = v; + an++; } - bool IsConnected(DtTileCacheLayer layer, int ia, int ib, int walkableClimb) + public static bool IsConnected(DtTileCacheLayer layer, int ia, int ib, int walkableClimb) { if (layer.areas[ia] != layer.areas[ib]) return false; @@ -264,7 +265,7 @@ namespace DotRecast.Detour.TileCache return true; } - bool CanMerge(int oldRegId, int newRegId, DtLayerMonotoneRegion[] regs, int nregs) + public static bool CanMerge(int oldRegId, int newRegId, DtLayerMonotoneRegion[] regs, int nregs) { int count = 0; for (int i = 0; i < nregs; ++i) @@ -272,9 +273,11 @@ namespace DotRecast.Detour.TileCache DtLayerMonotoneRegion reg = regs[i]; if (reg.regId != oldRegId) continue; - foreach (int nei in reg.neis) + + int nnei = reg.nneis; + for (int j = 0; j < nnei ; ++j) { - if (regs[nei].regId == newRegId) + if (regs[reg.neis[j]].regId == newRegId) count++; } } @@ -282,7 +285,7 @@ namespace DotRecast.Detour.TileCache return count == 1; } - private void AppendVertex(DtTempContour cont, int x, int y, int z, int r) + public static void AppendVertex(DtTempContour cont, int x, int y, int z, int r) { // Try to merge with existing segments. if (cont.nverts > 1) @@ -316,7 +319,7 @@ namespace DotRecast.Detour.TileCache cont.nverts++; } - private int GetNeighbourReg(DtTileCacheLayer layer, int ax, int ay, int dir) + public static int GetNeighbourReg(DtTileCacheLayer layer, int ax, int ay, int dir) { int w = layer.header.width; int ia = ax + ay * w; @@ -339,17 +342,17 @@ namespace DotRecast.Detour.TileCache return layer.regs[ib]; } - private int GetDirOffsetX(int dir) + public static int GetDirOffsetX(int dir) { return DirOffsetX[dir & 0x03]; } - private int GetDirOffsetY(int dir) + public static int GetDirOffsetY(int dir) { return DirOffsetY[dir & 0x03]; } - private void WalkContour(DtTileCacheLayer layer, int x, int y, DtTempContour cont) + public static void WalkContour(DtTileCacheLayer layer, int x, int y, DtTempContour cont) { int w = layer.header.width; int h = layer.header.height; @@ -434,7 +437,7 @@ namespace DotRecast.Detour.TileCache cont.nverts--; } - private float DistancePtSeg(int x, int z, int px, int pz, int qx, int qz) + public static float DistancePtSeg(int x, int z, int px, int pz, int qx, int qz) { float pqx = qx - px; float pqz = qz - pz; @@ -455,7 +458,7 @@ namespace DotRecast.Detour.TileCache return dx * dx + dz * dz; } - private void SimplifyContour(DtTempContour cont, float maxError) + public static void SimplifyContour(DtTempContour cont, float maxError) { cont.poly.Clear(); @@ -584,7 +587,7 @@ namespace DotRecast.Detour.TileCache } } - static int GetCornerHeight(DtTileCacheLayer layer, int x, int y, int z, int walkableClimb, out bool shouldRemove) + public static int GetCornerHeight(DtTileCacheLayer layer, int x, int y, int z, int walkableClimb, out bool shouldRemove) { int w = layer.header.width; int h = layer.header.height; @@ -634,7 +637,7 @@ namespace DotRecast.Detour.TileCache } // TODO: move this somewhere else, once the layer meshing is done. - public DtTileCacheContourSet BuildTileCacheContours(DtTileCacheLayer layer, int walkableClimb, float maxError) + public static DtTileCacheContourSet BuildTileCacheContours(DtTileCacheLayer layer, int walkableClimb, float maxError) { int w = layer.header.width; int h = layer.header.height; @@ -656,7 +659,7 @@ namespace DotRecast.Detour.TileCache for (int x = 0; x < w; ++x) { int idx = x + y * w; - int ri = layer.regs[idx]; + byte ri = layer.regs[idx]; if (ri == 0xff) continue; @@ -711,7 +714,7 @@ namespace DotRecast.Detour.TileCache const uint VERTEX_BUCKET_COUNT2 = (1 << 8); - private int ComputeVertexHash2(int x, int y, int z) + public static int ComputeVertexHash2(int x, int y, int z) { uint h1 = 0x8da6b343; // Large multiplicative constants; uint h2 = 0xd8163841; // here arbitrarily chosen primes @@ -720,7 +723,7 @@ namespace DotRecast.Detour.TileCache return (int)(n & (VERTEX_BUCKET_COUNT2 - 1)); } - private int AddVertex(int x, int y, int z, int[] verts, int[] firstVert, int[] nextVert, int nv) + public static int AddVertex(int x, int y, int z, int[] verts, int[] firstVert, int[] nextVert, int nv) { int bucket = ComputeVertexHash2(x, 0, z); int i = firstVert[bucket]; @@ -743,7 +746,7 @@ namespace DotRecast.Detour.TileCache return i; } - private void BuildMeshAdjacency(int[] polys, int npolys, int[] verts, int nverts, DtTileCacheContourSet lcset, + public static void BuildMeshAdjacency(int[] polys, int npolys, int[] verts, int nverts, DtTileCacheContourSet lcset, int maxVertsPerPoly) { // Based on code by Eric Lengyel from: @@ -954,22 +957,22 @@ namespace DotRecast.Detour.TileCache } } - private bool OverlapRangeExl(int amin, int amax, int bmin, int bmax) + public static bool OverlapRangeExl(int amin, int amax, int bmin, int bmax) { return (amin >= bmax || amax <= bmin) ? false : true; } - private int Prev(int i, int n) + public static int Prev(int i, int n) { return i - 1 >= 0 ? i - 1 : n - 1; } - private int Next(int i, int n) + public static int Next(int i, int n) { return i + 1 < n ? i + 1 : 0; } - private int Area2(int[] verts, int a, int b, int c) + public static int Area2(int[] verts, int a, int b, int c) { return (verts[b] - verts[a]) * (verts[c + 2] - verts[a + 2]) - (verts[c] - verts[a]) * (verts[b + 2] - verts[a + 2]); @@ -977,17 +980,17 @@ namespace DotRecast.Detour.TileCache // Returns true iff c is strictly to the left of the directed // line through a to b. - private bool Left(int[] verts, int a, int b, int c) + public static bool Left(int[] verts, int a, int b, int c) { return Area2(verts, a, b, c) < 0; } - private bool LeftOn(int[] verts, int a, int b, int c) + public static bool LeftOn(int[] verts, int a, int b, int c) { return Area2(verts, a, b, c) <= 0; } - private bool Collinear(int[] verts, int a, int b, int c) + public static bool Collinear(int[] verts, int a, int b, int c) { return Area2(verts, a, b, c) == 0; } @@ -995,7 +998,7 @@ namespace DotRecast.Detour.TileCache // Returns true iff ab properly intersects cd: they share // a point interior to both segments. The properness of the // intersection is ensured by using strict leftness. - private bool IntersectProp(int[] verts, int a, int b, int c, int d) + public static bool IntersectProp(int[] verts, int a, int b, int c, int d) { // Eliminate improper cases. if (Collinear(verts, a, b, c) || Collinear(verts, a, b, d) || Collinear(verts, c, d, a) @@ -1007,7 +1010,7 @@ namespace DotRecast.Detour.TileCache // Returns T iff (a,b,c) are collinear and point c lies // on the closed segment ab. - private bool Between(int[] verts, int a, int b, int c) + public static bool Between(int[] verts, int a, int b, int c) { if (!Collinear(verts, a, b, c)) return false; @@ -1021,7 +1024,7 @@ namespace DotRecast.Detour.TileCache } // Returns true iff segments ab and cd intersect, properly or improperly. - private bool Intersect(int[] verts, int a, int b, int c, int d) + public static bool Intersect(int[] verts, int a, int b, int c, int d) { if (IntersectProp(verts, a, b, c, d)) return true; @@ -1032,14 +1035,14 @@ namespace DotRecast.Detour.TileCache return false; } - private bool Vequal(int[] verts, int a, int b) + public static bool Vequal(int[] verts, int a, int b) { return verts[a] == verts[b] && verts[a + 2] == verts[b + 2]; } // Returns T iff (v_i, v_j) is a proper internal *or* external // diagonal of P, *ignoring edges incident to v_i and v_j*. - private bool Diagonalie(int i, int j, int n, int[] verts, int[] indices) + public static bool Diagonalie(int i, int j, int n, int[] verts, int[] indices) { int d0 = (indices[i] & 0x7fff) * 4; int d1 = (indices[j] & 0x7fff) * 4; @@ -1067,7 +1070,7 @@ namespace DotRecast.Detour.TileCache // Returns true iff the diagonal (i,j) is strictly internal to the // polygon P in the neighborhood of the i endpoint. - private bool InCone(int i, int j, int n, int[] verts, int[] indices) + public static bool InCone(int i, int j, int n, int[] verts, int[] indices) { int pi = (indices[i] & 0x7fff) * 4; int pj = (indices[j] & 0x7fff) * 4; @@ -1084,12 +1087,12 @@ namespace DotRecast.Detour.TileCache // Returns T iff (v_i, v_j) is a proper internal // diagonal of P. - private bool Diagonal(int i, int j, int n, int[] verts, int[] indices) + public static bool Diagonal(int i, int j, int n, int[] verts, int[] indices) { return InCone(i, j, n, verts, indices) && Diagonalie(i, j, n, verts, indices); } - private int Triangulate(int n, int[] verts, int[] indices, int[] tris) + public static int Triangulate(int n, int[] verts, int[] indices, int[] tris) { int ntris = 0; int dst = 0; // tris; @@ -1174,7 +1177,7 @@ namespace DotRecast.Detour.TileCache return ntris; } - private int CountPolyVerts(int[] polys, int p, int maxVertsPerPoly) + public static int CountPolyVerts(int[] polys, int p, int maxVertsPerPoly) { for (int i = 0; i < maxVertsPerPoly; ++i) if (polys[p + i] == DT_TILECACHE_NULL_IDX) @@ -1182,13 +1185,13 @@ namespace DotRecast.Detour.TileCache return maxVertsPerPoly; } - private bool Uleft(int[] verts, int a, int b, int c) + public static bool Uleft(int[] verts, int a, int b, int c) { return (verts[b] - verts[a]) * (verts[c + 2] - verts[a + 2]) - (verts[c] - verts[a]) * (verts[b + 2] - verts[a + 2]) < 0; } - private int GetPolyMergeValue(int[] polys, int pa, int pb, int[] verts, out int ea, out int eb, int maxVertsPerPoly) + public static int GetPolyMergeValue(int[] polys, int pa, int pb, int[] verts, out int ea, out int eb, int maxVertsPerPoly) { ea = 0; eb = 0; @@ -1259,7 +1262,7 @@ namespace DotRecast.Detour.TileCache return (dx * dx) + (dy * dy); } - private void MergePolys(int[] polys, int pa, int pb, int ea, int eb, int maxVertsPerPoly) + public static void MergePolys(int[] polys, int pa, int pb, int ea, int eb, int maxVertsPerPoly) { int[] tmp = new int[maxVertsPerPoly * 2]; @@ -1278,19 +1281,19 @@ namespace DotRecast.Detour.TileCache RcArrays.Copy(tmp, 0, polys, pa, maxVertsPerPoly); } - private int PushFront(int v, List arr) + public static int PushFront(int v, List arr) { arr.Insert(0, v); return arr.Count; } - private int PushBack(int v, List arr) + public static int PushBack(int v, List arr) { arr.Add(v); return arr.Count; } - private bool CanRemoveVertex(DtTileCachePolyMesh mesh, int rem) + public static bool CanRemoveVertex(DtTileCachePolyMesh mesh, int rem) { // Count number of polygons to remove. int maxVertsPerPoly = mesh.nvp; @@ -1388,7 +1391,7 @@ namespace DotRecast.Detour.TileCache return true; } - private void RemoveVertex(DtTileCachePolyMesh mesh, int rem, int maxTris) + public static void RemoveVertex(DtTileCachePolyMesh mesh, int rem, int maxTris) { // Count number of polygons to remove. int maxVertsPerPoly = mesh.nvp; @@ -1627,7 +1630,7 @@ namespace DotRecast.Detour.TileCache } } - public DtTileCachePolyMesh BuildTileCachePolyMesh(DtTileCacheContourSet lcset, int maxVertsPerPoly) + public static DtTileCachePolyMesh BuildTileCachePolyMesh(DtTileCacheContourSet lcset, int maxVertsPerPoly) { int maxVertices = 0; int maxTris = 0; @@ -1801,7 +1804,7 @@ namespace DotRecast.Detour.TileCache return mesh; } - public void MarkCylinderArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f pos, float radius, float height, int areaId) + public static void MarkCylinderArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f pos, float radius, float height, byte areaId) { RcVec3f bmin = new RcVec3f(); RcVec3f bmax = new RcVec3f(); @@ -1857,12 +1860,12 @@ namespace DotRecast.Detour.TileCache int y = layer.heights[x + z * w]; if (y < miny || y > maxy) continue; - layer.areas[x + z * w] = (short)areaId; + layer.areas[x + z * w] = areaId; } } } - public void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f bmin, RcVec3f bmax, int areaId) + public static void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f bmin, RcVec3f bmax, byte areaId) { int w = layer.header.width; int h = layer.header.height; @@ -1901,12 +1904,12 @@ namespace DotRecast.Detour.TileCache int y = layer.heights[x + z * w]; if (y < miny || y > maxy) continue; - layer.areas[x + z * w] = (short)areaId; + layer.areas[x + z * w] = areaId; } } } - public byte[] CompressTileCacheLayer(IRcCompressor comp, DtTileCacheLayer layer, RcByteOrder order, bool cCompatibility) + public static byte[] CompressTileCacheLayer(IRcCompressor comp, DtTileCacheLayer layer, RcByteOrder order, bool cCompatibility) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); @@ -1933,7 +1936,7 @@ namespace DotRecast.Detour.TileCache } } - public byte[] CompressTileCacheLayer(DtTileCacheLayerHeader header, int[] heights, int[] areas, int[] cons, RcByteOrder order, bool cCompatibility, IRcCompressor comp) + public static byte[] CompressTileCacheLayer(DtTileCacheLayerHeader header, int[] heights, int[] areas, int[] cons, RcByteOrder order, bool cCompatibility, IRcCompressor comp) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); @@ -1960,14 +1963,14 @@ namespace DotRecast.Detour.TileCache } } - public DtTileCacheLayer DecompressTileCacheLayer(IRcCompressor comp, byte[] compressed, RcByteOrder order, bool cCompatibility) + public static DtTileCacheLayer DecompressTileCacheLayer(IRcCompressor comp, byte[] compressed, RcByteOrder order, bool cCompatibility) { RcByteBuffer buf = new RcByteBuffer(compressed); buf.Order(order); DtTileCacheLayer layer = new DtTileCacheLayer(); try { - layer.header = reader.Read(buf, cCompatibility); + layer.header = DtTileCacheLayerHeaderReader.Read(buf, cCompatibility); } catch (IOException e) { @@ -1976,22 +1979,22 @@ namespace DotRecast.Detour.TileCache int gridSize = layer.header.width * layer.header.height; byte[] grids = comp.Decompress(compressed, buf.Position(), compressed.Length - buf.Position(), gridSize * 3); - layer.heights = new short[gridSize]; - layer.areas = new short[gridSize]; - layer.cons = new short[gridSize]; - layer.regs = new short[gridSize]; + layer.heights = new byte[gridSize]; + layer.areas = new byte[gridSize]; + layer.cons = new byte[gridSize]; + layer.regs = new byte[gridSize]; for (int i = 0; i < gridSize; i++) { - layer.heights[i] = (short)(grids[i] & 0xFF); - layer.areas[i] = (short)(grids[i + gridSize] & 0xFF); - layer.cons[i] = (short)(grids[i + gridSize * 2] & 0xFF); + layer.heights[i] = (byte)(grids[i] & 0xFF); + layer.areas[i] = (byte)(grids[i + gridSize] & 0xFF); + layer.cons[i] = (byte)(grids[i + gridSize * 2] & 0xFF); } return layer; } - public void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f center, RcVec3f extents, - float[] rotAux, int areaId) + public static void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f center, RcVec3f extents, + float[] rotAux, byte areaId) { int w = layer.header.width; int h = layer.header.height; @@ -2044,7 +2047,7 @@ namespace DotRecast.Detour.TileCache int y = layer.heights[x + z * w]; if (y < miny || y > maxy) continue; - layer.areas[x + z * w] = (short)areaId; + layer.areas[x + z * w] = areaId; } } } diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheContour.cs b/src/DotRecast.Detour.TileCache/DtTileCacheContour.cs index 4ddcf27..1f297a8 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheContour.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheContour.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,7 +24,7 @@ namespace DotRecast.Detour.TileCache { public int nverts; public int[] verts; - public int reg; - public int area; + public byte reg; + public byte area; } } \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheContourSet.cs b/src/DotRecast.Detour.TileCache/DtTileCacheContourSet.cs index 270c865..34819ae 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheContourSet.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheContourSet.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayer.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayer.cs index 09ca45e..a62aa59 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheLayer.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayer.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,13 +23,10 @@ namespace DotRecast.Detour.TileCache public class DtTileCacheLayer { public DtTileCacheLayerHeader header; - public int regCount; - - /// < Region count. - public short[] heights; // char - - public short[] areas; // char - public short[] cons; // char - public short[] regs; // char + public byte regCount; // < Region count. + public byte[] heights; // unsigned char + public byte[] areas; // unsigned char + public byte[] cons; // unsigned char + public byte[] regs; // unsigned char } } \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuildResult.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuildResult.cs index 661fb2e..2ff971b 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuildResult.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuildResult.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; namespace DotRecast.Detour.TileCache diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs index 269de2a..af50b4f 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayerBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -100,7 +100,6 @@ namespace DotRecast.Detour.TileCache List result = new List(); if (lset != null) { - DtTileCacheBuilder builder = new DtTileCacheBuilder(); for (int i = 0; i < lset.layers.Length; ++i) { RcHeightfieldLayer layer = lset.layers[i]; @@ -128,7 +127,7 @@ namespace DotRecast.Detour.TileCache header.hmax = layer.hmax; var comp = _compFactory.Create(storageParams.Compatibility ? 0 : 1); - var bytes = builder.CompressTileCacheLayer(header, layer.heights, layer.areas, layer.cons, storageParams.Order, storageParams.Compatibility, comp); + var bytes = DtTileCacheBuilder.CompressTileCacheLayer(header, layer.heights, layer.areas, layer.cons, storageParams.Order, storageParams.Compatibility, comp); result.Add(bytes); } } diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs b/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs index 52a9d2c..5ba4708 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheLayerHeader.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheObstacle.cs b/src/DotRecast.Detour.TileCache/DtTileCacheObstacle.cs index 3f4d4b5..fdaaf62 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheObstacle.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheObstacle.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -19,32 +19,28 @@ freely, subject to the following restrictions: */ using System.Collections.Generic; -using DotRecast.Core.Numerics; namespace DotRecast.Detour.TileCache { public class DtTileCacheObstacle { public readonly int index; - public DtTileCacheObstacleType type; - public RcVec3f pos = new RcVec3f(); - public RcVec3f bmin = new RcVec3f(); - public RcVec3f bmax = new RcVec3f(); - public float radius, height; - public RcVec3f center = new RcVec3f(); - public RcVec3f extents = new RcVec3f(); - public readonly float[] rotAux = new float[2]; // { Cos(0.5f*angle)*Sin(-0.5f*angle); Cos(0.5f*angle)*Cos(0.5f*angle) - 0.5 } - + + public DtObstacleCylinder cylinder = new DtObstacleCylinder(); + public DtObstacleBox box = new DtObstacleBox(); + public DtObstacleOrientedBox orientedBox = new DtObstacleOrientedBox(); + public List touched = new List(); public readonly List pending = new List(); public int salt; + public DtTileCacheObstacleType type; public DtObstacleState state = DtObstacleState.DT_OBSTACLE_EMPTY; public DtTileCacheObstacle next; public DtTileCacheObstacle(int index) { - salt = 1; this.index = index; + salt = 1; } } } \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheObstacleType.cs b/src/DotRecast.Detour.TileCache/DtTileCacheObstacleType.cs index 622ef00..4d712d6 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheObstacleType.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheObstacleType.cs @@ -1,9 +1,9 @@ -namespace DotRecast.Detour.TileCache +namespace DotRecast.Detour.TileCache { public enum DtTileCacheObstacleType { - CYLINDER, - BOX, - ORIENTED_BOX + DT_OBSTACLE_CYLINDER, + DT_OBSTACLE_BOX, // AABB + DT_OBSTACLE_ORIENTED_BOX // OBB }; } \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheParams.cs b/src/DotRecast.Detour.TileCache/DtTileCacheParams.cs index 2ac24c9..390b5e9 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheParams.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheParams.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/DtTileCachePolyMesh.cs b/src/DotRecast.Detour.TileCache/DtTileCachePolyMesh.cs index b80cd73..b73e2db 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCachePolyMesh.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCachePolyMesh.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,24 +23,13 @@ namespace DotRecast.Detour.TileCache public class DtTileCachePolyMesh { public int nvp; - public int nverts; + public int nverts; // < Number of vertices. + public int npolys; // < Number of polygons. + public int[] verts; // < Vertices of the mesh, 3 elements per vertex. + public int[] polys; // < Polygons of the mesh, nvp*2 elements per polygon. + public int[] flags; // < Per polygon flags. + public int[] areas; // < Area ID of polygons. - /// < Number of vertices. - public int npolys; - - /// < Number of polygons. - public int[] verts; - - /// < Vertices of the mesh, 3 elements per vertex. - public int[] polys; - - /// < Polygons of the mesh, nvp*2 elements per polygon. - public int[] flags; - - /// < Per polygon flags. - public int[] areas; - - /// < Area ID of polygons. public DtTileCachePolyMesh(int nvp) { this.nvp = nvp; diff --git a/src/DotRecast.Detour.TileCache/DtTileCacheStorageParams.cs b/src/DotRecast.Detour.TileCache/DtTileCacheStorageParams.cs index d275540..9e25a94 100644 --- a/src/DotRecast.Detour.TileCache/DtTileCacheStorageParams.cs +++ b/src/DotRecast.Detour.TileCache/DtTileCacheStorageParams.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/IDtTileCacheMeshProcess.cs b/src/DotRecast.Detour.TileCache/IDtTileCacheMeshProcess.cs index a854692..9faab14 100644 --- a/src/DotRecast.Detour.TileCache/IDtTileCacheMeshProcess.cs +++ b/src/DotRecast.Detour.TileCache/IDtTileCacheMeshProcess.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheCompressorFactory.cs b/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheCompressorFactory.cs index 81e215f..f50c9b1 100644 --- a/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheCompressorFactory.cs +++ b/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheCompressorFactory.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using DotRecast.Core; namespace DotRecast.Detour.TileCache.Io.Compress diff --git a/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheFastLzCompressor.cs b/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheFastLzCompressor.cs index b7706ae..d0fd0d3 100644 --- a/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheFastLzCompressor.cs +++ b/src/DotRecast.Detour.TileCache/Io/Compress/DtTileCacheFastLzCompressor.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/Io/Compress/IDtTileCacheCompressorFactory.cs b/src/DotRecast.Detour.TileCache/Io/Compress/IDtTileCacheCompressorFactory.cs index c0ce433..c42ea9f 100644 --- a/src/DotRecast.Detour.TileCache/Io/Compress/IDtTileCacheCompressorFactory.cs +++ b/src/DotRecast.Detour.TileCache/Io/Compress/IDtTileCacheCompressorFactory.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderReader.cs b/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderReader.cs index 739c785..8fc9255 100644 --- a/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderReader.cs +++ b/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderReader.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,9 +23,9 @@ using DotRecast.Core; namespace DotRecast.Detour.TileCache.Io { - public class DtTileCacheLayerHeaderReader + public static class DtTileCacheLayerHeaderReader { - public DtTileCacheLayerHeader Read(RcByteBuffer data, bool cCompatibility) + public static DtTileCacheLayerHeader Read(RcByteBuffer data, bool cCompatibility) { DtTileCacheLayerHeader header = new DtTileCacheLayerHeader(); header.magic = data.GetInt(); @@ -39,7 +39,7 @@ namespace DotRecast.Detour.TileCache.Io header.tx = data.GetInt(); header.ty = data.GetInt(); header.tlayer = data.GetInt(); - + header.bmin.X = data.GetFloat(); header.bmin.Y = data.GetFloat(); header.bmin.Z = data.GetFloat(); diff --git a/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderWriter.cs b/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderWriter.cs index 444888d..312e188 100644 --- a/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderWriter.cs +++ b/src/DotRecast.Detour.TileCache/Io/DtTileCacheLayerHeaderWriter.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,34 +24,34 @@ using DotRecast.Detour.Io; namespace DotRecast.Detour.TileCache.Io { - public class DtTileCacheLayerHeaderWriter : DtWriter + public class DtTileCacheLayerHeaderWriter { public void Write(BinaryWriter stream, DtTileCacheLayerHeader header, RcByteOrder order, bool cCompatibility) { - Write(stream, header.magic, order); - Write(stream, header.version, order); - Write(stream, header.tx, order); - Write(stream, header.ty, order); - Write(stream, header.tlayer, order); - - Write(stream, header.bmin.X, order); - Write(stream, header.bmin.Y, order); - Write(stream, header.bmin.Z, order); - Write(stream, header.bmax.X, order); - Write(stream, header.bmax.Y, order); - Write(stream, header.bmax.Z, order); + RcIO.Write(stream, header.magic, order); + RcIO.Write(stream, header.version, order); + RcIO.Write(stream, header.tx, order); + RcIO.Write(stream, header.ty, order); + RcIO.Write(stream, header.tlayer, order); - Write(stream, (short)header.hmin, order); - Write(stream, (short)header.hmax, order); - Write(stream, (byte)header.width); - Write(stream, (byte)header.height); - Write(stream, (byte)header.minx); - Write(stream, (byte)header.maxx); - Write(stream, (byte)header.miny); - Write(stream, (byte)header.maxy); + RcIO.Write(stream, header.bmin.X, order); + RcIO.Write(stream, header.bmin.Y, order); + RcIO.Write(stream, header.bmin.Z, order); + RcIO.Write(stream, header.bmax.X, order); + RcIO.Write(stream, header.bmax.Y, order); + RcIO.Write(stream, header.bmax.Z, order); + + RcIO.Write(stream, (short)header.hmin, order); + RcIO.Write(stream, (short)header.hmax, order); + RcIO.Write(stream, (byte)header.width); + RcIO.Write(stream, (byte)header.height); + RcIO.Write(stream, (byte)header.minx); + RcIO.Write(stream, (byte)header.maxx); + RcIO.Write(stream, (byte)header.miny); + RcIO.Write(stream, (byte)header.maxy); if (cCompatibility) { - Write(stream, (short)0, order); // C struct padding + RcIO.Write(stream, (short)0, order); // C struct padding } } } diff --git a/src/DotRecast.Detour.TileCache/Io/DtTileCacheReader.cs b/src/DotRecast.Detour.TileCache/Io/DtTileCacheReader.cs index 80fbc07..5f953ed 100644 --- a/src/DotRecast.Detour.TileCache/Io/DtTileCacheReader.cs +++ b/src/DotRecast.Detour.TileCache/Io/DtTileCacheReader.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -37,7 +37,7 @@ namespace DotRecast.Detour.TileCache.Io public DtTileCache Read(BinaryReader @is, int maxVertPerPoly, IDtTileCacheMeshProcess meshProcessor) { - RcByteBuffer bb = IOUtils.ToByteBuffer(@is); + RcByteBuffer bb = RcIO.ToByteBuffer(@is); return Read(bb, maxVertPerPoly, meshProcessor); } @@ -47,7 +47,7 @@ namespace DotRecast.Detour.TileCache.Io header.magic = bb.GetInt(); if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC) { - header.magic = IOUtils.SwapEndianness(header.magic); + header.magic = RcIO.SwapEndianness(header.magic); if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC) { throw new IOException("Invalid magic"); @@ -69,7 +69,8 @@ namespace DotRecast.Detour.TileCache.Io header.numTiles = bb.GetInt(); header.meshParams = paramReader.Read(bb); header.cacheParams = ReadCacheParams(bb, cCompatibility); - DtNavMesh mesh = new DtNavMesh(header.meshParams, maxVertPerPoly); + DtNavMesh mesh = new DtNavMesh(); + mesh.Init(header.meshParams, maxVertPerPoly); IRcCompressor comp = _compFactory.Create(cCompatibility ? 0 : 1); DtTileCacheStorageParams storageParams = new DtTileCacheStorageParams(bb.Order(), cCompatibility); DtTileCache tc = new DtTileCache(header.cacheParams, storageParams, mesh, comp, meshProcessor); diff --git a/src/DotRecast.Detour.TileCache/Io/DtTileCacheSetHeader.cs b/src/DotRecast.Detour.TileCache/Io/DtTileCacheSetHeader.cs index 85f31b6..3e8d41f 100644 --- a/src/DotRecast.Detour.TileCache/Io/DtTileCacheSetHeader.cs +++ b/src/DotRecast.Detour.TileCache/Io/DtTileCacheSetHeader.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour.TileCache/Io/DtTileCacheWriter.cs b/src/DotRecast.Detour.TileCache/Io/DtTileCacheWriter.cs index f68a57f..f916a33 100644 --- a/src/DotRecast.Detour.TileCache/Io/DtTileCacheWriter.cs +++ b/src/DotRecast.Detour.TileCache/Io/DtTileCacheWriter.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,10 +25,9 @@ using DotRecast.Detour.TileCache.Io.Compress; namespace DotRecast.Detour.TileCache.Io { - public class DtTileCacheWriter : DtWriter + public class DtTileCacheWriter { private readonly DtNavMeshParamWriter paramWriter = new DtNavMeshParamWriter(); - private readonly DtTileCacheBuilder builder = new DtTileCacheBuilder(); private readonly IDtTileCacheCompressorFactory _compFactory; public DtTileCacheWriter(IDtTileCacheCompressorFactory compFactory) @@ -39,8 +38,8 @@ namespace DotRecast.Detour.TileCache.Io public void Write(BinaryWriter stream, DtTileCache cache, RcByteOrder order, bool cCompatibility) { - Write(stream, DtTileCacheSetHeader.TILECACHESET_MAGIC, order); - Write(stream, cCompatibility + RcIO.Write(stream, DtTileCacheSetHeader.TILECACHESET_MAGIC, order); + RcIO.Write(stream, cCompatibility ? DtTileCacheSetHeader.TILECACHESET_VERSION : DtTileCacheSetHeader.TILECACHESET_VERSION_RECAST4J, order); int numTiles = 0; @@ -52,7 +51,7 @@ namespace DotRecast.Detour.TileCache.Io numTiles++; } - Write(stream, numTiles, order); + RcIO.Write(stream, numTiles, order); paramWriter.Write(stream, cache.GetNavMesh().GetParams(), order); WriteCacheParams(stream, cache.GetParams(), order); for (int i = 0; i < cache.GetTileCount(); i++) @@ -60,32 +59,32 @@ namespace DotRecast.Detour.TileCache.Io DtCompressedTile tile = cache.GetTile(i); if (tile == null || tile.data == null) continue; - Write(stream, (int)cache.GetTileRef(tile), order); + RcIO.Write(stream, (int)cache.GetTileRef(tile), order); byte[] data = tile.data; DtTileCacheLayer layer = cache.DecompressTile(tile); var comp = _compFactory.Create(cCompatibility ? 0 : 1); - data = builder.CompressTileCacheLayer(comp, layer, order, cCompatibility); - Write(stream, data.Length, order); + data = DtTileCacheBuilder.CompressTileCacheLayer(comp, layer, order, cCompatibility); + RcIO.Write(stream, data.Length, order); stream.Write(data); } } private void WriteCacheParams(BinaryWriter stream, DtTileCacheParams option, RcByteOrder order) { - Write(stream, option.orig.X, order); - Write(stream, option.orig.Y, order); - Write(stream, option.orig.Z, order); + RcIO.Write(stream, option.orig.X, order); + RcIO.Write(stream, option.orig.Y, order); + RcIO.Write(stream, option.orig.Z, order); - Write(stream, option.cs, order); - Write(stream, option.ch, order); - Write(stream, option.width, order); - Write(stream, option.height, order); - Write(stream, option.walkableHeight, order); - Write(stream, option.walkableRadius, order); - Write(stream, option.walkableClimb, order); - Write(stream, option.maxSimplificationError, order); - Write(stream, option.maxTiles, order); - Write(stream, option.maxObstacles, order); + RcIO.Write(stream, option.cs, order); + RcIO.Write(stream, option.ch, order); + RcIO.Write(stream, option.width, order); + RcIO.Write(stream, option.height, order); + RcIO.Write(stream, option.walkableHeight, order); + RcIO.Write(stream, option.walkableRadius, order); + RcIO.Write(stream, option.walkableClimb, order); + RcIO.Write(stream, option.maxSimplificationError, order); + RcIO.Write(stream, option.maxTiles, order); + RcIO.Write(stream, option.maxObstacles, order); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/BVItem.cs b/src/DotRecast.Detour/BVItem.cs index bcc2964..d34b7a1 100644 --- a/src/DotRecast.Detour/BVItem.cs +++ b/src/DotRecast.Detour/BVItem.cs @@ -1,9 +1,11 @@ -namespace DotRecast.Detour +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour { public class BVItem { - public readonly int[] bmin = new int[3]; - public readonly int[] bmax = new int[3]; + public RcVec3i bmin; + public RcVec3i bmax; public int i; }; } \ No newline at end of file diff --git a/src/DotRecast.Detour/BVItemXComparer.cs b/src/DotRecast.Detour/BVItemXComparer.cs index 7f24ff8..3bc1565 100644 --- a/src/DotRecast.Detour/BVItemXComparer.cs +++ b/src/DotRecast.Detour/BVItemXComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Detour { @@ -12,7 +12,7 @@ namespace DotRecast.Detour public int Compare(BVItem a, BVItem b) { - return a.bmin[0].CompareTo(b.bmin[0]); + return a.bmin.X.CompareTo(b.bmin.X); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/BVItemYComparer.cs b/src/DotRecast.Detour/BVItemYComparer.cs index 40d7d8e..e475e1a 100644 --- a/src/DotRecast.Detour/BVItemYComparer.cs +++ b/src/DotRecast.Detour/BVItemYComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Detour { @@ -12,7 +12,7 @@ namespace DotRecast.Detour public int Compare(BVItem a, BVItem b) { - return a.bmin[1].CompareTo(b.bmin[1]); + return a.bmin.Y.CompareTo(b.bmin.Y); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/BVItemZComparer.cs b/src/DotRecast.Detour/BVItemZComparer.cs index 103d051..3babe65 100644 --- a/src/DotRecast.Detour/BVItemZComparer.cs +++ b/src/DotRecast.Detour/BVItemZComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Detour { @@ -12,7 +12,7 @@ namespace DotRecast.Detour public int Compare(BVItem a, BVItem b) { - return a.bmin[2].CompareTo(b.bmin[2]); + return a.bmin.Z.CompareTo(b.bmin.Z); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DetourBuilder.cs b/src/DotRecast.Detour/DetourBuilder.cs index 7d1422a..046a6d9 100644 --- a/src/DotRecast.Detour/DetourBuilder.cs +++ b/src/DotRecast.Detour/DetourBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtBVNode.cs b/src/DotRecast.Detour/DtBVNode.cs index 8170277..dc57d5e 100644 --- a/src/DotRecast.Detour/DtBVNode.cs +++ b/src/DotRecast.Detour/DtBVNode.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,26 +18,17 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using System; +using DotRecast.Core.Numerics; namespace DotRecast.Detour { - /** - * Bounding volume node. - * - * @note This structure is rarely if ever used by the end user. - * @see MeshTile - */ - [Serializable] + /// Bounding volume node. + /// @note This structure is rarely if ever used by the end user. + /// @see dtMeshTile public class DtBVNode { - /** Minimum bounds of the node's AABB. [(x, y, z)] */ - public int[] bmin = new int[3]; - - /** Maximum bounds of the node's AABB. [(x, y, z)] */ - public int[] bmax = new int[3]; - - /** The node's index. (Negative for escape sequence.) */ - public int i; + public RcVec3i bmin; //< Minimum bounds of the node's AABB. [(x, y, z)] + public RcVec3i bmax; //< Maximum bounds of the node's AABB. [(x, y, z)] + public int i; //< The node's index. (Negative for escape sequence.) } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtCallbackPolyQuery.cs b/src/DotRecast.Detour/DtCallbackPolyQuery.cs new file mode 100644 index 0000000..77f626f --- /dev/null +++ b/src/DotRecast.Detour/DtCallbackPolyQuery.cs @@ -0,0 +1,22 @@ +using System; + +namespace DotRecast.Detour +{ + public class DtCallbackPolyQuery : IDtPolyQuery + { + private readonly Action _callback; + + public DtCallbackPolyQuery(Action callback) + { + _callback = callback; + } + + public void Process(DtMeshTile tile, DtPoly[] poly, Span refs, int count) + { + for (int i = 0; i < count; ++i) + { + _callback?.Invoke(tile, poly[i], refs[i]); + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour/DtCollectPolysQuery.cs b/src/DotRecast.Detour/DtCollectPolysQuery.cs new file mode 100644 index 0000000..e98daa7 --- /dev/null +++ b/src/DotRecast.Detour/DtCollectPolysQuery.cs @@ -0,0 +1,43 @@ +using System; +using DotRecast.Core; + +namespace DotRecast.Detour +{ + public class DtCollectPolysQuery : IDtPolyQuery + { + private long[] m_polys; + private int m_maxPolys; + private int m_numCollected; + private bool m_overflow; + + public DtCollectPolysQuery(long[] polys, int maxPolys) + { + m_polys = polys; + m_maxPolys = maxPolys; + } + + public int NumCollected() + { + return m_numCollected; + } + + public bool Overflowed() + { + return m_overflow; + } + + public void Process(DtMeshTile tile, DtPoly[] poly, Span refs, int count) + { + int numLeft = m_maxPolys - m_numCollected; + int toCopy = count; + if (toCopy > numLeft) + { + m_overflow = true; + toCopy = numLeft; + } + + RcSpans.Copy(refs, 0, m_polys, m_numCollected, toCopy); + m_numCollected += toCopy; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour/DtConnectPoly.cs b/src/DotRecast.Detour/DtConnectPoly.cs index c0b42d8..2b70be7 100644 --- a/src/DotRecast.Detour/DtConnectPoly.cs +++ b/src/DotRecast.Detour/DtConnectPoly.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace DotRecast.Detour { diff --git a/src/DotRecast.Detour/DtConvexConvexInFlag.cs b/src/DotRecast.Detour/DtConvexConvexInFlag.cs index cbfe2ea..61b0ec0 100644 --- a/src/DotRecast.Detour/DtConvexConvexInFlag.cs +++ b/src/DotRecast.Detour/DtConvexConvexInFlag.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public enum DtConvexConvexInFlag { diff --git a/src/DotRecast.Detour/DtConvexConvexIntersection.cs b/src/DotRecast.Detour/DtConvexConvexIntersection.cs index a53f8ac..459a243 100644 --- a/src/DotRecast.Detour/DtConvexConvexIntersection.cs +++ b/src/DotRecast.Detour/DtConvexConvexIntersection.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public enum DtConvexConvexIntersection { diff --git a/src/DotRecast.Detour/DtConvexConvexIntersections.cs b/src/DotRecast.Detour/DtConvexConvexIntersections.cs index c718c99..4e219ee 100644 --- a/src/DotRecast.Detour/DtConvexConvexIntersections.cs +++ b/src/DotRecast.Detour/DtConvexConvexIntersections.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -18,23 +18,20 @@ freely, subject to the following restrictions: */ using System; -using DotRecast.Core; using DotRecast.Core.Numerics; namespace DotRecast.Detour { - /** - * Convex-convex intersection based on "Computational Geometry in C" by Joseph O'Rourke - */ + // Convex-convex intersection based on "Computational Geometry in C" by Joseph O'Rourke public static class DtConvexConvexIntersections { - private static readonly float EPSILON = 0.0001f; + private const float EPSILON = 0.0001f; - public static float[] Intersect(float[] p, float[] q) + public static float[] Intersect(Span p, Span q) { int n = p.Length / 3; int m = q.Length / 3; - float[] inters = new float[Math.Max(m, n) * 3 * 3]; + Span inters = stackalloc float[Math.Max(m, n) * 3 * 3]; int ii = 0; /* Initialize variables. */ RcVec3f a = new RcVec3f(); @@ -54,10 +51,10 @@ namespace DotRecast.Detour do { - a = RcVecUtils.Create(p, 3 * (ai % n)); - b = RcVecUtils.Create(q, 3 * (bi % m)); - a1 = RcVecUtils.Create(p, 3 * ((ai + n - 1) % n)); // prev a - b1 = RcVecUtils.Create(q, 3 * ((bi + m - 1) % m)); // prev b + a = RcVec.Create(p, 3 * (ai % n)); + b = RcVec.Create(q, 3 * (bi % m)); + a1 = RcVec.Create(p, 3 * ((ai + n - 1) % n)); // prev a + b1 = RcVec.Create(q, 3 * ((bi + m - 1) % m)); // prev b RcVec3f A = RcVec3f.Subtract(a, a1); RcVec3f B = RcVec3f.Subtract(b, b1); @@ -171,12 +168,11 @@ namespace DotRecast.Detour return null; } - float[] copied = new float[ii]; - RcArrays.Copy(inters, copied, ii); + float[] copied = inters.Slice(0, ii).ToArray(); return copied; } - private static int AddVertex(float[] inters, int ii, RcVec3f p) + private static int AddVertex(Span inters, int ii, RcVec3f p) { if (ii > 0) { diff --git a/src/DotRecast.Detour/DtDefaultQueryHeuristic.cs b/src/DotRecast.Detour/DtDefaultQueryHeuristic.cs index 377d2df..ffd93f9 100644 --- a/src/DotRecast.Detour/DtDefaultQueryHeuristic.cs +++ b/src/DotRecast.Detour/DtDefaultQueryHeuristic.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtDetailTriEdgeFlags.cs b/src/DotRecast.Detour/DtDetailTriEdgeFlags.cs index cfd420e..df30c7b 100644 --- a/src/DotRecast.Detour/DtDetailTriEdgeFlags.cs +++ b/src/DotRecast.Detour/DtDetailTriEdgeFlags.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public static class DtDetailTriEdgeFlags { diff --git a/src/DotRecast.Detour/DtDetour.cs b/src/DotRecast.Detour/DtDetour.cs new file mode 100644 index 0000000..cd39baf --- /dev/null +++ b/src/DotRecast.Detour/DtDetour.cs @@ -0,0 +1,184 @@ +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour +{ + public static class DtDetour + { + /// The maximum number of vertices per navigation polygon. + /// @ingroup detour + public const int DT_VERTS_PER_POLYGON = 6; + + /** A magic number used to detect compatibility of navigation tile data. */ + public const int DT_NAVMESH_MAGIC = 'D' << 24 | 'N' << 16 | 'A' << 8 | 'V'; + + /** A version number used to detect compatibility of navigation tile data. */ + public const int DT_NAVMESH_VERSION = 7; + + public const int DT_NAVMESH_VERSION_RECAST4J_FIRST = 0x8807; + public const int DT_NAVMESH_VERSION_RECAST4J_NO_POLY_FIRSTLINK = 0x8808; + public const int DT_NAVMESH_VERSION_RECAST4J_32BIT_BVTREE = 0x8809; + public const int DT_NAVMESH_VERSION_RECAST4J_LAST = 0x8809; + + /** A magic number used to detect the compatibility of navigation tile states. */ + public const int DT_NAVMESH_STATE_MAGIC = 'D' << 24 | 'N' << 16 | 'M' << 8 | 'S'; + + /** A version number used to detect compatibility of navigation tile states. */ + public const int DT_NAVMESH_STATE_VERSION = 1; + + public const int DT_SALT_BITS = 16; + public const int DT_TILE_BITS = 28; + public const int DT_POLY_BITS = 20; + + /// A flag that indicates that an entity links to an external entity. + /// (E.g. A polygon edge is a portal that links to another polygon.) + public const int DT_EXT_LINK = 0x8000; + + /// A value that indicates the entity does not link to anything. + public const int DT_NULL_LINK = unchecked((int)0xffffffff); + + public const int DT_NODE_PARENT_BITS = 24; + public const int DT_NODE_STATE_BITS = 2; + public const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS; // number of extra states per node. See dtNode::state + + /// A flag that indicates that an off-mesh connection can be traversed in + /// both directions. (Is bidirectional.) + public const int DT_OFFMESH_CON_BIDIR = 1; + + /// The maximum number of user defined area ids. + public const int DT_MAX_AREAS = 64; + + /// Limit raycasting during any angle pahfinding + /// The limit is given as a multiple of the character radius + public const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f; + + /// @{ + /// @name Encoding and Decoding + /// These functions are generally meant for internal use only. + /// Derives a standard polygon reference. + /// @note This function is generally meant for internal use only. + /// @param[in] salt The tile's salt value. + /// @param[in] it The index of the tile. + /// @param[in] ip The index of the polygon within the tile. + public static long EncodePolyId(int salt, int it, int ip) + { + return (((long)salt) << (DT_POLY_BITS + DT_TILE_BITS)) | ((long)it << DT_POLY_BITS) | (long)ip; + } + + /// Decodes a standard polygon reference. + /// @note This function is generally meant for internal use only. + /// @param[in] ref The polygon reference to decode. + /// @param[out] salt The tile's salt value. + /// @param[out] it The index of the tile. + /// @param[out] ip The index of the polygon within the tile. + /// @see #encodePolyId + public static void DecodePolyId(long refs, out int salt, out int it, out int ip) + { + long saltMask = (1L << DT_SALT_BITS) - 1; + long tileMask = (1L << DT_TILE_BITS) - 1; + long polyMask = (1L << DT_POLY_BITS) - 1; + salt = (int)((refs >> (DT_POLY_BITS + DT_TILE_BITS)) & saltMask); + it = (int)((refs >> DT_POLY_BITS) & tileMask); + ip = (int)(refs & polyMask); + } + + /// Extracts a tile's salt value from the specified polygon reference. + /// @note This function is generally meant for internal use only. + /// @param[in] ref The polygon reference. + /// @see #encodePolyId + public static int DecodePolyIdSalt(long refs) + { + long saltMask = (1L << DT_SALT_BITS) - 1; + return (int)((refs >> (DT_POLY_BITS + DT_TILE_BITS)) & saltMask); + } + + /// Extracts the tile's index from the specified polygon reference. + /// @note This function is generally meant for internal use only. + /// @param[in] ref The polygon reference. + /// @see #encodePolyId + public static int DecodePolyIdTile(long refs) + { + long tileMask = (1L << DT_TILE_BITS) - 1; + return (int)((refs >> DT_POLY_BITS) & tileMask); + } + + /// Extracts the polygon's index (within its tile) from the specified + /// polygon reference. + /// @note This function is generally meant for internal use only. + /// @param[in] ref The polygon reference. + /// @see #encodePolyId + public static int DecodePolyIdPoly(long refs) + { + long polyMask = (1L << DT_POLY_BITS) - 1; + return (int)(refs & polyMask); + } + + public static int ComputeTileHash(int x, int y, int mask) + { + uint h1 = 0x8da6b343; // Large multiplicative constants; + uint h2 = 0xd8163841; // here arbitrarily chosen primes + uint n = h1 * (uint)x + h2 * (uint)y; + return (int)(n & mask); + } + + public static float GetSlabCoord(float[] verts, int va, int side) + { + if (side == 0 || side == 4) + { + return verts[va]; + } + else if (side == 2 || side == 6) + { + return verts[va + 2]; + } + + return 0; + } + + public static void CalcSlabEndPoints(float[] verts, int va, int vb, ref RcVec2f bmin, ref RcVec2f bmax, int side) + { + if (side == 0 || side == 4) + { + if (verts[va + 2] < verts[vb + 2]) + { + bmin.X = verts[va + 2]; + bmin.Y = verts[va + 1]; + bmax.X = verts[vb + 2]; + bmax.Y = verts[vb + 1]; + } + else + { + bmin.X = verts[vb + 2]; + bmin.Y = verts[vb + 1]; + bmax.X = verts[va + 2]; + bmax.Y = verts[va + 1]; + } + } + else if (side == 2 || side == 6) + { + if (verts[va + 0] < verts[vb + 0]) + { + bmin.X = verts[va + 0]; + bmin.Y = verts[va + 1]; + bmax.X = verts[vb + 0]; + bmax.Y = verts[vb + 1]; + } + else + { + bmin.X = verts[vb + 0]; + bmin.Y = verts[vb + 1]; + bmax.X = verts[va + 0]; + bmax.Y = verts[va + 1]; + } + } + } + + /// Get flags for edge in detail triangle. + /// @param[in] triFlags The flags for the triangle (last component of detail vertices above). + /// @param[in] edgeIndex The index of the first vertex of the edge. For instance, if 0, + /// returns flags for edge AB. + public static int GetDetailTriEdgeFlags(int triFlags, int edgeIndex) + { + return (triFlags >> (edgeIndex * 2)) & 0x3; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour/DtFindNearestPolyQuery.cs b/src/DotRecast.Detour/DtFindNearestPolyQuery.cs index 3aed2aa..b0c4cf1 100644 --- a/src/DotRecast.Detour/DtFindNearestPolyQuery.cs +++ b/src/DotRecast.Detour/DtFindNearestPolyQuery.cs @@ -7,44 +7,49 @@ namespace DotRecast.Detour { private readonly DtNavMeshQuery _query; private readonly RcVec3f _center; - private long _nearestRef; - private RcVec3f _nearestPt; - private bool _overPoly; private float _nearestDistanceSqr; + private long _nearestRef; + private RcVec3f _nearestPoint; + private bool _overPoly; public DtFindNearestPolyQuery(DtNavMeshQuery query, RcVec3f center) { - this._query = query; - this._center = center; + _query = query; + _center = center; _nearestDistanceSqr = float.MaxValue; - _nearestPt = center; + _nearestPoint = center; } - public void Process(DtMeshTile tile, DtPoly poly, long refs) + public void Process(DtMeshTile tile, DtPoly[] poly, Span refs, int count) { - // Find nearest polygon amongst the nearby polygons. - _query.ClosestPointOnPoly(refs, _center, out var closestPtPoly, out var posOverPoly); + for (int i = 0; i < count; ++i) + { + long polyRef = refs[i]; + float d; + + // Find nearest polygon amongst the nearby polygons. + _query.ClosestPointOnPoly(polyRef, _center, out var closestPtPoly, out var posOverPoly); - // If a point is directly over a polygon and closer than - // climb height, favor that instead of straight line nearest point. - float d = 0; - RcVec3f diff = RcVec3f.Subtract(_center, closestPtPoly); - if (posOverPoly) - { - d = MathF.Abs(diff.Y) - tile.data.header.walkableClimb; - d = d > 0 ? d * d : 0; - } - else - { - d = diff.LengthSquared(); - } + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + RcVec3f diff = RcVec3f.Subtract(_center, closestPtPoly); + if (posOverPoly) + { + d = MathF.Abs(diff.Y) - tile.data.header.walkableClimb; + d = d > 0 ? d * d : 0; + } + else + { + d = diff.LengthSquared(); + } - if (d < _nearestDistanceSqr) - { - _nearestPt = closestPtPoly; - _nearestDistanceSqr = d; - _nearestRef = refs; - _overPoly = posOverPoly; + if (d < _nearestDistanceSqr) + { + _nearestPoint = closestPtPoly; + _nearestDistanceSqr = d; + _nearestRef = polyRef; + _overPoly = posOverPoly; + } } } @@ -55,7 +60,7 @@ namespace DotRecast.Detour public RcVec3f NearestPt() { - return _nearestPt; + return _nearestPoint; } public bool OverPoly() diff --git a/src/DotRecast.Detour/DtFindPathOption.cs b/src/DotRecast.Detour/DtFindPathOption.cs index 0d4b1c3..6739b0b 100644 --- a/src/DotRecast.Detour/DtFindPathOption.cs +++ b/src/DotRecast.Detour/DtFindPathOption.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public readonly struct DtFindPathOption { diff --git a/src/DotRecast.Detour/DtFindPathOptions.cs b/src/DotRecast.Detour/DtFindPathOptions.cs index c26d0f5..98d7e04 100644 --- a/src/DotRecast.Detour/DtFindPathOptions.cs +++ b/src/DotRecast.Detour/DtFindPathOptions.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { /// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath public static class DtFindPathOptions diff --git a/src/DotRecast.Detour/DtLink.cs b/src/DotRecast.Detour/DtLink.cs index 434bb73..6321f52 100644 --- a/src/DotRecast.Detour/DtLink.cs +++ b/src/DotRecast.Detour/DtLink.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,32 +18,20 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; + namespace DotRecast.Detour { - /** - * Defines a link between polygons. - * - * @note This structure is rarely if ever used by the end user. - * @see MeshTile - */ + /// Defines a link between polygons. + /// @note This structure is rarely if ever used by the end user. + /// @see dtMeshTile public class DtLink { - /** Neighbour reference. (The neighbor that is linked to.) */ - public long refs; - - /** Index of the next link. */ - public int next; - - /** Index of the polygon edge that owns this link. */ - public int edge; - - /** If a boundary link, defines on which side the link is. */ - public int side; - - /** If a boundary link, defines the minimum sub-edge area. */ - public int bmin; - - /** If a boundary link, defines the maximum sub-edge area. */ - public int bmax; + public long refs; //< Neighbour reference. (The neighbor that is linked to.) + public int next; //< Index of the next link. + public byte edge; //< Index of the polygon edge that owns this link. + public byte side; //< If a boundary link, defines on which side the link is. + public byte bmin; //< If a boundary link, defines the minimum sub-edge area. + public byte bmax; //< If a boundary link, defines the maximum sub-edge area. } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtMeshData.cs b/src/DotRecast.Detour/DtMeshData.cs index 73c71f4..b277730 100644 --- a/src/DotRecast.Detour/DtMeshData.cs +++ b/src/DotRecast.Detour/DtMeshData.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,33 +25,22 @@ namespace DotRecast.Detour [Serializable] public class DtMeshData { - /** The tile header. */ - public DtMeshHeader header; + public DtMeshHeader header; //< The tile header. + public DtPoly[] polys; //< The tile polygons. [Size: dtMeshHeader::polyCount] + public float[] verts; //< The tile vertices. [(x, y, z) * dtMeshHeader::vertCount] + public DtPolyDetail[] detailMeshes; //< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount] - /** The tile vertices. [Size: MeshHeader::vertCount] */ - public float[] verts; - - /** The tile polygons. [Size: MeshHeader::polyCount] */ - public DtPoly[] polys; - - /** The tile's detail sub-meshes. [Size: MeshHeader::detailMeshCount] */ - public DtPolyDetail[] detailMeshes; - - /** The detail mesh's unique vertices. [(x, y, z) * MeshHeader::detailVertCount] */ + /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount] public float[] detailVerts; - /** - * The detail mesh's triangles. [(vertA, vertB, vertC) * MeshHeader::detailTriCount] See DetailTriEdgeFlags and - * NavMesh::getDetailTriEdgeFlags. - */ + /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount]. + /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags. public int[] detailTris; - /** - * The tile bounding volume nodes. [Size: MeshHeader::bvNodeCount] (Will be null if bounding volumes are disabled.) - */ + /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount] + /// (Will be null if bounding volumes are disabled.) public DtBVNode[] bvTree; - /** The tile off-mesh connections. [Size: MeshHeader::offMeshConCount] */ - public DtOffMeshConnection[] offMeshCons; + public DtOffMeshConnection[] offMeshCons; //< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount] } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtMeshHeader.cs b/src/DotRecast.Detour/DtMeshHeader.cs index a79b9e4..60445e9 100644 --- a/src/DotRecast.Detour/DtMeshHeader.cs +++ b/src/DotRecast.Detour/DtMeshHeader.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtMeshTile.cs b/src/DotRecast.Detour/DtMeshTile.cs index 8b03657..0058154 100644 --- a/src/DotRecast.Detour/DtMeshTile.cs +++ b/src/DotRecast.Detour/DtMeshTile.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,33 +18,22 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using System.Collections.Generic; - namespace DotRecast.Detour { - /** - * Defines a navigation mesh tile. - */ + using static DtDetour; + + /// Defines a navigation mesh tile. + /// @ingroup detour public class DtMeshTile { - public readonly int index; + public readonly int index; // DtNavMesh.m_tiles array index + public int linksFreeList = DT_NULL_LINK; //< Index to the next free link. + public int salt; //< Counter describing modifications to the tile. + public DtMeshData data; // The tile data. + public DtLink[] links; // The tile links. [Size: dtMeshHeader::maxLinkCount] - /** Counter describing modifications to the tile. */ - public int salt; - - /** The tile data. */ - public DtMeshData data; - - public int[] polyLinks; - - /** The tile links. */ - public readonly List links = new List(); - - /** Index to the next free link. */ - public int linksFreeList = DtNavMesh.DT_NULL_LINK; // FIXME: Remove - - /** Tile flags. (See: #dtTileFlags) */ - public int flags; + public int flags; //< Tile flags. (See: #dtTileFlags) + public DtMeshTile next; //< The next free tile, or the next tile in the spatial grid. public DtMeshTile(int index) { diff --git a/src/DotRecast.Detour/DtNavMesh.cs b/src/DotRecast.Detour/DtNavMesh.cs index a0a162f..a479123 100644 --- a/src/DotRecast.Detour/DtNavMesh.cs +++ b/src/DotRecast.Detour/DtNavMesh.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -21,104 +21,70 @@ freely, subject to the following restrictions: using System; using System.Collections.Generic; using DotRecast.Core; -using DotRecast.Core.Buffers; using DotRecast.Core.Numerics; namespace DotRecast.Detour { + using static DtDetour; + + /// A navigation mesh based on tiles of convex polygons. + /// @ingroup detour public class DtNavMesh { - /** A magic number used to detect compatibility of navigation tile data. */ - public const int DT_NAVMESH_MAGIC = 'D' << 24 | 'N' << 16 | 'A' << 8 | 'V'; + private DtNavMeshParams m_params; //< Current initialization params. TODO: do not store this info twice. + private RcVec3f m_orig; // < Origin of the tile (0,0) + private float m_tileWidth; // < Dimensions of each tile. + private float m_tileHeight; // < Dimensions of each tile. + private int m_maxTiles; // < Max number of tiles. + private int m_tileLutSize; //< Tile hash lookup size (must be pot). + private int m_tileLutMask; // < Tile hash lookup mask. - /** A version number used to detect compatibility of navigation tile data. */ - public const int DT_NAVMESH_VERSION = 7; + private DtMeshTile[] m_posLookup; //< Tile hash lookup. + private DtMeshTile m_nextFree; //< Freelist of tiles. + private DtMeshTile[] m_tiles; //< List of tiles. - public const int DT_NAVMESH_VERSION_RECAST4J_FIRST = 0x8807; - public const int DT_NAVMESH_VERSION_RECAST4J_NO_POLY_FIRSTLINK = 0x8808; - public const int DT_NAVMESH_VERSION_RECAST4J_32BIT_BVTREE = 0x8809; - public const int DT_NAVMESH_VERSION_RECAST4J_LAST = 0x8809; - - /** A magic number used to detect the compatibility of navigation tile states. */ - public const int DT_NAVMESH_STATE_MAGIC = 'D' << 24 | 'N' << 16 | 'M' << 8 | 'S'; - - /** A version number used to detect compatibility of navigation tile states. */ - public const int DT_NAVMESH_STATE_VERSION = 1; - - public const int DT_SALT_BITS = 16; - public const int DT_TILE_BITS = 28; - public const int DT_POLY_BITS = 20; - - /// A flag that indicates that an entity links to an external entity. - /// (E.g. A polygon edge is a portal that links to another polygon.) - public const int DT_EXT_LINK = 0x8000; - - /// A value that indicates the entity does not link to anything. - public const int DT_NULL_LINK = unchecked((int)0xffffffff); - - /// A flag that indicates that an off-mesh connection can be traversed in - /// both directions. (Is bidirectional.) - public const int DT_OFFMESH_CON_BIDIR = 1; - - /// The maximum number of user defined area ids. - public const int DT_MAX_AREAS = 64; - - /// Limit raycasting during any angle pahfinding - /// The limit is given as a multiple of the character radius - public const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f; - - private readonly DtNavMeshParams m_params; - - /// < Current initialization params. TODO: do not store this info twice. - private readonly RcVec3f m_orig; - - /// < Origin of the tile (0,0) - // float m_orig[3]; ///< Origin of the tile (0,0) - private float m_tileWidth; - - private float m_tileHeight; - - /// < Dimensions of each tile. - int m_maxTiles; - - /// < Max number of tiles. - private readonly int m_tileLutMask; - - /// < Tile hash lookup mask. - private readonly Dictionary> posLookup = new Dictionary>(); - - private readonly LinkedList availableTiles = new LinkedList(); - private readonly DtMeshTile[] m_tiles; - - /// < List of tiles. /** The maximum number of vertices per navigation polygon. */ - private readonly int m_maxVertPerPoly; + private int m_maxVertPerPoly; private int m_tileCount; - public DtNavMesh(DtMeshData data, int maxVertsPerPoly, int flags) - : this(GetNavMeshParams(data), maxVertsPerPoly) + public DtStatus Init(DtNavMeshParams param, int maxVertsPerPoly) { - AddTile(data, flags, 0); - } + m_params = param; + m_orig = param.orig; + m_tileWidth = param.tileWidth; + m_tileHeight = param.tileHeight; - public DtNavMesh(DtNavMeshParams option, int maxVertsPerPoly) - { - m_params = option; - m_orig = option.orig; - m_tileWidth = option.tileWidth; - m_tileHeight = option.tileHeight; // Init tiles - m_maxTiles = option.maxTiles; m_maxVertPerPoly = maxVertsPerPoly; - m_tileLutMask = Math.Max(1, DtUtils.NextPow2(option.maxTiles)) - 1; + m_maxTiles = param.maxTiles; + m_tileLutSize = DtUtils.NextPow2(param.maxTiles); + if (0 == m_tileLutSize) + m_tileLutSize = 1; + m_tileLutMask = m_tileLutSize - 1; + m_tiles = new DtMeshTile[m_maxTiles]; - for (int i = 0; i < m_maxTiles; i++) + m_posLookup = new DtMeshTile[m_tileLutSize]; + m_nextFree = null; + for (int i = m_maxTiles - 1; i >= 0; --i) { m_tiles[i] = new DtMeshTile(i); m_tiles[i].salt = 1; - availableTiles.AddLast(m_tiles[i]); + m_tiles[i].next = m_nextFree; + m_nextFree = m_tiles[i]; } + + return DtStatus.DT_SUCCESS; + } + + public DtStatus Init(DtMeshData data, int maxVertsPerPoly, int flags) + { + var option = GetNavMeshParams(data); + DtStatus status = Init(option, maxVertsPerPoly); + if (status.Failed()) + return status; + + return AddTile(data, flags, 0, out _); } private static DtNavMeshParams GetNavMeshParams(DtMeshData data) @@ -169,76 +135,10 @@ namespace DotRecast.Detour return EncodePolyId(tile.salt, it, 0); } - /// @{ - /// @name Encoding and Decoding - /// These functions are generally meant for internal use only. - /// Derives a standard polygon reference. - /// @note This function is generally meant for internal use only. - /// @param[in] salt The tile's salt value. - /// @param[in] it The index of the tile. - /// @param[in] ip The index of the polygon within the tile. - public static long EncodePolyId(int salt, int it, int ip) - { - return (((long)salt) << (DT_POLY_BITS + DT_TILE_BITS)) | ((long)it << DT_POLY_BITS) | (long)ip; - } - - /// Decodes a standard polygon reference. - /// @note This function is generally meant for internal use only. - /// @param[in] ref The polygon reference to decode. - /// @param[out] salt The tile's salt value. - /// @param[out] it The index of the tile. - /// @param[out] ip The index of the polygon within the tile. - /// @see #encodePolyId - static void DecodePolyId(long refs, out int salt, out int it, out int ip) - { - long saltMask = (1L << DT_SALT_BITS) - 1; - long tileMask = (1L << DT_TILE_BITS) - 1; - long polyMask = (1L << DT_POLY_BITS) - 1; - salt = (int)((refs >> (DT_POLY_BITS + DT_TILE_BITS)) & saltMask); - it = (int)((refs >> DT_POLY_BITS) & tileMask); - ip = (int)(refs & polyMask); - } - - /// Extracts a tile's salt value from the specified polygon reference. - /// @note This function is generally meant for internal use only. - /// @param[in] ref The polygon reference. - /// @see #encodePolyId - static int DecodePolyIdSalt(long refs) - { - long saltMask = (1L << DT_SALT_BITS) - 1; - return (int)((refs >> (DT_POLY_BITS + DT_TILE_BITS)) & saltMask); - } - - /// Extracts the tile's index from the specified polygon reference. - /// @note This function is generally meant for internal use only. - /// @param[in] ref The polygon reference. - /// @see #encodePolyId - public static int DecodePolyIdTile(long refs) - { - long tileMask = (1L << DT_TILE_BITS) - 1; - return (int)((refs >> DT_POLY_BITS) & tileMask); - } - - /// Extracts the polygon's index (within its tile) from the specified - /// polygon reference. - /// @note This function is generally meant for internal use only. - /// @param[in] ref The polygon reference. - /// @see #encodePolyId - static int DecodePolyIdPoly(long refs) - { - long polyMask = (1L << DT_POLY_BITS) - 1; - return (int)(refs & polyMask); - } - private int AllocLink(DtMeshTile tile) { if (tile.linksFreeList == DT_NULL_LINK) - { - DtLink link = new DtLink(); - link.next = DT_NULL_LINK; - tile.links.Add(link); - return tile.links.Count - 1; - } + return DT_NULL_LINK; int linkIdx = tile.linksFreeList; tile.linksFreeList = tile.links[linkIdx].next; @@ -359,8 +259,8 @@ namespace DotRecast.Detour var tbmax = tile.data.header.bmax; float qfac = tile.data.header.bvQuantFactor; // Calculate quantized box - Span bmin = stackalloc int[3]; - Span bmax = stackalloc int[3]; + RcVec3i bmin; + RcVec3i bmax; // dtClamp query box to world box. float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; @@ -369,12 +269,12 @@ namespace DotRecast.Detour float maxy = Math.Clamp(qmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float maxz = Math.Clamp(qmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z; // Quantize - bmin[0] = (int)(qfac * minx) & 0x7ffffffe; - bmin[1] = (int)(qfac * miny) & 0x7ffffffe; - bmin[2] = (int)(qfac * minz) & 0x7ffffffe; - bmax[0] = (int)(qfac * maxx + 1) | 1; - bmax[1] = (int)(qfac * maxy + 1) | 1; - bmax[2] = (int)(qfac * maxz + 1) | 1; + bmin.X = (int)(qfac * minx) & 0x7ffffffe; + bmin.Y = (int)(qfac * miny) & 0x7ffffffe; + bmin.Z = (int)(qfac * minz) & 0x7ffffffe; + bmax.X = (int)(qfac * maxx + 1) | 1; + bmax.Y = (int)(qfac * maxy + 1) | 1; + bmax.Z = (int)(qfac * maxz + 1) | 1; // Traverse tree long @base = GetPolyRefBase(tile); @@ -382,7 +282,7 @@ namespace DotRecast.Detour while (nodeIndex < end) { DtBVNode node = tile.data.bvTree[nodeIndex]; - bool overlap = DtUtils.OverlapQuantBounds(bmin, bmax, node.bmin, node.bmax); + bool overlap = DtUtils.OverlapQuantBounds(ref bmin, ref bmax, ref node.bmin, ref node.bmax); bool isLeafNode = node.i >= 0; if (isLeafNode && overlap) @@ -419,13 +319,13 @@ namespace DotRecast.Detour // Calc polygon bounds. int v = p.verts[0] * 3; - bmin = RcVecUtils.Create(tile.data.verts, v); - bmax = RcVecUtils.Create(tile.data.verts, v); + bmin = RcVec.Create(tile.data.verts, v); + bmax = RcVec.Create(tile.data.verts, v); for (int j = 1; j < p.vertCount; ++j) { v = p.verts[j] * 3; - bmin = RcVecUtils.Min(bmin, tile.data.verts, v); - bmax = RcVecUtils.Max(bmax, tile.data.verts, v); + bmin = RcVec3f.Min(bmin, RcVec.Create(tile.data.verts, v)); + bmax = RcVec3f.Max(bmax, RcVec.Create(tile.data.verts, v)); } if (DtUtils.OverlapBounds(qmin, qmax, bmin, bmax)) @@ -438,31 +338,21 @@ namespace DotRecast.Detour } } - public long UpdateTile(DtMeshData data, int flags) + public DtStatus UpdateTile(DtMeshData data, int flags) { long refs = GetTileRefAt(data.header.x, data.header.y, data.header.layer); refs = RemoveTile(refs); - return AddTile(data, flags, refs); + return AddTile(data, flags, refs, out _); } - /// Adds a tile to the navigation mesh. - /// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData) - /// @param[in] dataSize Data size of the new tile mesh. - /// @param[in] flags Tile flags. (See: #dtTileFlags) - /// @param[in] lastRef The desired reference for the tile. (When reloading a - /// tile.) [opt] [Default: 0] - /// @param[out] result The tile reference. (If the tile was succesfully - /// added.) [opt] - /// @return The status flags for the operation. /// @par /// - /// The add operation will fail if the data is in the wrong format, the - /// allocated tile + /// The add operation will fail if the data is in the wrong format, the allocated tile /// space is full, or there is a tile already at the specified reference. /// /// The lastRef parameter is used to restore a tile with the same tile - /// reference it had previously used. In this case the #long's for the - /// tile will be restored to the same values they were before the tile was + /// reference it had previously used. In this case the #dtPolyRef's for the + /// tile will be restored to the same values they were before the tile was /// removed. /// /// The nav mesh assumes exclusive access to the data passed and will make @@ -471,30 +361,37 @@ namespace DotRecast.Detour /// removed from this nav mesh. /// /// @see dtCreateNavMeshData, #removeTile - public long AddTile(DtMeshData data, int flags, long lastRef) + /// Adds a tile to the navigation mesh. + /// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData) + /// @param[in] dataSize Data size of the new tile mesh. + /// @param[in] flags Tile flags. (See: #dtTileFlags) + /// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0] + /// @param[out] result The tile reference. (If the tile was succesfully added.) [opt] + /// @return The status flags for the operation. + public DtStatus AddTile(DtMeshData data, int flags, long lastRef, out long result) { + result = 0; + // Make sure the data is in right format. DtMeshHeader header = data.header; // Make sure the location is free. if (GetTileAt(header.x, header.y, header.layer) != null) { - throw new Exception("Tile already exists"); + return DtStatus.DT_FAILURE | DtStatus.DT_ALREADY_OCCUPIED; } // Allocate a tile. DtMeshTile tile = null; if (lastRef == 0) { - // Make sure we could allocate a tile. - if (0 == availableTiles.Count) + if (null != m_nextFree) { - throw new Exception("Could not allocate a tile"); + tile = m_nextFree; + m_nextFree = tile.next; + tile.next = null; + m_tileCount++; } - - tile = availableTiles.First?.Value; - availableTiles.RemoveFirst(); - m_tileCount++; } else { @@ -502,33 +399,53 @@ namespace DotRecast.Detour int tileIndex = DecodePolyIdTile(lastRef); if (tileIndex >= m_maxTiles) { - throw new Exception("Tile index too high"); + return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY; } // Try to find the specific tile id from the free list. DtMeshTile target = m_tiles[tileIndex]; - // Remove from freelist - if (!availableTiles.Remove(target)) + DtMeshTile prev = null; + tile = m_nextFree; + + while (null != tile && tile != target) { - // Could not find the correct location. - throw new Exception("Could not find tile"); + prev = tile; + tile = tile.next; } - tile = target; + // Could not find the correct location. + if (tile != target) + return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY; + + // Remove from freelist + if (null == prev) + m_nextFree = tile.next; + else + prev.next = tile.next; + // Restore salt. tile.salt = DecodePolyIdSalt(lastRef); } - tile.data = data; - tile.flags = flags; - tile.links.Clear(); - tile.polyLinks = new int[data.polys.Length]; - Array.Fill(tile.polyLinks, DtNavMesh.DT_NULL_LINK); + // Make sure we could allocate a tile. + if (null == tile) + { + return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY; + } // Insert tile into the position lut. - GetTileListByPos(header.x, header.y).Add(tile); + int h = ComputeTileHash(header.x, header.y, m_tileLutMask); + tile.next = m_posLookup[h]; + m_posLookup[h] = tile; + // Patch header pointers. + tile.data = data; + tile.links = new DtLink[data.header.maxLinkCount]; + for (int i = 0; i < tile.links.Length; ++i) + { + tile.links[i] = new DtLink(); + } // If there are no items in the bvtree, reset the tree pointer. if (tile.data.bvTree != null && tile.data.bvTree.Length == 0) @@ -536,16 +453,29 @@ namespace DotRecast.Detour tile.data.bvTree = null; } + // Build links freelist + tile.linksFreeList = 0; + tile.links[data.header.maxLinkCount - 1].next = DT_NULL_LINK; + for (int i = 0; i < data.header.maxLinkCount - 1; ++i) + tile.links[i].next = i + 1; + // Init tile. + tile.flags = flags; ConnectIntLinks(tile); + // Base off-mesh connections to their starting polygons and connect connections inside the tile. BaseOffMeshLinks(tile); ConnectExtOffMeshLinks(tile, tile, -1); + // Create connections with neighbour tiles. + const int MAX_NEIS = 32; + DtMeshTile[] neis = new DtMeshTile[MAX_NEIS]; + int nneis; + // Connect with layers in current tile. - List neis = GetTilesAt(header.x, header.y); - for (int j = 0; j < neis.Count; ++j) + nneis = GetTilesAt(header.x, header.y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) { if (neis[j] == tile) { @@ -561,8 +491,8 @@ namespace DotRecast.Detour // Connect with neighbour tiles. for (int i = 0; i < 8; ++i) { - neis = GetNeighbourTilesAt(header.x, header.y, i); - for (int j = 0; j < neis.Count; ++j) + nneis = GetNeighbourTilesAt(header.x, header.y, i, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) { ConnectExtLinks(tile, neis[j], i); ConnectExtLinks(neis[j], tile, DtUtils.OppositeTile(i)); @@ -571,7 +501,8 @@ namespace DotRecast.Detour } } - return GetTileRef(tile); + result = GetTileRef(tile); + return DtStatus.DT_SUCCESS; } /// Removes the specified tile from the navigation mesh. @@ -604,39 +535,52 @@ namespace DotRecast.Detour } // Remove tile from hash lookup. - GetTileListByPos(tile.data.header.x, tile.data.header.y).Remove(tile); - - // Remove connections to neighbour tiles. - // Create connections with neighbour tiles. - - // Disconnect from other layers in current tile. - List nneis = GetTilesAt(tile.data.header.x, tile.data.header.y); - foreach (DtMeshTile j in nneis) + int h = ComputeTileHash(tile.data.header.x, tile.data.header.y, m_tileLutMask); + DtMeshTile prev = null; + DtMeshTile cur = m_posLookup[h]; + while (null != cur) { - if (j == tile) + if (cur == tile) { - continue; + if (null != prev) + prev.next = cur.next; + else + m_posLookup[h] = cur.next; + break; } - UnconnectLinks(j, tile); + prev = cur; + cur = cur.next; + } + + // Remove connections to neighbour tiles. + const int MAX_NEIS = 32; + DtMeshTile[] neis = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + // Disconnect from other layers in current tile. + nneis = GetTilesAt(tile.data.header.x, tile.data.header.y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + if (neis[j] == tile) continue; + UnconnectLinks(neis[j], tile); } // Disconnect from neighbour tiles. for (int i = 0; i < 8; ++i) { - nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i); - foreach (DtMeshTile j in nneis) + nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) { - UnconnectLinks(j, tile); + UnconnectLinks(neis[j], tile); } } // Reset tile. tile.data = null; - tile.flags = 0; - tile.links.Clear(); - tile.linksFreeList = DtNavMesh.DT_NULL_LINK; + tile.links = null; + tile.linksFreeList = DT_NULL_LINK; // Update salt, salt should never be zero. tile.salt = (tile.salt + 1) & ((1 << DT_SALT_BITS) - 1); @@ -646,7 +590,8 @@ namespace DotRecast.Detour } // Add to free list. - availableTiles.AddFirst(tile); + tile.next = m_nextFree; + m_nextFree = tile; m_tileCount--; return GetTileRef(tile); } @@ -664,7 +609,7 @@ namespace DotRecast.Detour for (int i = 0; i < tile.data.header.polyCount; ++i) { DtPoly poly = tile.data.polys[i]; - tile.polyLinks[poly.index] = DT_NULL_LINK; + poly.firstLink = DT_NULL_LINK; if (poly.GetPolyType() == DtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -684,17 +629,17 @@ namespace DotRecast.Detour int idx = AllocLink(tile); DtLink link = tile.links[idx]; link.refs = @base | (long)(poly.neis[j] - 1); - link.edge = j; + link.edge = (byte)j; link.side = 0xff; link.bmin = link.bmax = 0; // Add to linked list. - link.next = tile.polyLinks[poly.index]; - tile.polyLinks[poly.index] = idx; + link.next = poly.firstLink; + poly.firstLink = idx; } } } - /// Removes external links at specified side.V + /// Removes external links at specified side. void UnconnectLinks(DtMeshTile tile, DtMeshTile target) { if (tile == null || target == null) @@ -707,7 +652,7 @@ namespace DotRecast.Detour for (int i = 0; i < tile.data.header.polyCount; ++i) { DtPoly poly = tile.data.polys[i]; - int j = tile.polyLinks[poly.index]; + int j = poly.firstLink; int pj = DT_NULL_LINK; while (j != DT_NULL_LINK) { @@ -717,7 +662,7 @@ namespace DotRecast.Detour int nj = tile.links[j].next; if (pj == DT_NULL_LINK) { - tile.polyLinks[poly.index] = nj; + poly.firstLink = nj; } else { @@ -777,46 +722,49 @@ namespace DotRecast.Detour foreach (var connectPoly in connectPolys) { int idx = AllocLink(tile); - DtLink link = tile.links[idx]; - link.refs = connectPoly.refs; - link.edge = j; - link.side = dir; - - link.next = tile.polyLinks[poly.index]; - tile.polyLinks[poly.index] = idx; - - // Compress portal limits to a byte value. - if (dir == 0 || dir == 4) + if (idx != DT_NULL_LINK) { - float tmin = (connectPoly.tmin - tile.data.verts[va + 2]) - / (tile.data.verts[vb + 2] - tile.data.verts[va + 2]); - float tmax = (connectPoly.tmax - tile.data.verts[va + 2]) - / (tile.data.verts[vb + 2] - tile.data.verts[va + 2]); - if (tmin > tmax) - { - float temp = tmin; - tmin = tmax; - tmax = temp; - } + DtLink link = tile.links[idx]; + link.refs = connectPoly.refs; + link.edge = (byte)j; + link.side = (byte)dir; - link.bmin = (int)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); - link.bmax = (int)MathF.Round(Math.Clamp(tmax, 0.0f, 1.0f) * 255.0f); - } - else if (dir == 2 || dir == 6) - { - float tmin = (connectPoly.tmin - tile.data.verts[va]) - / (tile.data.verts[vb] - tile.data.verts[va]); - float tmax = (connectPoly.tmax - tile.data.verts[va]) - / (tile.data.verts[vb] - tile.data.verts[va]); - if (tmin > tmax) - { - float temp = tmin; - tmin = tmax; - tmax = temp; - } + link.next = poly.firstLink; + poly.firstLink = idx; - link.bmin = (int)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); - link.bmax = (int)MathF.Round(Math.Clamp(tmax, 0.0f, 1.0f) * 255.0f); + // Compress portal limits to a byte value. + if (dir == 0 || dir == 4) + { + float tmin = (connectPoly.tmin - tile.data.verts[va + 2]) + / (tile.data.verts[vb + 2] - tile.data.verts[va + 2]); + float tmax = (connectPoly.tmax - tile.data.verts[va + 2]) + / (tile.data.verts[vb + 2] - tile.data.verts[va + 2]); + if (tmin > tmax) + { + float temp = tmin; + tmin = tmax; + tmax = temp; + } + + link.bmin = (byte)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); + link.bmax = (byte)MathF.Round(Math.Clamp(tmax, 0.0f, 1.0f) * 255.0f); + } + else if (dir == 2 || dir == 6) + { + float tmin = (connectPoly.tmin - tile.data.verts[va]) + / (tile.data.verts[vb] - tile.data.verts[va]); + float tmax = (connectPoly.tmax - tile.data.verts[va]) + / (tile.data.verts[vb] - tile.data.verts[va]); + if (tmin > tmax) + { + float temp = tmin; + tmin = tmax; + tmax = temp; + } + + link.bmin = (byte)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); + link.bmax = (byte)MathF.Round(Math.Clamp(tmax, 0.0f, 1.0f) * 255.0f); + } } } } @@ -846,7 +794,7 @@ namespace DotRecast.Detour DtPoly targetPoly = target.data.polys[targetCon.poly]; // Skip off-mesh connections which start location could not be // connected at all. - if (target.polyLinks[targetPoly.index] == DT_NULL_LINK) + if (targetPoly.firstLink == DT_NULL_LINK) { continue; } @@ -884,11 +832,11 @@ namespace DotRecast.Detour DtLink link = target.links[idx]; link.refs = refs; link.edge = 1; - link.side = oppositeSide; + link.side = (byte)oppositeSide; link.bmin = link.bmax = 0; // Add to linked list. - link.next = target.polyLinks[targetPoly.index]; - target.polyLinks[targetPoly.index] = idx; + link.next = targetPoly.firstLink; + targetPoly.firstLink = idx; // Link target poly to off-mesh connection. if ((targetCon.flags & DT_OFFMESH_CON_BIDIR) != 0) @@ -899,11 +847,11 @@ namespace DotRecast.Detour link = tile.links[tidx]; link.refs = GetPolyRefBase(target) | (long)targetCon.poly; link.edge = 0xff; - link.side = (side == -1 ? 0xff : side); + link.side = (byte)(side == -1 ? 0xff : side); link.bmin = link.bmax = 0; // Add to linked list. - link.next = tile.polyLinks[landPoly.index]; - tile.polyLinks[landPoly.index] = tidx; + link.next = landPoly.firstLink; + landPoly.firstLink = tidx; } } } @@ -970,59 +918,7 @@ namespace DotRecast.Detour return n; } - static float GetSlabCoord(float[] verts, int va, int side) - { - if (side == 0 || side == 4) - { - return verts[va]; - } - else if (side == 2 || side == 6) - { - return verts[va + 2]; - } - - return 0; - } - - static void CalcSlabEndPoints(float[] verts, int va, int vb, ref RcVec2f bmin, ref RcVec2f bmax, int side) - { - if (side == 0 || side == 4) - { - if (verts[va + 2] < verts[vb + 2]) - { - bmin.X = verts[va + 2]; - bmin.Y = verts[va + 1]; - bmax.X = verts[vb + 2]; - bmax.Y = verts[vb + 1]; - } - else - { - bmin.X = verts[vb + 2]; - bmin.Y = verts[vb + 1]; - bmax.X = verts[va + 2]; - bmax.Y = verts[va + 1]; - } - } - else if (side == 2 || side == 6) - { - if (verts[va + 0] < verts[vb + 0]) - { - bmin.X = verts[va + 0]; - bmin.Y = verts[va + 1]; - bmax.X = verts[vb + 0]; - bmax.Y = verts[vb + 1]; - } - else - { - bmin.X = verts[vb + 0]; - bmin.Y = verts[vb + 1]; - bmax.X = verts[va + 0]; - bmax.Y = verts[va + 1]; - } - } - } - - bool OverlapSlabs(RcVec2f amin, RcVec2f amax, RcVec2f bmin, RcVec2f bmax, float px, float py) + private bool OverlapSlabs(RcVec2f amin, RcVec2f amax, RcVec2f bmin, RcVec2f bmax, float px, float py) { // Check for horizontal overlap. // The segment is shrunken a little so that slabs which touch @@ -1113,8 +1009,8 @@ namespace DotRecast.Detour link.side = 0xff; link.bmin = link.bmax = 0; // Add to linked list. - link.next = tile.polyLinks[poly.index]; - tile.polyLinks[poly.index] = idx; + link.next = poly.firstLink; + poly.firstLink = idx; // Start end-point is always connect back to off-mesh connection. int tidx = AllocLink(tile); @@ -1126,8 +1022,8 @@ namespace DotRecast.Detour link.side = 0xff; link.bmin = link.bmax = 0; // Add to linked list. - link.next = tile.polyLinks[landPoly.index]; - tile.polyLinks[landPoly.index] = tidx; + link.next = landPoly.firstLink; + landPoly.firstLink = tidx; } } @@ -1149,6 +1045,7 @@ namespace DotRecast.Detour RcVec3f pmin = new RcVec3f(); RcVec3f pmax = new RcVec3f(); + Span tempV = stackalloc RcVec3f[3]; if (tile.data.detailMeshes != null) { ref DtPolyDetail pd = ref tile.data.detailMeshes[ip]; @@ -1161,7 +1058,7 @@ namespace DotRecast.Detour continue; } - RcVec3f[] v = new RcVec3f[3]; + Span v = tempV; for (int j = 0; j < 3; ++j) { if (tris[ti + j] < poly.vertCount) @@ -1209,7 +1106,7 @@ namespace DotRecast.Detour } else { - RcVec3f[] v = new RcVec3f[2]; + Span v = tempV.Slice(0, 2); for (int j = 0; j < poly.vertCount; ++j) { int k = (j + 1) % poly.vertCount; @@ -1247,26 +1144,27 @@ namespace DotRecast.Detour int ip = poly.index; - using var verts = RcRentedArray.Rent(m_maxVertPerPoly * 3); + Span verts = stackalloc float[m_maxVertPerPoly * 3]; int nv = poly.vertCount; for (int i = 0; i < nv; ++i) { - RcArrays.Copy(tile.data.verts, poly.verts[i] * 3, verts.AsArray(), i * 3, 3); + RcSpans.Copy(tile.data.verts, poly.verts[i] * 3, verts, i * 3, 3); } - if (!DtUtils.PointInPolygon(pos, verts.AsArray(), nv)) + if (!DtUtils.PointInPolygon(pos, verts, nv)) { return false; } // Find height at the location. + Span tempV = stackalloc RcVec3f[3]; if (tile.data.detailMeshes != null) { ref DtPolyDetail pd = ref tile.data.detailMeshes[ip]; for (int j = 0; j < pd.triCount; ++j) { int t = (pd.triBase + j) * 4; - RcVec3f[] v = new RcVec3f[3]; + Span v = tempV; for (int k = 0; k < 3; ++k) { if (tile.data.detailTris[t + k] < poly.vertCount) @@ -1300,7 +1198,7 @@ namespace DotRecast.Detour } else { - RcVec3f[] v = new RcVec3f[3]; + Span v = tempV; v[0].X = tile.data.verts[poly.verts[0] * 3]; v[0].Y = tile.data.verts[poly.verts[0] * 3 + 1]; v[0].Z = tile.data.verts[poly.verts[0] * 3 + 2]; @@ -1408,19 +1306,27 @@ namespace DotRecast.Detour DtMeshTile GetTileAt(int x, int y, int layer) { - foreach (DtMeshTile tile in GetTileListByPos(x, y)) + // Find tile based on hash. + int h = ComputeTileHash(x, y, m_tileLutMask); + DtMeshTile tile = m_posLookup[h]; + while (null != tile) { - if (tile.data.header != null && tile.data.header.x == x && tile.data.header.y == y - && tile.data.header.layer == layer) + if (null != tile.data && + null != tile.data.header && + tile.data.header.x == x && + tile.data.header.y == y && + tile.data.header.layer == layer) { return tile; } + + tile = tile.next; } return null; } - List GetNeighbourTilesAt(int x, int y, int side) + int GetNeighbourTilesAt(int x, int y, int side, DtMeshTile[] tiles, int maxTiles) { int nx = x, ny = y; switch (side) @@ -1455,21 +1361,31 @@ namespace DotRecast.Detour break; } - return GetTilesAt(nx, ny); + return GetTilesAt(nx, ny, tiles, maxTiles); } - public List GetTilesAt(int x, int y) + public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles) { - List tiles = new List(); - foreach (DtMeshTile tile in GetTileListByPos(x, y)) + int n = 0; + + // Find tile based on hash. + int h = ComputeTileHash(x, y, m_tileLutMask); + DtMeshTile tile = m_posLookup[h]; + while (null != tile) { - if (tile.data.header != null && tile.data.header.x == x && tile.data.header.y == y) + if (null != tile.data && + null != tile.data.header && + tile.data.header.x == x && + tile.data.header.y == y) { - tiles.Add(tile); + if (n < maxTiles) + tiles[n++] = tile; } + + tile = tile.next; } - return tiles; + return n; } public long GetTileRefAt(int x, int y, int layer) @@ -1510,14 +1426,6 @@ namespace DotRecast.Detour return EncodePolyId(tile.salt, tile.index, 0); } - public static int ComputeTileHash(int x, int y, int mask) - { - uint h1 = 0x8da6b343; // Large multiplicative constants; - uint h2 = 0xd8163841; // here arbitrarily chosen primes - uint n = h1 * (uint)x + h2 * (uint)y; - return (int)(n & mask); - } - /// Gets the endpoints for an off-mesh connection, ordered by "direction of travel". /// @param[in] prevRef The reference of the polygon before the connection. /// @param[in] polyRef The reference of the off-mesh connection polygon. @@ -1569,7 +1477,7 @@ namespace DotRecast.Detour int idx0 = 0, idx1 = 1; // Find link that points to first vertex. - for (int i = tile.polyLinks[poly.index]; i != DT_NULL_LINK; i = tile.links[i].next) + for (int i = poly.firstLink; i != DT_NULL_LINK; i = tile.links[i].next) { if (tile.links[i].edge == 0) { @@ -1583,8 +1491,8 @@ namespace DotRecast.Detour } } - startPos = RcVecUtils.Create(tile.data.verts, poly.verts[idx0] * 3); - endPos = RcVecUtils.Create(tile.data.verts, poly.verts[idx1] * 3); + startPos = RcVec.Create(tile.data.verts, poly.verts[idx0] * 3); + endPos = RcVec.Create(tile.data.verts, poly.verts[idx1] * 3); return DtStatus.DT_SUCCESS; } @@ -1599,9 +1507,9 @@ namespace DotRecast.Detour return m_tileCount; } - public int GetAvailableTileCount() + public bool IsAvailableTileCount() { - return availableTiles.Count; + return null != m_nextFree; } public DtStatus SetPolyFlags(long refs, int flags) @@ -1759,32 +1667,6 @@ namespace DotRecast.Detour return center; } - /** - * Get flags for edge in detail triangle. - * - * @param triFlags - * The flags for the triangle (last component of detail vertices above). - * @param edgeIndex - * The index of the first vertex of the edge. For instance, if 0, - * @return flags for edge AB. - */ - public static int GetDetailTriEdgeFlags(int triFlags, int edgeIndex) - { - return (triFlags >> (edgeIndex * 2)) & 0x3; - } - - private List GetTileListByPos(int x, int z) - { - var tileHash = ComputeTileHash(x, z, m_tileLutMask); - if (!posLookup.TryGetValue(tileHash, out var tiles)) - { - tiles = new List(); - posLookup.Add(tileHash, tiles); - } - - return tiles; - } - public void ComputeBounds(out RcVec3f bmin, out RcVec3f bmax) { bmin = new RcVec3f(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); diff --git a/src/DotRecast.Detour/DtNavMeshBuilder.cs b/src/DotRecast.Detour/DtNavMeshBuilder.cs index 81258db..7f5c863 100644 --- a/src/DotRecast.Detour/DtNavMeshBuilder.cs +++ b/src/DotRecast.Detour/DtNavMeshBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,42 +24,35 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour { + using static DtDetour; + public static class DtNavMeshBuilder { const int MESH_NULL_IDX = 0xffff; - private static int[][] CalcExtends(BVItem[] items, int nitems, int imin, int imax) + private static void CalcExtends(BVItem[] items, int nitems, int imin, int imax, ref RcVec3i bmin, ref RcVec3i bmax) { - int[] bmin = new int[3]; - int[] bmax = new int[3]; - bmin[0] = items[imin].bmin[0]; - bmin[1] = items[imin].bmin[1]; - bmin[2] = items[imin].bmin[2]; - - bmax[0] = items[imin].bmax[0]; - bmax[1] = items[imin].bmax[1]; - bmax[2] = items[imin].bmax[2]; + bmin = items[imin].bmin; + bmax = items[imin].bmax; for (int i = imin + 1; i < imax; ++i) { BVItem it = items[i]; - if (it.bmin[0] < bmin[0]) - bmin[0] = it.bmin[0]; - if (it.bmin[1] < bmin[1]) - bmin[1] = it.bmin[1]; - if (it.bmin[2] < bmin[2]) - bmin[2] = it.bmin[2]; + if (it.bmin.X < bmin.X) + bmin.X = it.bmin.X; + if (it.bmin.Y < bmin.Y) + bmin.Y = it.bmin.Y; + if (it.bmin.Z < bmin.Z) + bmin.Z = it.bmin.Z; - if (it.bmax[0] > bmax[0]) - bmax[0] = it.bmax[0]; - if (it.bmax[1] > bmax[1]) - bmax[1] = it.bmax[1]; - if (it.bmax[2] > bmax[2]) - bmax[2] = it.bmax[2]; + if (it.bmax.X > bmax.X) + bmax.X = it.bmax.X; + if (it.bmax.Y > bmax.Y) + bmax.Y = it.bmax.Y; + if (it.bmax.Z > bmax.Z) + bmax.Z = it.bmax.Z; } - - return new int[][] { bmin, bmax }; } private static int LongestAxis(int x, int y, int z) @@ -92,25 +85,21 @@ namespace DotRecast.Detour if (inum == 1) { // Leaf - node.bmin[0] = items[imin].bmin[0]; - node.bmin[1] = items[imin].bmin[1]; - node.bmin[2] = items[imin].bmin[2]; - - node.bmax[0] = items[imin].bmax[0]; - node.bmax[1] = items[imin].bmax[1]; - node.bmax[2] = items[imin].bmax[2]; + node.bmin = items[imin].bmin; + node.bmax = items[imin].bmax; node.i = items[imin].i; } else { // Split - int[][] minmax = CalcExtends(items, nitems, imin, imax); - node.bmin = minmax[0]; - node.bmax = minmax[1]; + CalcExtends(items, nitems, imin, imax, ref node.bmin, ref node.bmax); - int axis = LongestAxis(node.bmax[0] - node.bmin[0], node.bmax[1] - node.bmin[1], - node.bmax[2] - node.bmin[2]); + int axis = LongestAxis( + node.bmax.X - node.bmin.X, + node.bmax.Y - node.bmin.Y, + node.bmax.Z - node.bmin.Z + ); if (axis == 0) { @@ -159,29 +148,29 @@ namespace DotRecast.Detour int vb = option.detailMeshes[i * 4 + 0]; int ndv = option.detailMeshes[i * 4 + 1]; int dv = vb * 3; - var bmin = RcVecUtils.Create(option.detailVerts, dv); - var bmax = RcVecUtils.Create(option.detailVerts, dv); + var bmin = RcVec.Create(option.detailVerts, dv); + var bmax = RcVec.Create(option.detailVerts, dv); for (int j = 1; j < ndv; j++) { - bmin = RcVecUtils.Min(bmin, option.detailVerts, dv + j * 3); - bmax = RcVecUtils.Max(bmax, option.detailVerts, dv + j * 3); + bmin = RcVec3f.Min(bmin, RcVec.Create(option.detailVerts, dv + j * 3)); + bmax = RcVec3f.Max(bmax, RcVec.Create(option.detailVerts, dv + j * 3)); } // BV-tree uses cs for all dimensions - it.bmin[0] = Math.Clamp((int)((bmin.X - option.bmin.X) * quantFactor), 0, int.MaxValue); - it.bmin[1] = Math.Clamp((int)((bmin.Y - option.bmin.Y) * quantFactor), 0, int.MaxValue); - it.bmin[2] = Math.Clamp((int)((bmin.Z - option.bmin.Z) * quantFactor), 0, int.MaxValue); + it.bmin.X = Math.Clamp((int)((bmin.X - option.bmin.X) * quantFactor), 0, int.MaxValue); + it.bmin.Y = Math.Clamp((int)((bmin.Y - option.bmin.Y) * quantFactor), 0, int.MaxValue); + it.bmin.Z = Math.Clamp((int)((bmin.Z - option.bmin.Z) * quantFactor), 0, int.MaxValue); - it.bmax[0] = Math.Clamp((int)((bmax.X - option.bmin.X) * quantFactor), 0, int.MaxValue); - it.bmax[1] = Math.Clamp((int)((bmax.Y - option.bmin.Y) * quantFactor), 0, int.MaxValue); - it.bmax[2] = Math.Clamp((int)((bmax.Z - option.bmin.Z) * quantFactor), 0, int.MaxValue); + it.bmax.X = Math.Clamp((int)((bmax.X - option.bmin.X) * quantFactor), 0, int.MaxValue); + it.bmax.Y = Math.Clamp((int)((bmax.Y - option.bmin.Y) * quantFactor), 0, int.MaxValue); + it.bmax.Z = Math.Clamp((int)((bmax.Z - option.bmin.Z) * quantFactor), 0, int.MaxValue); } else { int p = i * option.nvp * 2; - it.bmin[0] = it.bmax[0] = option.verts[option.polys[p] * 3 + 0]; - it.bmin[1] = it.bmax[1] = option.verts[option.polys[p] * 3 + 1]; - it.bmin[2] = it.bmax[2] = option.verts[option.polys[p] * 3 + 2]; + it.bmin.X = it.bmax.X = option.verts[option.polys[p] * 3 + 0]; + it.bmin.Y = it.bmax.Y = option.verts[option.polys[p] * 3 + 1]; + it.bmin.Z = it.bmax.Z = option.verts[option.polys[p] * 3 + 2]; for (int j = 1; j < option.nvp; ++j) { @@ -191,24 +180,24 @@ namespace DotRecast.Detour int y = option.verts[option.polys[p + j] * 3 + 1]; int z = option.verts[option.polys[p + j] * 3 + 2]; - if (x < it.bmin[0]) - it.bmin[0] = x; - if (y < it.bmin[1]) - it.bmin[1] = y; - if (z < it.bmin[2]) - it.bmin[2] = z; + if (x < it.bmin.X) + it.bmin.X = x; + if (y < it.bmin.Y) + it.bmin.Y = y; + if (z < it.bmin.Z) + it.bmin.Z = z; - if (x > it.bmax[0]) - it.bmax[0] = x; - if (y > it.bmax[1]) - it.bmax[1] = y; - if (z > it.bmax[2]) - it.bmax[2] = z; + if (x > it.bmax.X) + it.bmax.X = x; + if (y > it.bmax.Y) + it.bmax.Y = y; + if (z > it.bmax.Z) + it.bmax.Z = z; } // Remap y - it.bmin[1] = (int)MathF.Floor(it.bmin[1] * option.ch * quantFactor); - it.bmax[1] = (int)MathF.Ceiling(it.bmax[1] * option.ch * quantFactor); + it.bmin.Y = (int)MathF.Floor(it.bmin.Y * option.ch * quantFactor); + it.bmax.Y = (int)MathF.Ceiling(it.bmax.Y * option.ch * quantFactor); } } @@ -251,14 +240,15 @@ namespace DotRecast.Detour return 0xff; } - /** - * Builds navigation mesh tile data from the provided tile creation data. - * - * @param option - * Tile creation data. - * - * @return created tile data - */ + // TODO: Better error handling. + + /// @par + /// + /// The output data array is allocated using the detour allocator (dtAlloc()). The method + /// used to free the memory will be determined by how the tile is added to the navigation + /// mesh. + /// + /// @see dtNavMesh, dtNavMesh::addTile() public static DtMeshData CreateNavMeshData(DtNavMeshCreateParams option) { if (option.vertCount >= 0xffff) @@ -316,8 +306,8 @@ namespace DotRecast.Detour for (int i = 0; i < option.offMeshConCount; ++i) { - var p0 = RcVecUtils.Create(option.offMeshConVerts, (i * 2 + 0) * 3); - var p1 = RcVecUtils.Create(option.offMeshConVerts, (i * 2 + 1) * 3); + var p0 = RcVec.Create(option.offMeshConVerts, (i * 2 + 0) * 3); + var p1 = RcVec.Create(option.offMeshConVerts, (i * 2 + 1) * 3); offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(p0, bmin, bmax); offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(p1, bmin, bmax); @@ -424,8 +414,8 @@ namespace DotRecast.Detour DtOffMeshConnection[] offMeshCons = new DtOffMeshConnection[storedOffMeshConCount]; // Store header - header.magic = DtNavMesh.DT_NAVMESH_MAGIC; - header.version = DtNavMesh.DT_NAVMESH_VERSION; + header.magic = DT_NAVMESH_MAGIC; + header.version = DT_NAVMESH_VERSION; header.x = option.tileX; header.y = option.tileZ; header.layer = option.tileLayer; @@ -497,13 +487,13 @@ namespace DotRecast.Detour if (dir == 0xf) // Border p.neis[j] = 0; else if (dir == 0) // Portal x- - p.neis[j] = DtNavMesh.DT_EXT_LINK | 4; + p.neis[j] = DT_EXT_LINK | 4; else if (dir == 1) // Portal z+ - p.neis[j] = DtNavMesh.DT_EXT_LINK | 2; + p.neis[j] = DT_EXT_LINK | 2; else if (dir == 2) // Portal x+ - p.neis[j] = DtNavMesh.DT_EXT_LINK | 0; + p.neis[j] = DT_EXT_LINK | 0; else if (dir == 3) // Portal z- - p.neis[j] = DtNavMesh.DT_EXT_LINK | 6; + p.neis[j] = DT_EXT_LINK | 6; } else { @@ -550,9 +540,9 @@ namespace DotRecast.Detour int ndv = option.detailMeshes[i * 4 + 1]; int nv = navPolys[i].vertCount; int vertBase = vbase; - int vertCount = (ndv - nv); + byte vertCount = (byte)(ndv - nv); int triBase = option.detailMeshes[i * 4 + 2]; - int triCount = option.detailMeshes[i * 4 + 3]; + byte triCount = (byte)option.detailMeshes[i * 4 + 3]; navDMeshes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); // Copy vertices except the first 'nv' verts which are equal to // nav poly verts. @@ -574,9 +564,9 @@ namespace DotRecast.Detour { int nv = navPolys[i].vertCount; int vertBase = 0; - int vertCount = 0; + byte vertCount = 0; int triBase = tbase; - int triCount = (nv - 2); + byte triCount = (byte)(nv - 2); navDMeshes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) @@ -618,11 +608,11 @@ namespace DotRecast.Detour int endPts = i * 2 * 3; for (int j = 0; j < 2; ++j) { - con.pos[j] = RcVecUtils.Create(option.offMeshConVerts, endPts + (j * 3)); + con.pos[j] = RcVec.Create(option.offMeshConVerts, endPts + (j * 3)); } con.rad = option.offMeshConRad[i]; - con.flags = option.offMeshConDir[i] != 0 ? DtNavMesh.DT_OFFMESH_CON_BIDIR : 0; + con.flags = option.offMeshConDir[i] != 0 ? DT_OFFMESH_CON_BIDIR : 0; con.side = offMeshConClass[i * 2 + 1]; if (option.offMeshConUserID != null) con.userId = option.offMeshConUserID[i]; diff --git a/src/DotRecast.Detour/DtNavMeshCreateParams.cs b/src/DotRecast.Detour/DtNavMeshCreateParams.cs index 741671f..508851e 100644 --- a/src/DotRecast.Detour/DtNavMeshCreateParams.cs +++ b/src/DotRecast.Detour/DtNavMeshCreateParams.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtNavMeshParams.cs b/src/DotRecast.Detour/DtNavMeshParams.cs index 5f0168d..fb17250 100644 --- a/src/DotRecast.Detour/DtNavMeshParams.cs +++ b/src/DotRecast.Detour/DtNavMeshParams.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,27 +23,16 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour { - /** - * Configuration parameters used to define multi-tile navigation meshes. The values are used to allocate space during - * the initialization of a navigation mesh. - * - * @see NavMesh - */ + /// Configuration parameters used to define multi-tile navigation meshes. + /// The values are used to allocate space during the initialization of a navigation mesh. + /// @see dtNavMesh::init() + /// @ingroup detour public struct DtNavMeshParams { - /** The world space origin of the navigation mesh's tile space. [(x, y, z)] */ - public RcVec3f orig; - - /** The width of each tile. (Along the x-axis.) */ - public float tileWidth; - - /** The height of each tile. (Along the z-axis.) */ - public float tileHeight; - - /** The maximum number of tiles the navigation mesh can contain. */ - public int maxTiles; - - /** The maximum number of polygons each tile can contain. */ - public int maxPolys; + public RcVec3f orig; //< The world space origin of the navigation mesh's tile space. [(x, y, z)] + public float tileWidth; //< The width of each tile. (Along the x-axis.) + public float tileHeight; //< The height of each tile. (Along the z-axis.) + public int maxTiles; //< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely. + public int maxPolys; //< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely. } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtNavMeshQuery.cs b/src/DotRecast.Detour/DtNavMeshQuery.cs index aaf23f8..ee0e699 100644 --- a/src/DotRecast.Detour/DtNavMeshQuery.cs +++ b/src/DotRecast.Detour/DtNavMeshQuery.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -21,24 +21,41 @@ freely, subject to the following restrictions: using System; using System.Collections.Generic; using DotRecast.Core; -using DotRecast.Core.Buffers; -using DotRecast.Core.Collections; using DotRecast.Core.Numerics; namespace DotRecast.Detour { + using static DtDetour; + + /// Provides the ability to perform pathfinding related queries against + /// a navigation mesh. + /// @ingroup detour public class DtNavMeshQuery { - /// < Add a vertex at every polygon edge crossing. - protected readonly DtNavMesh m_nav; + protected readonly DtNavMesh m_nav; //< Pointer to navmesh data. + protected DtQueryData m_query; //< Sliced query state. - protected readonly DtNodePool m_tinyNodePool; - protected readonly DtNodePool m_nodePool; - protected readonly DtNodeQueue m_openList; + protected readonly DtNodePool m_tinyNodePool; //< Pointer to small node pool. + protected readonly DtNodePool m_nodePool; //< Pointer to node pool. + protected readonly DtNodeQueue m_openList; //< Pointer to open list queue. - protected DtQueryData m_query; + ////////////////////////////////////////////////////////////////////////////////////////// - /// < Sliced query state. + /// @class dtNavMeshQuery + /// + /// For methods that support undersized buffers, if the buffer is too small + /// to hold the entire result set the return status of the method will include + /// the #DT_BUFFER_TOO_SMALL flag. + /// + /// Constant member functions can be used by multiple clients without side + /// effects. (E.g. No change to the closed list. No impact on an in-progress + /// sliced path query. Etc.) + /// + /// Walls and portals: A @e wall is a polygon segment that is + /// considered impassable. A @e portal is a passable segment between polygons. + /// A portal may be treated as a wall based on the dtQueryFilter used for a query. + /// + /// @see dtNavMesh, dtQueryFilter, #dtAllocNavMeshQuery(), #dtAllocNavMeshQuery() public DtNavMeshQuery(DtNavMesh nav) { m_nav = nav; @@ -69,8 +86,8 @@ namespace DotRecast.Detour float tsum = 0.0f; for (int i = 0; i < m_nav.GetMaxTiles(); i++) { - DtMeshTile mt = m_nav.GetTile(i); - if (mt == null || mt.data == null || mt.data.header == null) + DtMeshTile t = m_nav.GetTile(i); + if (t == null || t.data == null || t.data.header == null) { continue; } @@ -81,7 +98,7 @@ namespace DotRecast.Detour float u = frand.Next(); if (u * tsum <= area) { - tile = mt; + tile = t; } } @@ -138,18 +155,18 @@ namespace DotRecast.Detour } // Randomly pick point on polygon. - using var verts = RcRentedArray.Rent(3 * m_nav.GetMaxVertsPerPoly()); - using var areas = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly()); - RcArrays.Copy(tile.data.verts, poly.verts[0] * 3, verts.AsArray(), 0, 3); + Span verts = stackalloc float[3 * m_nav.GetMaxVertsPerPoly()]; + Span areas = stackalloc float[m_nav.GetMaxVertsPerPoly()]; + RcSpans.Copy(tile.data.verts, poly.verts[0] * 3, verts, 0, 3); for (int j = 1; j < poly.vertCount; ++j) { - RcArrays.Copy(tile.data.verts, poly.verts[j] * 3, verts.AsArray(), j * 3, 3); + RcSpans.Copy(tile.data.verts, poly.verts[j] * 3, verts, j * 3, 3); } float s = frand.Next(); - float t = frand.Next(); + float t0 = frand.Next(); - var pt = DtUtils.RandomPointInConvexPoly(verts.AsArray(), poly.vertCount, areas.AsArray(), s, t); + DtUtils.RandomPointInConvexPoly(verts, poly.vertCount, areas, s, t0, out var pt); ClosestPointOnPoly(polyRef, pt, out var closest, out var _); randomRef = polyRef; @@ -254,6 +271,7 @@ namespace DotRecast.Detour DtNode bestNode = m_openList.Pop(); bestNode.flags &= ~DtNodeFlags.DT_NODE_OPEN; bestNode.flags |= DtNodeFlags.DT_NODE_CLOSED; + // Get poly and tile. // The API input has been checked already, skip checking internal data. long bestRef = bestNode.id; @@ -301,7 +319,7 @@ namespace DotRecast.Detour parentRef = m_nodePool.GetNodeAtIdx(bestNode.pidx).id; } - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { DtLink link = bestTile.links[i]; long neighbourRef = link.refs; @@ -387,8 +405,8 @@ namespace DotRecast.Detour float s = frand.Next(); float t = frand.Next(); - using var areas = RcRentedArray.Rent(randomPolyVerts.Length / 3); - RcVec3f pt = DtUtils.RandomPointInConvexPoly(randomPolyVerts, randomPolyVerts.Length / 3, areas.AsArray(), s, t); + Span areas = stackalloc float[randomPolyVerts.Length / 3]; + DtUtils.RandomPointInConvexPoly(randomPolyVerts, randomPolyVerts.Length / 3, areas, s, t, out var pt); ClosestPointOnPoly(randomPolyRef, pt, out var closest, out var _); randomRef = randomPolyRef; @@ -458,16 +476,16 @@ namespace DotRecast.Detour } // Collect vertices. - using var verts = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly() * 3); - using var edged = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly()); - using var edget = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly()); + Span verts = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3]; + Span edged = stackalloc float[m_nav.GetMaxVertsPerPoly()]; + Span edget = stackalloc float[m_nav.GetMaxVertsPerPoly()]; int nv = poly.vertCount; for (int i = 0; i < nv; ++i) { - RcArrays.Copy(tile.data.verts, poly.verts[i] * 3, verts.AsArray(), i * 3, 3); + RcSpans.Copy(tile.data.verts, poly.verts[i] * 3, verts, i * 3, 3); } - if (DtUtils.DistancePtPolyEdgesSqr(pos, verts.AsArray(), nv, edged.AsArray(), edget.AsArray())) + if (DtUtils.DistancePtPolyEdgesSqr(pos, verts, nv, edged, edget)) { closest = pos; } @@ -487,7 +505,7 @@ namespace DotRecast.Detour int va = imin * 3; int vb = ((imin + 1) % nv) * 3; - closest = RcVecUtils.Lerp(verts.AsArray(), va, vb, edget[imin]); + closest = RcVec.Lerp(verts, va, vb, edget[imin]); } return DtStatus.DT_SUCCESS; @@ -574,18 +592,25 @@ namespace DotRecast.Detour return DtStatus.DT_SUCCESS; } - // FIXME: (PP) duplicate? + /// Queries polygons within a tile. protected void QueryPolygonsInTile(DtMeshTile tile, RcVec3f qmin, RcVec3f qmax, IDtQueryFilter filter, IDtPolyQuery query) { + const int batchSize = 32; + Span polyRefs = stackalloc long[batchSize]; + DtPoly[] polys = new DtPoly[batchSize]; + int n = 0; + if (tile.data.bvTree != null) { int nodeIndex = 0; + int end = tile.data.header.bvNodeCount; var tbmin = tile.data.header.bmin; var tbmax = tile.data.header.bmax; float qfac = tile.data.header.bvQuantFactor; + // Calculate quantized box - Span bmin = stackalloc int[3]; - Span bmax = stackalloc int[3]; + RcVec3i bmin; + RcVec3i bmax; // dtClamp query box to world box. float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; @@ -594,20 +619,19 @@ namespace DotRecast.Detour float maxy = Math.Clamp(qmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float maxz = Math.Clamp(qmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z; // Quantize - bmin[0] = (int)(qfac * minx) & 0x7ffffffe; - bmin[1] = (int)(qfac * miny) & 0x7ffffffe; - bmin[2] = (int)(qfac * minz) & 0x7ffffffe; - bmax[0] = (int)(qfac * maxx + 1) | 1; - bmax[1] = (int)(qfac * maxy + 1) | 1; - bmax[2] = (int)(qfac * maxz + 1) | 1; + bmin.X = (int)(qfac * minx) & 0x7ffffffe; + bmin.Y = (int)(qfac * miny) & 0x7ffffffe; + bmin.Z = (int)(qfac * minz) & 0x7ffffffe; + bmax.X = (int)(qfac * maxx + 1) | 1; + bmax.Y = (int)(qfac * maxy + 1) | 1; + bmax.Z = (int)(qfac * maxz + 1) | 1; // Traverse tree long @base = m_nav.GetPolyRefBase(tile); - int end = tile.data.header.bvNodeCount; while (nodeIndex < end) { DtBVNode node = tile.data.bvTree[nodeIndex]; - bool overlap = DtUtils.OverlapQuantBounds(bmin, bmax, node.bmin, node.bmax); + bool overlap = DtUtils.OverlapQuantBounds(ref bmin, ref bmax, ref node.bmin, ref node.bmax); bool isLeafNode = node.i >= 0; if (isLeafNode && overlap) @@ -615,7 +639,18 @@ namespace DotRecast.Detour long refs = @base | (long)node.i; if (filter.PassFilter(refs, tile, tile.data.polys[node.i])) { - query.Process(tile, tile.data.polys[node.i], refs); + polyRefs[n] = refs; + polys[n] = tile.data.polys[node.i]; + + if (n == batchSize - 1) + { + query.Process(tile, polys, polyRefs, batchSize); + n = 0; + } + else + { + n++; + } } } @@ -644,6 +679,7 @@ namespace DotRecast.Detour continue; } + // Must pass filter long refs = @base | (long)i; if (!filter.PassFilter(refs, tile, p)) { @@ -652,36 +688,88 @@ namespace DotRecast.Detour // Calc polygon bounds. int v = p.verts[0] * 3; - bmin = RcVecUtils.Create(tile.data.verts, v); - bmax = RcVecUtils.Create(tile.data.verts, v); + bmin = RcVec.Create(tile.data.verts, v); + bmax = RcVec.Create(tile.data.verts, v); for (int j = 1; j < p.vertCount; ++j) { v = p.verts[j] * 3; - bmin = RcVecUtils.Min(bmin, tile.data.verts, v); - bmax = RcVecUtils.Max(bmax, tile.data.verts, v); + bmin = RcVec3f.Min(bmin, RcVec.Create(tile.data.verts, v)); + bmax = RcVec3f.Max(bmax, RcVec.Create(tile.data.verts, v)); } if (DtUtils.OverlapBounds(qmin, qmax, bmin, bmax)) { - query.Process(tile, p, refs); + polyRefs[n] = refs; + polys[n] = p; + + if (n == batchSize - 1) + { + query.Process(tile, polys, polyRefs, batchSize); + n = 0; + } + else + { + n++; + } } } } + + // Process the last polygons that didn't make a full batch. + if (n > 0) + { + query.Process(tile, polys, polyRefs, n); + } } - /** - * Finds polygons that overlap the search box. - * - * If no polygons are found, the function will return with a polyCount of zero. - * - * @param center - * The center of the search box. [(x, y, z)] - * @param halfExtents - * The search distance along each axis. [(x, y, z)] - * @param filter - * The polygon filter to apply to the query. - * @return The reference ids of the polygons that overlap the query box. - */ + /// @par + /// + /// If no polygons are found, the function will return #DT_SUCCESS with a + /// @p polyCount of zero. + /// + /// If @p polys is too small to hold the entire result set, then the array will + /// be filled to capacity. The method of choosing which polygons from the + /// full set are included in the partial result set is undefined. + /// + /// Finds polygons that overlap the search box. + /// @param[in] center The center of the search box. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[out] polys The reference ids of the polygons that overlap the query box. + /// @param[out] polyCount The number of polygons in the search result. + /// @param[in] maxPolys The maximum number of polygons the search result can hold. + /// @returns The status flags for the query. + public DtStatus QueryPolygons(RcVec3f center, RcVec3f halfExtents, + IDtQueryFilter filter, + long[] polys, out int polyCount, int maxPolys) + { + polyCount = 0; + if (null == polys || maxPolys < 0) + return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; + + DtCollectPolysQuery collector = new DtCollectPolysQuery(polys, maxPolys); + DtStatus status = QueryPolygons(center, halfExtents, filter, collector); + if (status.Failed()) + return status; + + polyCount = collector.NumCollected(); + return collector.Overflowed() + ? DtStatus.DT_SUCCESS | DtStatus.DT_BUFFER_TOO_SMALL + : DtStatus.DT_SUCCESS; + } + + /// @par + /// + /// The query will be invoked with batches of polygons. Polygons passed + /// to the query have bounding boxes that overlap with the center and halfExtents + /// passed to this function. The dtPolyQuery::process function is invoked multiple + /// times until all overlapping polygons have been processed. + /// + /// Finds polygons that overlap the search box. + /// @param[in] center The center of the search box. [(x, y, z)] + /// @param[in] halfExtents The search distance along each axis. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] query The query. Polygons found will be batched together and passed to this query. public DtStatus QueryPolygons(RcVec3f center, RcVec3f halfExtents, IDtQueryFilter filter, IDtPolyQuery query) { if (!center.IsFinite() || !halfExtents.IsFinite() || null == filter) @@ -692,39 +780,27 @@ namespace DotRecast.Detour // Find tiles the query touches. RcVec3f bmin = RcVec3f.Subtract(center, halfExtents); RcVec3f bmax = RcVec3f.Add(center, halfExtents); - foreach (var t in QueryTiles(center, halfExtents)) - { - QueryPolygonsInTile(t, bmin, bmax, filter, query); - } - return DtStatus.DT_SUCCESS; - } - - /** - * Finds tiles that overlap the search box. - */ - public IList QueryTiles(RcVec3f center, RcVec3f halfExtents) - { - if (!center.IsFinite() || !halfExtents.IsFinite()) - { - return RcImmutableArray.Empty; - } - - RcVec3f bmin = RcVec3f.Subtract(center, halfExtents); - RcVec3f bmax = RcVec3f.Add(center, halfExtents); + // Find tiles the query touches. m_nav.CalcTileLoc(bmin, out var minx, out var miny); m_nav.CalcTileLoc(bmax, out var maxx, out var maxy); - List tiles = new List(); + const int MAX_NEIS = 32; + DtMeshTile[] neis = new DtMeshTile[MAX_NEIS]; + for (int y = miny; y <= maxy; ++y) { for (int x = minx; x <= maxx; ++x) { - tiles.AddRange(m_nav.GetTilesAt(x, y)); + int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + QueryPolygonsInTile(neis[j], bmin, bmax, filter, query); + } } } - return tiles; + return DtStatus.DT_SUCCESS; } /// @par @@ -776,7 +852,7 @@ namespace DotRecast.Detour // so it is enough to compute it from the first tile. DtMeshTile tile = m_nav.GetTileByRef(startRef); float agentRadius = tile.data.header.walkableRadius; - raycastLimitSqr = RcMath.Sqr(agentRadius * DtNavMesh.DT_RAY_CAST_LIMIT_PROPORTIONS); + raycastLimitSqr = RcMath.Sqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS); } if (startRef == endRef) @@ -852,7 +928,7 @@ namespace DotRecast.Detour } } - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { long neighbourRef = bestTile.links[i].refs; @@ -904,7 +980,7 @@ namespace DotRecast.Detour DtRaycastOptions.DT_RAYCAST_USE_COSTS, ref rayHit, grandpaRef); if (rayStatus.Succeeded()) { - foundShortCut = rayHit.t >= 1.0f; + foundShortCut = rayHit.t >= 1.0f && rayHit.path[^1] == neighbourRef; if (foundShortCut) { shortcut = new List(rayHit.path); @@ -990,26 +1066,21 @@ namespace DotRecast.Detour return status; } - /** - * Intializes a sliced path query. - * - * Common use case: -# Call InitSlicedFindPath() to initialize the sliced path query. -# Call UpdateSlicedFindPath() - * until it returns complete. -# Call FinalizeSlicedFindPath() to get the path. - * - * @param startRef - * The reference id of the start polygon. - * @param endRef - * The reference id of the end polygon. - * @param startPos - * A position within the start polygon. [(x, y, z)] - * @param endPos - * A position within the end polygon. [(x, y, z)] - * @param filter - * The polygon filter to apply to the query. - * @param options - * query options (see: #FindPathOptions) - * @return - */ + ///@} + /// @name Sliced Pathfinding Functions + /// Common use case: + /// -# Call initSlicedFindPath() to initialize the sliced path query. + /// -# Call updateSlicedFindPath() until it returns complete. + /// -# Call finalizeSlicedFindPath() to get the path. + ///@{ + /// Initializes a sliced path query. + /// @param[in] startRef The reference id of the start polygon. + /// @param[in] endRef The reference id of the end polygon. + /// @param[in] startPos A position within the start polygon. [(x, y, z)] + /// @param[in] endPos A position within the end polygon. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] options query options (see: #dtFindPathOptions) + /// @returns The status flags for the query. public DtStatus InitSlicedFindPath(long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, int options) { return InitSlicedFindPath(startRef, endRef, startPos, endPos, filter, options, DtDefaultQueryHeuristic.Default, -1.0f); @@ -1047,7 +1118,7 @@ namespace DotRecast.Detour // so it is enough to compute it from the first tile. DtMeshTile tile = m_nav.GetTileByRef(startRef); float agentRadius = tile.data.header.walkableRadius; - m_query.raycastLimitSqr = RcMath.Sqr(agentRadius * DtNavMesh.DT_RAY_CAST_LIMIT_PROPORTIONS); + m_query.raycastLimitSqr = RcMath.Sqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS); } if (startRef == endRef) @@ -1075,13 +1146,10 @@ namespace DotRecast.Detour return m_query.status; } - /** - * Updates an in-progress sliced path query. - * - * @param maxIter - * The maximum number of iterations to perform. - * @return The status flags for the query. - */ + /// Updates an in-progress sliced path query. + /// @param[in] maxIter The maximum number of iterations to perform. + /// @param[out] doneIters The actual number of iterations completed. [opt] + /// @returns The status flags for the query. public virtual DtStatus UpdateSlicedFindPath(int maxIter, out int doneIters) { doneIters = 0; @@ -1173,7 +1241,7 @@ namespace DotRecast.Detour } } - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { long neighbourRef = bestTile.links[i].refs; @@ -1228,7 +1296,7 @@ namespace DotRecast.Detour DtRaycastOptions.DT_RAYCAST_USE_COSTS, ref rayHit, grandpaRef); if (status.Succeeded()) { - foundShortCut = rayHit.t >= 1.0f; + foundShortCut = rayHit.t >= 1.0f && rayHit.path[^1] == neighbourRef; if (foundShortCut) { shortcut = new List(rayHit.path); @@ -1321,8 +1389,10 @@ namespace DotRecast.Detour } /// Finalizes and returns the results of a sliced path query. - /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) - /// [(polyRef) * @p pathCount] + /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) + /// [(polyRef) * @p pathCount] + /// @param[out] pathCount The number of polygons returned in the @p path array. + /// @param[in] maxPath The max number of polygons the path array can hold. [Limit: >= 1] /// @returns The status flags for the query. public virtual DtStatus FinalizeSlicedFindPath(ref List path) { @@ -1364,19 +1434,21 @@ namespace DotRecast.Detour /// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest /// polygon on the existing path that was visited during the search. - /// @param[in] existing An array of polygon references for the existing path. - /// @param[in] existingSize The number of polygon in the @p existing array. - /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) - /// [(polyRef) * @p pathCount] + /// @param[in] existing An array of polygon references for the existing path. + /// @param[in] existingSize The number of polygon in the @p existing array. + /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) + /// [(polyRef) * @p pathCount] + /// @param[out] pathCount The number of polygons returned in the @p path array. + /// @param[in] maxPath The max number of polygons the @p path array can hold. [Limit: >= 1] /// @returns The status flags for the query. - public virtual DtStatus FinalizeSlicedFindPathPartial(List existing, ref List path) + public virtual DtStatus FinalizeSlicedFindPathPartial(List existing, int existingSize, ref List path) { if (null == path) return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; path.Clear(); - if (null == existing || existing.Count <= 0) + if (null == existing || existingSize <= 0) { return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } @@ -1397,7 +1469,7 @@ namespace DotRecast.Detour { // Find furthest existing node that was visited. DtNode node = null; - for (int i = existing.Count - 1; i >= 0; --i) + for (int i = existingSize - 1; i >= 0; --i) { node = m_nodePool.FindNode(existing[i]); if (node != null) @@ -1423,24 +1495,27 @@ namespace DotRecast.Detour return DtStatus.DT_SUCCESS | details; } - protected DtStatus AppendVertex(RcVec3f pos, int flags, long refs, ref List straightPath, - int maxStraightPath) + protected DtStatus AppendVertex(RcVec3f pos, byte flags, long refs, Span straightPath, ref int straightPathCount, int maxStraightPath) { - if (straightPath.Count > 0 && DtUtils.VEqual(straightPath[straightPath.Count - 1].pos, pos)) + if (straightPathCount > 0 && RcVec.Equal(straightPath[straightPathCount - 1].pos, pos)) { // The vertices are equal, update flags and poly. - straightPath[straightPath.Count - 1] = new DtStraightPath(straightPath[straightPath.Count - 1].pos, flags, refs); + straightPath[straightPathCount - 1] = new DtStraightPath(straightPath[straightPathCount - 1].pos, flags, refs); } else { - if (straightPath.Count < maxStraightPath) + // Append new vertex. + straightPath[straightPathCount] = new DtStraightPath(pos, flags, refs); + straightPathCount++; + + // If there is no space to append more vertices, return. + if (straightPathCount >= maxStraightPath) { - // Append new vertex. - straightPath.Add(new DtStraightPath(pos, flags, refs)); + return DtStatus.DT_SUCCESS | DtStatus.DT_BUFFER_TOO_SMALL; } - // If reached end of path or there is no space to append more vertices, return. - if (flags == DtStraightPathFlags.DT_STRAIGHTPATH_END || straightPath.Count >= maxStraightPath) + // If reached end of path, return. + if (flags == DtStraightPathFlags.DT_STRAIGHTPATH_END) { return DtStatus.DT_SUCCESS; } @@ -1450,9 +1525,9 @@ namespace DotRecast.Detour } protected DtStatus AppendPortals(int startIdx, int endIdx, RcVec3f endPos, List path, - ref List straightPath, int maxStraightPath, int options) + Span straightPath, ref int straightPathCount, int maxStraightPath, int options) { - var startPos = straightPath[straightPath.Count - 1].pos; + var startPos = straightPath[straightPathCount - 1].pos; // Append or update last vertex DtStatus stat; for (int i = startIdx; i < endIdx; i++) @@ -1491,7 +1566,7 @@ namespace DotRecast.Detour if (DtUtils.IntersectSegSeg2D(startPos, endPos, left, right, out var _, out var t)) { var pt = RcVec3f.Lerp(left, right, t); - stat = AppendVertex(pt, 0, path[i + 1], ref straightPath, maxStraightPath); + stat = AppendVertex(pt, 0, path[i + 1], straightPath, ref straightPathCount, maxStraightPath); if (!stat.InProgress()) { return stat; @@ -1528,17 +1603,22 @@ namespace DotRecast.Detour /// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0] /// @param[in] options Query options. (see: #dtStraightPathOptions) /// @returns The status flags for the query. - public virtual DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos, List path, - ref List straightPath, - int maxStraightPath, int options) + public virtual DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos, + List path, int pathSize, + Span straightPath, out int straightPathCount, int maxStraightPath, + int options) { - if (!startPos.IsFinite() || !endPos.IsFinite() || null == straightPath - || null == path || 0 == path.Count || path[0] == 0 || maxStraightPath <= 0) + straightPathCount = 0; + + if (!startPos.IsFinite() || !endPos.IsFinite() || + null == straightPath || + null == path || pathSize <= 0 || path[0] == 0 + || maxStraightPath <= 0) { return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } - straightPath.Clear(); + DtStatus stat = DtStatus.DT_STATUS_NOTHING; // TODO: Should this be callers responsibility? var closestStartPosRes = ClosestPointOnPolyBoundary(path[0], startPos, out var closestStartPos); @@ -1547,20 +1627,20 @@ namespace DotRecast.Detour return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } - var closestEndPosRes = ClosestPointOnPolyBoundary(path[path.Count - 1], endPos, out var closestEndPos); + var closestEndPosRes = ClosestPointOnPolyBoundary(path[pathSize - 1], endPos, out var closestEndPos); if (closestEndPosRes.Failed()) { return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } // Add start point. - DtStatus stat = AppendVertex(closestStartPos, DtStraightPathFlags.DT_STRAIGHTPATH_START, path[0], ref straightPath, maxStraightPath); + stat = AppendVertex(closestStartPos, DtStraightPathFlags.DT_STRAIGHTPATH_START, path[0], straightPath, ref straightPathCount, maxStraightPath); if (!stat.InProgress()) { return stat; } - if (path.Count > 1) + if (pathSize > 1) { RcVec3f portalApex = closestStartPos; RcVec3f portalLeft = portalApex; @@ -1575,13 +1655,13 @@ namespace DotRecast.Detour long leftPolyRef = path[0]; long rightPolyRef = path[0]; - for (int i = 0; i < path.Count; ++i) + for (int i = 0; i < pathSize; ++i) { RcVec3f left; RcVec3f right; int toType; - if (i + 1 < path.Count) + if (i + 1 < pathSize) { int fromType; // // fromType is ignored. @@ -1601,13 +1681,13 @@ namespace DotRecast.Detour if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) { // Ignore status return value as we're just about to return anyway. - AppendPortals(apexIndex, i, closestEndPos, path, ref straightPath, maxStraightPath, options); + AppendPortals(apexIndex, i, closestEndPos, path, straightPath, ref straightPathCount, maxStraightPath, options); } // Ignore status return value as we're just about to return anyway. - AppendVertex(closestEndPos, 0, path[i], ref straightPath, maxStraightPath); + AppendVertex(closestEndPos, 0, path[i], straightPath, ref straightPathCount, maxStraightPath); - return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM | (straightPath.Count >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); + return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); } // If starting really close the portal, advance. @@ -1631,10 +1711,10 @@ namespace DotRecast.Detour // Right vertex. if (DtUtils.TriArea2D(portalApex, portalRight, right) <= 0.0f) { - if (DtUtils.VEqual(portalApex, portalRight) || DtUtils.TriArea2D(portalApex, portalLeft, right) > 0.0f) + if (RcVec.Equal(portalApex, portalRight) || DtUtils.TriArea2D(portalApex, portalLeft, right) > 0.0f) { portalRight = right; - rightPolyRef = (i + 1 < path.Count) ? path[i + 1] : 0; + rightPolyRef = (i + 1 < pathSize) ? path[i + 1] : 0; rightPolyType = toType; rightIndex = i; } @@ -1643,7 +1723,7 @@ namespace DotRecast.Detour // Append portals along the current straight path segment. if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) { - stat = AppendPortals(apexIndex, leftIndex, portalLeft, path, ref straightPath, maxStraightPath, options); + stat = AppendPortals(apexIndex, leftIndex, portalLeft, path, straightPath, ref straightPathCount, maxStraightPath, options); if (!stat.InProgress()) { return stat; @@ -1653,7 +1733,7 @@ namespace DotRecast.Detour portalApex = portalLeft; apexIndex = leftIndex; - int flags = 0; + byte flags = 0; if (leftPolyRef == 0) { flags = DtStraightPathFlags.DT_STRAIGHTPATH_END; @@ -1666,7 +1746,7 @@ namespace DotRecast.Detour long refs = leftPolyRef; // Append or update vertex - stat = AppendVertex(portalApex, flags, refs, ref straightPath, maxStraightPath); + stat = AppendVertex(portalApex, flags, refs, straightPath, ref straightPathCount, maxStraightPath); if (!stat.InProgress()) { return stat; @@ -1687,10 +1767,10 @@ namespace DotRecast.Detour // Left vertex. if (DtUtils.TriArea2D(portalApex, portalLeft, left) >= 0.0f) { - if (DtUtils.VEqual(portalApex, portalLeft) || DtUtils.TriArea2D(portalApex, portalRight, left) < 0.0f) + if (RcVec.Equal(portalApex, portalLeft) || DtUtils.TriArea2D(portalApex, portalRight, left) < 0.0f) { portalLeft = left; - leftPolyRef = (i + 1 < path.Count) ? path[i + 1] : 0; + leftPolyRef = (i + 1 < pathSize) ? path[i + 1] : 0; leftPolyType = toType; leftIndex = i; } @@ -1699,7 +1779,7 @@ namespace DotRecast.Detour // Append portals along the current straight path segment. if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) { - stat = AppendPortals(apexIndex, rightIndex, portalRight, path, ref straightPath, maxStraightPath, options); + stat = AppendPortals(apexIndex, rightIndex, portalRight, path, straightPath, ref straightPathCount, maxStraightPath, options); if (!stat.InProgress()) { return stat; @@ -1709,7 +1789,7 @@ namespace DotRecast.Detour portalApex = portalRight; apexIndex = rightIndex; - int flags = 0; + byte flags = 0; if (rightPolyRef == 0) { flags = DtStraightPathFlags.DT_STRAIGHTPATH_END; @@ -1722,7 +1802,7 @@ namespace DotRecast.Detour long refs = rightPolyRef; // Append or update vertex - stat = AppendVertex(portalApex, flags, refs, ref straightPath, maxStraightPath); + stat = AppendVertex(portalApex, flags, refs, straightPath, ref straightPathCount, maxStraightPath); if (!stat.InProgress()) { return stat; @@ -1744,7 +1824,7 @@ namespace DotRecast.Detour // Append portals along the current straight path segment. if ((options & (DtStraightPathOptions.DT_STRAIGHTPATH_AREA_CROSSINGS | DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS)) != 0) { - stat = AppendPortals(apexIndex, path.Count - 1, closestEndPos, path, ref straightPath, maxStraightPath, options); + stat = AppendPortals(apexIndex, pathSize - 1, closestEndPos, path, straightPath, ref straightPathCount, maxStraightPath, options); if (!stat.InProgress()) { return stat; @@ -1753,8 +1833,8 @@ namespace DotRecast.Detour } // Ignore status return value as we're just about to return anyway. - AppendVertex(closestEndPos, DtStraightPathFlags.DT_STRAIGHTPATH_END, 0, ref straightPath, maxStraightPath); - return DtStatus.DT_SUCCESS | (straightPath.Count >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); + AppendVertex(closestEndPos, DtStraightPathFlags.DT_STRAIGHTPATH_END, 0, straightPath, ref straightPathCount, maxStraightPath); + return DtStatus.DT_SUCCESS | (straightPathCount >= maxStraightPath ? DtStatus.DT_BUFFER_TOO_SMALL : DtStatus.DT_STATUS_NOTHING); } /// @par @@ -1789,14 +1869,11 @@ namespace DotRecast.Detour /// @returns The status flags for the query. public DtStatus MoveAlongSurface(long startRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, - out RcVec3f resultPos, ref List visited) + out RcVec3f resultPos, Span visited, out int visitedCount, int maxVisitedSize) { resultPos = RcVec3f.Zero; - if (null != visited) - { - visited.Clear(); - } + visitedCount = 0; // Validate input if (!m_nav.IsValidPolyRef(startRef) || !startPos.IsFinite() @@ -1805,6 +1882,8 @@ namespace DotRecast.Detour return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } + DtStatus status = DtStatus.DT_SUCCESS; + m_tinyNodePool.Clear(); DtNode startNode = m_tinyNodePool.GetNode(startRef); @@ -1825,7 +1904,7 @@ namespace DotRecast.Detour var searchPos = RcVec3f.Lerp(startPos, endPos, 0.5f); float searchRadSqr = RcMath.Sqr(RcVec3f.Distance(startPos, endPos) / 2.0f + 0.001f); - using var verts = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly() * 3); + Span verts = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3]; const int MAX_NEIS = 8; Span neis = stackalloc long[MAX_NEIS]; @@ -1845,11 +1924,11 @@ namespace DotRecast.Detour int nverts = curPoly.vertCount; for (int i = 0; i < nverts; ++i) { - RcArrays.Copy(curTile.data.verts, curPoly.verts[i] * 3, verts.AsArray(), i * 3, 3); + RcSpans.Copy(curTile.data.verts, curPoly.verts[i] * 3, verts, i * 3, 3); } // If target is inside the poly, stop search. - if (DtUtils.PointInPolygon(endPos, verts.AsArray(), nverts)) + if (DtUtils.PointInPolygon(endPos, verts, nverts)) { bestNode = curNode; bestPos = endPos; @@ -1862,10 +1941,10 @@ namespace DotRecast.Detour // Find links to neighbours. int nneis = 0; - if ((curPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) + if ((curPoly.neis[j] & DT_EXT_LINK) != 0) { // Tile border. - for (int k = curTile.polyLinks[curPoly.index]; k != DtNavMesh.DT_NULL_LINK; k = curTile.links[k].next) + for (int k = curPoly.firstLink; k != DT_NULL_LINK; k = curTile.links[k].next) { DtLink link = curTile.links[k]; if (link.edge == j) @@ -1900,11 +1979,11 @@ namespace DotRecast.Detour // Wall edge, calc distance. int vj = j * 3; int vi = i * 3; - var distSqr = DtUtils.DistancePtSegSqr2D(endPos, verts.AsArray(), vj, vi, out var tseg); + var distSqr = DtUtils.DistancePtSegSqr2D(endPos, verts, vj, vi, out var tseg); if (distSqr < bestDist) { // Update nearest distance. - bestPos = RcVecUtils.Lerp(verts.AsArray(), vj, vi, tseg); + bestPos = RcVec.Lerp(verts, vj, vi, tseg); bestDist = distSqr; bestNode = curNode; } @@ -1924,7 +2003,7 @@ namespace DotRecast.Detour // TODO: Maybe should use GetPortalPoints(), but this one is way faster. int vj = j * 3; int vi = i * 3; - var distSqr = DtUtils.DistancePtSegSqr2D(searchPos, verts.AsArray(), vj, vi, out var _); + var distSqr = DtUtils.DistancePtSegSqr2D(searchPos, verts, vj, vi, out var _); if (distSqr > searchRadSqr) { continue; @@ -1939,6 +2018,7 @@ namespace DotRecast.Detour } } + int n = 0; if (bestNode != null) { // Reverse the path. @@ -1956,14 +2036,22 @@ namespace DotRecast.Detour node = prev; do { - visited.Add(node.id); + visited[n++] = node.id; + if (n >= maxVisitedSize) + { + status |= DtStatus.DT_BUFFER_TOO_SMALL; + ; + break; + } + node = m_tinyNodePool.GetNodeAtIdx(node.pidx); } while (node != null); } resultPos = bestPos; + visitedCount = n; - return DtStatus.DT_SUCCESS; + return status; } protected DtStatus GetPortalPoints(long from, long to, out RcVec3f left, out RcVec3f right, out int fromType, out int toType) @@ -2002,7 +2090,7 @@ namespace DotRecast.Detour // Find the link that points to the 'to' polygon. DtLink link = null; - for (int i = fromTile.polyLinks[fromPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = fromTile.links[i].next) + for (int i = fromPoly.firstLink; i != DT_NULL_LINK; i = fromTile.links[i].next) { if (fromTile.links[i].refs == to) { @@ -2020,7 +2108,7 @@ namespace DotRecast.Detour if (fromPoly.GetPolyType() == DtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) { // Find link that points to first vertex. - for (int i = fromTile.polyLinks[fromPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = fromTile.links[i].next) + for (int i = fromPoly.firstLink; i != DT_NULL_LINK; i = fromTile.links[i].next) { if (fromTile.links[i].refs == to) { @@ -2042,7 +2130,7 @@ namespace DotRecast.Detour if (toPoly.GetPolyType() == DtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION) { - for (int i = toTile.polyLinks[toPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = toTile.links[i].next) + for (int i = toPoly.firstLink; i != DT_NULL_LINK; i = toTile.links[i].next) { if (toTile.links[i].refs == from) { @@ -2083,8 +2171,8 @@ namespace DotRecast.Detour float s = 1.0f / 255.0f; float tmin = link.bmin * s; float tmax = link.bmax * s; - left = RcVecUtils.Lerp(fromTile.data.verts, v0 * 3, v1 * 3, tmin); - right = RcVecUtils.Lerp(fromTile.data.verts, v0 * 3, v1 * 3, tmax); + left = RcVec.Lerp(fromTile.data.verts, v0 * 3, v1 * 3, tmin); + right = RcVec.Lerp(fromTile.data.verts, v0 * 3, v1 * 3, tmax); } } @@ -2185,16 +2273,16 @@ namespace DotRecast.Detour /// /// This method is meant to be used for quick, short distance checks. /// - /// If the path array is too small to hold the result, it will be filled as + /// If the path array is too small to hold the result, it will be filled as /// far as possible from the start postion toward the end position. /// /// Using the Hit Parameter t of RaycastHit - /// - /// If the hit parameter is a very high value (FLT_MAX), then the ray has hit - /// the end position. In this case the path represents a valid corridor to the + /// + /// If the hit parameter is a very high value (FLT_MAX), then the ray has hit + /// the end position. In this case the path represents a valid corridor to the /// end position and the value of @p hitNormal is undefined. /// - /// If the hit parameter is zero, then the start position is on the wall that + /// If the hit parameter is zero, then the start position is on the wall that /// was hit and the value of @p hitNormal is undefined. /// /// If 0 < t < 1.0 then the following applies: @@ -2206,32 +2294,29 @@ namespace DotRecast.Detour /// /// Use Case Restriction /// - /// The raycast ignores the y-value of the end position. (2D check.) This + /// The raycast ignores the y-value of the end position. (2D check.) This /// places significant limits on how it can be used. For example: /// - /// Consider a scene where there is a main floor with a second floor balcony - /// that hangs over the main floor. So the first floor mesh extends below the - /// balcony mesh. The start position is somewhere on the first floor. The end + /// Consider a scene where there is a main floor with a second floor balcony + /// that hangs over the main floor. So the first floor mesh extends below the + /// balcony mesh. The start position is somewhere on the first floor. The end /// position is on the balcony. /// - /// The raycast will search toward the end position along the first floor mesh. + /// The raycast will search toward the end position along the first floor mesh. /// If it reaches the end position's xz-coordinates it will indicate FLT_MAX /// (no wall hit), meaning it reached the end position. This is one example of why /// this method is meant for short distance checks. /// - /// Casts a 'walkability' ray along the surface of the navigation mesh from + /// Casts a 'walkability' ray along the surface of the navigation mesh from /// the start position toward the end position. - /// @note A wrapper around Raycast(..., RaycastHit*). Retained for backward compatibility. - /// @param[in] startRef The reference id of the start polygon. - /// @param[in] startPos A position within the start polygon representing - /// the start of the ray. [(x, y, z)] - /// @param[in] endPos The position to cast the ray toward. [(x, y, z)] - /// @param[out] t The hit parameter. (FLT_MAX if no wall hit.) - /// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)] - /// @param[in] filter The polygon filter to apply to the query. - /// @param[out] path The reference ids of the visited polygons. [opt] - /// @param[out] pathCount The number of visited polygons. [opt] - /// @param[in] maxPath The maximum number of polygons the @p path array can hold. + /// @param[in] startRef The reference id of the start polygon. + /// @param[in] startPos A position within the start polygon representing + /// the start of the ray. [(x, y, z)] + /// @param[in] endPos The position to cast the ray toward. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] options govern how the raycast behaves. See dtRaycastOptions + /// @param[out] hit Pointer to a raycast hit structure which will be filled by the results. + /// @param[in] prevRef parent of start ref. Used during for cost calculation [opt] /// @returns The status flags for the query. public DtStatus Raycast(long startRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, int options, @@ -2248,7 +2333,7 @@ namespace DotRecast.Detour hit.path.Clear(); hit.pathCost = 0; - using var verts = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly() + 1); + Span verts = stackalloc RcVec3f[m_nav.GetMaxVertsPerPoly() + 1]; RcVec3f curPos = RcVec3f.Zero; RcVec3f lastPos = RcVec3f.Zero; @@ -2278,11 +2363,11 @@ namespace DotRecast.Detour int nv = 0; for (int i = 0; i < poly.vertCount; ++i) { - verts[nv] = RcVecUtils.Create(tile.data.verts, poly.verts[i] * 3); + verts[nv] = RcVec.Create(tile.data.verts, poly.verts[i] * 3); nv++; } - bool intersects = DtUtils.IntersectSegmentPoly2D(startPos, endPos, verts.AsArray(), nv, out var tmin, out var tmax, out var segMin, out var segMax); + bool intersects = DtUtils.IntersectSegmentPoly2D(startPos, endPos, verts, nv, out var tmin, out var tmax, out var segMin, out var segMax); if (!intersects) { // Could not hit the polygon, keep the old t and report hit. @@ -2318,7 +2403,7 @@ namespace DotRecast.Detour // Follow neighbours. long nextRef = 0; - for (int i = tile.polyLinks[poly.index]; i != DtNavMesh.DT_NULL_LINK; i = tile.links[i].next) + for (int i = poly.firstLink; i != DT_NULL_LINK; i = tile.links[i].next) { DtLink link = tile.links[i]; @@ -2416,7 +2501,7 @@ namespace DotRecast.Detour // compute the intersection point at the furthest end of the polygon // and correct the height (since the raycast moves in 2d) lastPos = curPos; - curPos = RcVecUtils.Mad(startPos, dir, hit.t); + curPos = RcVec.Mad(startPos, dir, hit.t); var e1 = verts[segMax]; var e2 = verts[(segMax + 1) % nv]; var eDir = RcVec3f.Subtract(e2, e1); @@ -2438,7 +2523,7 @@ namespace DotRecast.Detour // int va = a * 3; // int vb = b * 3; float dx = verts[b].X - verts[a].X; - float dz = verts[b].Z - verts[a].Z; + float dz = verts[b].Z - verts[a].X; hit.hitNormal = RcVec3f.Normalize(new RcVec3f(dz, 0, -dx)); return DtStatus.DT_SUCCESS; } @@ -2559,7 +2644,7 @@ namespace DotRecast.Detour resultParent.Add(parentRef); resultCost.Add(bestNode.total); - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { DtLink link = bestTile.links[i]; long neighbourRef = link.refs; @@ -2736,7 +2821,7 @@ namespace DotRecast.Detour resultParent.Add(parentRef); resultCost.Add(bestNode.total); - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { DtLink link = bestTile.links[i]; long neighbourRef = link.refs; @@ -2877,8 +2962,8 @@ namespace DotRecast.Detour float radiusSqr = RcMath.Sqr(radius); - using var pa = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly() * 3); - using var pb = RcRentedArray.Rent(m_nav.GetMaxVertsPerPoly() * 3); + Span pa = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3]; + Span pb = stackalloc float[m_nav.GetMaxVertsPerPoly() * 3]; while (0 < stack.Count) { @@ -2891,7 +2976,7 @@ namespace DotRecast.Detour long curRef = curNode.id; m_nav.GetTileAndPolyByRefUnsafe(curRef, out var curTile, out var curPoly); - for (int i = curTile.polyLinks[curPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = curTile.links[i].next) + for (int i = curPoly.firstLink; i != DT_NULL_LINK; i = curTile.links[i].next) { DtLink link = curTile.links[i]; long neighbourRef = link.refs; @@ -2949,7 +3034,7 @@ namespace DotRecast.Detour int npa = neighbourPoly.vertCount; for (int k = 0; k < npa; ++k) { - RcArrays.Copy(neighbourTile.data.verts, neighbourPoly.verts[k] * 3, pa.AsArray(), k * 3, 3); + RcSpans.Copy(neighbourTile.data.verts, neighbourPoly.verts[k] * 3, pa, k * 3, 3); } bool overlap = false; @@ -2959,7 +3044,7 @@ namespace DotRecast.Detour // Connected polys do not overlap. bool connected = false; - for (int k = curTile.polyLinks[curPoly.index]; k != DtNavMesh.DT_NULL_LINK; k = curTile.links[k].next) + for (int k = curPoly.firstLink; k != DT_NULL_LINK; k = curTile.links[k].next) { if (curTile.links[k].refs == pastRef) { @@ -2980,10 +3065,10 @@ namespace DotRecast.Detour int npb = pastPoly.vertCount; for (int k = 0; k < npb; ++k) { - RcArrays.Copy(pastTile.data.verts, pastPoly.verts[k] * 3, pb.AsArray(), k * 3, 3); + RcSpans.Copy(pastTile.data.verts, pastPoly.verts[k] * 3, pb, k * 3, 3); } - if (DtUtils.OverlapPolyPoly2D(pa.AsArray(), npa, pb.AsArray(), npb)) + if (DtUtils.OverlapPolyPoly2D(pa, npa, pb, npb)) { overlap = true; break; @@ -3005,11 +3090,14 @@ namespace DotRecast.Detour } - protected void InsertInterval(List ints, int tmin, int tmax, long refs) + protected void InsertInterval(Span ints, ref int nints, int maxInts, int tmin, int tmax, long refs) { + if (nints + 1 > maxInts) + return; + // Find insertion point. int idx = 0; - while (idx < ints.Count) + while (idx < nints) { if (tmax <= ints[idx].tmin) { @@ -3019,37 +3107,44 @@ namespace DotRecast.Detour idx++; } + // Move current results. + if (0 != nints - idx) + { + RcSpans.Move(ints, idx, idx + 1, nints - idx); + } + // Store - ints.Insert(idx, new DtSegInterval(refs, tmin, tmax)); + ints[idx] = new DtSegInterval(refs, tmin, tmax); + nints++; } /// @par /// - /// If the @p segmentRefs parameter is provided, then all polygon segments will be returned. + /// If the @p segmentRefs parameter is provided, then all polygon segments will be returned. /// Otherwise only the wall segments are returned. - /// - /// A segment that is normally a portal will be included in the result set as a + /// + /// A segment that is normally a portal will be included in the result set as a /// wall if the @p filter results in the neighbor polygon becoomming impassable. - /// - /// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the + /// + /// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the /// maximum segments per polygon of the source navigation mesh. - /// + /// /// Returns the segments for the specified polygon, optionally including portals. - /// @param[in] ref The reference id of the polygon. - /// @param[in] filter The polygon filter to apply to the query. - /// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount] - /// @param[out] segmentRefs The reference ids of each segment's neighbor polygon. - /// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount] - /// @param[out] segmentCount The number of segments returned. - /// @param[in] maxSegments The maximum number of segments the result arrays can hold. + /// @param[in] ref The reference id of the polygon. + /// @param[in] filter The polygon filter to apply to the query. + /// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount] + /// @param[out] segmentRefs The reference ids of each segment's neighbor polygon. + /// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount] + /// @param[out] segmentCount The number of segments returned. + /// @param[in] maxSegments The maximum number of segments the result arrays can hold. /// @returns The status flags for the query. - public DtStatus GetPolyWallSegments(long refs, bool storePortals, IDtQueryFilter filter, - ref List segmentVerts, ref List segmentRefs) + public DtStatus GetPolyWallSegments(long refs, IDtQueryFilter filter, + Span segmentVerts, Span segmentRefs, ref int segmentCount, + int maxSegments) { - segmentVerts.Clear(); - segmentRefs.Clear(); + segmentCount = 0; - var status = m_nav.GetTileAndPolyByRef(refs, out var tile, out var poly); + DtStatus status = m_nav.GetTileAndPolyByRef(refs, out var tile, out var poly); if (status.Failed()) { return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; @@ -3060,15 +3155,23 @@ namespace DotRecast.Detour return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM; } - List ints = new List(16); + int n = 0; + const int MAX_INTERVAL = 16; + Span ints = stackalloc DtSegInterval[MAX_INTERVAL]; + int nints; + + bool storePortals = segmentRefs != null; + + status = DtStatus.DT_SUCCESS; + for (int i = 0, j = poly.vertCount - 1; i < poly.vertCount; j = i++) { // Skip non-solid edges. - ints.Clear(); - if ((poly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) + nints = 0; + if ((poly.neis[j] & DT_EXT_LINK) != 0) { // Tile border. - for (int k = tile.polyLinks[poly.index]; k != DtNavMesh.DT_NULL_LINK; k = tile.links[k].next) + for (int k = poly.firstLink; k != DT_NULL_LINK; k = tile.links[k].next) { DtLink link = tile.links[k]; if (link.edge == j) @@ -3078,7 +3181,7 @@ namespace DotRecast.Detour m_nav.GetTileAndPolyByRefUnsafe(link.refs, out var neiTile, out var neiPoly); if (filter.PassFilter(link.refs, neiTile, neiPoly)) { - InsertInterval(ints, link.bmin, link.bmax, link.refs); + InsertInterval(ints, ref nints, MAX_INTERVAL, link.bmin, link.bmax, link.refs); } } } @@ -3104,37 +3207,62 @@ namespace DotRecast.Detour continue; } - int ivj = poly.verts[j] * 3; - int ivi = poly.verts[i] * 3; - var seg = new RcSegmentVert(); - seg.vmin = RcVecUtils.Create(tile.data.verts, ivj); - seg.vmax = RcVecUtils.Create(tile.data.verts, ivi); - // RcArrays.Copy(tile.data.verts, ivj, seg, 0, 3); - // RcArrays.Copy(tile.data.verts, ivi, seg, 3, 3); - segmentVerts.Add(seg); - segmentRefs.Add(neiRef); + if (n < maxSegments) + { + int ivj = poly.verts[j] * 3; + int ivi = poly.verts[i] * 3; + var seg = new RcSegmentVert(); + seg.vmin = RcVec.Create(tile.data.verts, ivj); + seg.vmax = RcVec.Create(tile.data.verts, ivi); + segmentVerts[n] = seg; + if (null != segmentRefs) + { + segmentRefs[n] = neiRef; + } + + n++; + } + else + { + status |= DtStatus.DT_BUFFER_TOO_SMALL; + } + continue; } // Add sentinels - InsertInterval(ints, -1, 0, 0); - InsertInterval(ints, 255, 256, 0); + InsertInterval(ints, ref nints, MAX_INTERVAL, -1, 0, 0); + InsertInterval(ints, ref nints, MAX_INTERVAL, 255, 256, 0); // Store segments. int vj = poly.verts[j] * 3; int vi = poly.verts[i] * 3; - for (int k = 1; k < ints.Count; ++k) + for (int k = 1; k < nints; ++k) { // Portal segment. if (storePortals && ints[k].refs != 0) { float tmin = ints[k].tmin / 255.0f; float tmax = ints[k].tmax / 255.0f; - var seg = new RcSegmentVert(); - seg.vmin = RcVecUtils.Lerp(tile.data.verts, vj, vi, tmin); - seg.vmax = RcVecUtils.Lerp(tile.data.verts, vj, vi, tmax); - segmentVerts.Add(seg); - segmentRefs.Add(ints[k].refs); + + if (n < maxSegments) + { + var seg = new RcSegmentVert(); + seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin); + seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax); + segmentVerts[n] = seg; + + if (null != segmentRefs) + { + segmentRefs[n] = ints[k].refs; + } + + n++; + } + else + { + status |= DtStatus.DT_BUFFER_TOO_SMALL; + } } // Wall segment. @@ -3144,16 +3272,32 @@ namespace DotRecast.Detour { float tmin = imin / 255.0f; float tmax = imax / 255.0f; - var seg = new RcSegmentVert(); - seg.vmin = RcVecUtils.Lerp(tile.data.verts, vj, vi, tmin); - seg.vmax = RcVecUtils.Lerp(tile.data.verts, vj, vi, tmax); - segmentVerts.Add(seg); - segmentRefs.Add(0L); + + if (n < maxSegments) + { + var seg = new RcSegmentVert(); + seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin); + seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax); + segmentVerts[n] = seg; + + if (null != segmentRefs) + { + segmentRefs[n] = 0; + } + + n++; + } + else + { + status |= DtStatus.DT_BUFFER_TOO_SMALL; + } } } } - return DtStatus.DT_SUCCESS; + segmentCount = n; + + return status; } /// @par @@ -3232,11 +3376,11 @@ namespace DotRecast.Detour for (int i = 0, j = bestPoly.vertCount - 1; i < bestPoly.vertCount; j = i++) { // Skip non-solid edges. - if ((bestPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) + if ((bestPoly.neis[j] & DT_EXT_LINK) != 0) { // Tile border. bool solid = true; - for (int k = bestTile.polyLinks[bestPoly.index]; k != DtNavMesh.DT_NULL_LINK; k = bestTile.links[k].next) + for (int k = bestPoly.firstLink; k != DT_NULL_LINK; k = bestTile.links[k].next) { DtLink link = bestTile.links[k]; if (link.edge == j) @@ -3288,11 +3432,11 @@ namespace DotRecast.Detour hitPos.Y = bestTile.data.verts[vj + 1] + (bestTile.data.verts[vi + 1] - bestTile.data.verts[vj + 1]) * tseg; hitPos.Z = bestTile.data.verts[vj + 2] + (bestTile.data.verts[vi + 2] - bestTile.data.verts[vj + 2]) * tseg; hasBestV = true; - bestvj = RcVecUtils.Create(bestTile.data.verts, vj); - bestvi = RcVecUtils.Create(bestTile.data.verts, vi); + bestvj = RcVec.Create(bestTile.data.verts, vj); + bestvi = RcVec.Create(bestTile.data.verts, vi); } - for (int i = bestTile.polyLinks[bestPoly.index]; i != DtNavMesh.DT_NULL_LINK; i = bestTile.links[i].next) + for (int i = bestPoly.firstLink; i != DT_NULL_LINK; i = bestTile.links[i].next) { DtLink link = bestTile.links[i]; long neighbourRef = link.refs; diff --git a/src/DotRecast.Detour/DtNavMeshQueryMock.cs b/src/DotRecast.Detour/DtNavMeshQueryMock.cs new file mode 100644 index 0000000..3d9ec99 --- /dev/null +++ b/src/DotRecast.Detour/DtNavMeshQueryMock.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using DotRecast.Core.Numerics; + +namespace DotRecast.Detour +{ + public class DtNavMeshQueryMock : DtNavMeshQuery + { + private readonly DtStraightPath[] _straightPath; + private readonly DtStatus _status; + + public DtNavMeshQueryMock(DtStraightPath[] straightPath, DtStatus status) + : base(null) + { + _straightPath = straightPath; + _status = status; + } + + public override DtStatus FindStraightPath(RcVec3f startPos, RcVec3f endPos, + List path, int pathSize, + Span straightPath, out int straightPathCount, int maxStraightPath, + int options) + { + straightPathCount = 0; + for (int i = 0; i < _straightPath.Length && i < maxStraightPath; ++i) + { + straightPath[i] = _straightPath[i]; + straightPathCount += 1; + } + + return _status; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Detour/DtNavMeshRaycast.cs b/src/DotRecast.Detour/DtNavMeshRaycast.cs index ac1aaca..e0d60b1 100644 --- a/src/DotRecast.Detour/DtNavMeshRaycast.cs +++ b/src/DotRecast.Detour/DtNavMeshRaycast.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -17,6 +17,7 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; using DotRecast.Core; using DotRecast.Core.Numerics; @@ -48,6 +49,7 @@ namespace DotRecast.Detour private static bool Raycast(DtMeshTile tile, RcVec3f sp, RcVec3f sq, out float hitTime) { hitTime = 0.0f; + Span tempVerts = stackalloc RcVec3f[3]; for (int i = 0; i < tile.data.header.polyCount; ++i) { DtPoly p = tile.data.polys[i]; @@ -58,7 +60,7 @@ namespace DotRecast.Detour ref DtPolyDetail pd = ref tile.data.detailMeshes[i]; - RcVec3f[] verts = new RcVec3f[3]; + Span verts = tempVerts; for (int j = 0; j < pd.triCount; ++j) { int t = (pd.triBase + j) * 4; diff --git a/src/DotRecast.Detour/DtNoOpDtPolygonByCircleConstraint.cs b/src/DotRecast.Detour/DtNoOpDtPolygonByCircleConstraint.cs index 01cc576..e5d2651 100644 --- a/src/DotRecast.Detour/DtNoOpDtPolygonByCircleConstraint.cs +++ b/src/DotRecast.Detour/DtNoOpDtPolygonByCircleConstraint.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour { diff --git a/src/DotRecast.Detour/DtNode.cs b/src/DotRecast.Detour/DtNode.cs index c874c38..258be8c 100644 --- a/src/DotRecast.Detour/DtNode.cs +++ b/src/DotRecast.Detour/DtNode.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtNodeFlags.cs b/src/DotRecast.Detour/DtNodeFlags.cs index d204ad4..d383b8e 100644 --- a/src/DotRecast.Detour/DtNodeFlags.cs +++ b/src/DotRecast.Detour/DtNodeFlags.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public static class DtNodeFlags { diff --git a/src/DotRecast.Detour/DtNodePool.cs b/src/DotRecast.Detour/DtNodePool.cs index bc6e99e..08ecf8f 100644 --- a/src/DotRecast.Detour/DtNodePool.cs +++ b/src/DotRecast.Detour/DtNodePool.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtNodeQueue.cs b/src/DotRecast.Detour/DtNodeQueue.cs index fd44bfe..a265ad9 100644 --- a/src/DotRecast.Detour/DtNodeQueue.cs +++ b/src/DotRecast.Detour/DtNodeQueue.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtOffMeshConnection.cs b/src/DotRecast.Detour/DtOffMeshConnection.cs index 5d6cf63..6c0f1c2 100644 --- a/src/DotRecast.Detour/DtOffMeshConnection.cs +++ b/src/DotRecast.Detour/DtOffMeshConnection.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtPathUtils.cs b/src/DotRecast.Detour/DtPathUtils.cs index 7534f29..24dd8a6 100644 --- a/src/DotRecast.Detour/DtPathUtils.cs +++ b/src/DotRecast.Detour/DtPathUtils.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,22 +24,24 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour { + using static DtDetour; + public static class DtPathUtils { - private const int MAX_STEER_POINTS = 3; - public static bool GetSteerTarget(DtNavMeshQuery navQuery, RcVec3f startPos, RcVec3f endPos, float minTargetDist, - List path, + List path, int pathSize, out RcVec3f steerPos, out int steerPosFlag, out long steerPosRef) { + const int MAX_STEER_POINTS = 3; + steerPos = RcVec3f.Zero; steerPosFlag = 0; steerPosRef = 0; // Find steer target. - var straightPath = new List(MAX_STEER_POINTS); - var result = navQuery.FindStraightPath(startPos, endPos, path, ref straightPath, MAX_STEER_POINTS, 0); + Span straightPath = stackalloc DtStraightPath[MAX_STEER_POINTS]; + var result = navQuery.FindStraightPath(startPos, endPos, path, pathSize, straightPath, out var nsteerPath, MAX_STEER_POINTS, 0); if (result.Failed()) { return false; @@ -47,7 +49,7 @@ namespace DotRecast.Detour // Find vertex far enough to steer to. int ns = 0; - while (ns < straightPath.Count) + while (ns < nsteerPath) { // Stop at Off-Mesh link or when point is further than slop away. if (((straightPath[ns].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) @@ -57,7 +59,7 @@ namespace DotRecast.Detour } // Failed to find good point to steer to. - if (ns >= straightPath.Count) + if (ns >= nsteerPath) return false; steerPos = straightPath[ns].pos; @@ -88,29 +90,32 @@ namespace DotRecast.Detour // +-S-+-T-+ // |:::| | <-- the step can end up in here, resulting U-turn path. // +---+---+ - public static List FixupShortcuts(List path, DtNavMeshQuery navQuery) + public static int FixupShortcuts(ref List path, int npath, DtNavMeshQuery navQuery) { - if (path.Count < 3) + if (npath < 3) { - return path; + return npath; } // Get connected polygons - List neis = new List(); + const int maxNeis = 16; + Span neis = stackalloc long[maxNeis]; + int nneis = 0; var status = navQuery.GetAttachedNavMesh().GetTileAndPolyByRef(path[0], out var tile, out var poly); if (status.Failed()) { - return path; + return npath; } - for (int k = tile.polyLinks[poly.index]; k != DtNavMesh.DT_NULL_LINK; k = tile.links[k].next) + for (int k = poly.firstLink; k != DT_NULL_LINK; k = tile.links[k].next) { DtLink link = tile.links[k]; if (link.refs != 0) { - neis.Add(link.refs); + if (nneis < maxNeis) + neis[nneis++] = link.refs; } } @@ -118,9 +123,9 @@ namespace DotRecast.Detour // in the path, short cut to that polygon directly. const int maxLookAhead = 6; int cut = 0; - for (int i = Math.Min(maxLookAhead, path.Count) - 1; i > 1 && cut == 0; i--) + for (int i = Math.Min(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) { - for (int j = 0; j < neis.Count; j++) + for (int j = 0; j < nneis; j++) { if (path[i] == neis[j]) { @@ -134,23 +139,25 @@ namespace DotRecast.Detour { List shortcut = new List(); shortcut.Add(path[0]); - shortcut.AddRange(path.GetRange(cut, path.Count - cut)); - return shortcut; + shortcut.AddRange(path.GetRange(cut, npath - cut)); + + path = shortcut; + return shortcut.Count; } - return path; + return npath; } - public static List MergeCorridorStartMoved(List path, int npath, int maxPath, List visited) + public static int MergeCorridorStartMoved(ref List path, int npath, int maxPath, Span visited, int nvisited) { int furthestPath = -1; int furthestVisited = -1; // Find furthest common polygon. - for (int i = path.Count - 1; i >= 0; --i) + for (int i = npath - 1; i >= 0; --i) { bool found = false; - for (int j = visited.Count - 1; j >= 0; --j) + for (int j = nvisited - 1; j >= 0; --j) { if (path[i] == visited[j]) { @@ -169,7 +176,7 @@ namespace DotRecast.Detour // If no intersection found just return current path. if (furthestPath == -1 || furthestVisited == -1) { - return path; + return npath; } // Concatenate paths. @@ -177,25 +184,27 @@ namespace DotRecast.Detour // Adjust beginning of the buffer to include the visited. List result = new List(); // Store visited - for (int i = visited.Count - 1; i > furthestVisited; --i) + for (int i = nvisited - 1; i > furthestVisited; --i) { result.Add(visited[i]); } - result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath)); - return result; + result.AddRange(path.GetRange(furthestPath, npath - furthestPath)); + + path = result; + return result.Count; } - public static List MergeCorridorEndMoved(List path, int npath, int maxPath, List visited) + public static int MergeCorridorEndMoved(ref List path, int npath, int maxPath, Span visited, int nvisited) { int furthestPath = -1; int furthestVisited = -1; // Find furthest common polygon. - for (int i = 0; i < path.Count; ++i) + for (int i = 0; i < npath; ++i) { bool found = false; - for (int j = visited.Count - 1; j >= 0; --j) + for (int j = nvisited - 1; j >= 0; --j) { if (path[i] == visited[j]) { @@ -214,25 +223,30 @@ namespace DotRecast.Detour // If no intersection found just return current path. if (furthestPath == -1 || furthestVisited == -1) { - return path; + return npath; } // Concatenate paths. List result = path.GetRange(0, furthestPath); - result.AddRange(visited.GetRange(furthestVisited, visited.Count - furthestVisited)); - return result; + foreach (var v in visited.Slice(furthestVisited, nvisited - furthestVisited)) + { + result.Add(v); + } + + path = result; + return result.Count; } - public static List MergeCorridorStartShortcut(List path, int npath, int maxPath, List visited) + public static int MergeCorridorStartShortcut(ref List path, int npath, int maxPath, List visited, int nvisited) { int furthestPath = -1; int furthestVisited = -1; // Find furthest common polygon. - for (int i = path.Count - 1; i >= 0; --i) + for (int i = npath - 1; i >= 0; --i) { bool found = false; - for (int j = visited.Count - 1; j >= 0; --j) + for (int j = nvisited - 1; j >= 0; --j) { if (path[i] == visited[j]) { @@ -251,15 +265,17 @@ namespace DotRecast.Detour // If no intersection found just return current path. if (furthestPath == -1 || furthestVisited <= 0) { - return path; + return npath; } // Concatenate paths. // Adjust beginning of the buffer to include the visited. List result = visited.GetRange(0, furthestVisited); - result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath)); - return result; + result.AddRange(path.GetRange(furthestPath, npath - furthestPath)); + + path = result; + return result.Count; } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtPoly.cs b/src/DotRecast.Detour/DtPoly.cs index 37f0c6a..62c2aec 100644 --- a/src/DotRecast.Detour/DtPoly.cs +++ b/src/DotRecast.Detour/DtPoly.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,26 +18,28 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using System; - namespace DotRecast.Detour { - /** Defines a polygon within a MeshTile object. */ - [Serializable] + /// Defines a polygon within a dtMeshTile object. + /// @ingroup detour public class DtPoly { - public int index; + public readonly int index; + + /// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.) + public int firstLink; - /** The indices of the polygon's vertices. The actual vertices are located in MeshTile::verts. */ - public int[] verts; + /// The indices of the polygon's vertices. + /// The actual vertices are located in dtMeshTile::verts. + public readonly int[] verts; - /** Packed data representing neighbor polygons references and flags for each edge. */ - public int[] neis; + /// Packed data representing neighbor polygons references and flags for each edge. + public readonly int[] neis; - /** The user defined polygon flags. */ + /// The user defined polygon flags. public int flags; - /** The number of vertices in the polygon. */ + /// The number of vertices in the polygon. public int vertCount; /// The bit packed area id and polygon type. @@ -51,25 +53,25 @@ namespace DotRecast.Detour neis = new int[maxVertsPerPoly]; } - /** Sets the user defined area id. [Limit: < {@link org.recast4j.detour.NavMesh#DT_MAX_AREAS}] */ + /// Sets the user defined area id. [Limit: < #DT_MAX_AREAS] public void SetArea(int a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); } - /** Sets the polygon type. (See: #dtPolyTypes.) */ + /// Sets the polygon type. (See: #dtPolyTypes.) public void SetPolyType(int t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); } - /** Gets the user defined area id. */ + /// Gets the user defined area id. public int GetArea() { return areaAndtype & 0x3f; } - /** Gets the polygon type. (See: #dtPolyTypes) */ + /// Gets the polygon type. (See: #dtPolyTypes) public int GetPolyType() { return areaAndtype >> 6; diff --git a/src/DotRecast.Detour/DtPolyDetail.cs b/src/DotRecast.Detour/DtPolyDetail.cs index 365eb31..5bc02f9 100644 --- a/src/DotRecast.Detour/DtPolyDetail.cs +++ b/src/DotRecast.Detour/DtPolyDetail.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -20,22 +20,15 @@ freely, subject to the following restrictions: namespace DotRecast.Detour { - /** Defines the location of detail sub-mesh data within a dtMeshTile. */ - public struct DtPolyDetail + /// Defines the location of detail sub-mesh data within a dtMeshTile. + public readonly struct DtPolyDetail { - /** The offset of the vertices in the MeshTile::detailVerts array. */ - public int vertBase; + public readonly int vertBase; //< The offset of the vertices in the dtMeshTile::detailVerts array. + public readonly int triBase; //< The offset of the triangles in the dtMeshTile::detailTris array. + public readonly byte vertCount; //< The number of vertices in the sub-mesh. + public readonly byte triCount; //< The number of triangles in the sub-mesh. - /** The offset of the triangles in the MeshTile::detailTris array. */ - public int triBase; - - /** The number of vertices in the sub-mesh. */ - public int vertCount; - - /** The number of triangles in the sub-mesh. */ - public int triCount; - - public DtPolyDetail(int vertBase, int triBase, int vertCount, int triCount) + public DtPolyDetail(int vertBase, int triBase, byte vertCount, byte triCount) { this.vertBase = vertBase; this.triBase = triBase; diff --git a/src/DotRecast.Detour/DtPolyPoint.cs b/src/DotRecast.Detour/DtPolyPoint.cs index 0ca78c0..fb46073 100644 --- a/src/DotRecast.Detour/DtPolyPoint.cs +++ b/src/DotRecast.Detour/DtPolyPoint.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour { diff --git a/src/DotRecast.Detour/DtPolyTypes.cs b/src/DotRecast.Detour/DtPolyTypes.cs index 2d26de8..5af6048 100644 --- a/src/DotRecast.Detour/DtPolyTypes.cs +++ b/src/DotRecast.Detour/DtPolyTypes.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { /// Flags representing the type of a navigation mesh polygon. public static class DtPolyTypes diff --git a/src/DotRecast.Detour/DtQueryData.cs b/src/DotRecast.Detour/DtQueryData.cs index 6d1e455..8997614 100644 --- a/src/DotRecast.Detour/DtQueryData.cs +++ b/src/DotRecast.Detour/DtQueryData.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtQueryDefaultFilter.cs b/src/DotRecast.Detour/DtQueryDefaultFilter.cs index 4bbffa2..08f13ac 100644 --- a/src/DotRecast.Detour/DtQueryDefaultFilter.cs +++ b/src/DotRecast.Detour/DtQueryDefaultFilter.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,6 +23,8 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour { + using static DtDetour; + /** * The Default Implementation * @@ -50,7 +52,7 @@ namespace DotRecast.Detour */ public class DtQueryDefaultFilter : IDtQueryFilter { - private readonly float[] m_areaCost = new float[DtNavMesh.DT_MAX_AREAS]; //< Cost per area type. (Used by default implementation.) + private readonly float[] m_areaCost = new float[DT_MAX_AREAS]; //< Cost per area type. (Used by default implementation.) private int m_includeFlags; //< Flags for polygons that can be visited. (Used by default implementation.) private int m_excludeFlags; //< Flags for polygons that should not be visited. (Used by default implementation.) @@ -58,7 +60,7 @@ namespace DotRecast.Detour { m_includeFlags = 0xffff; m_excludeFlags = 0; - for (int i = 0; i < DtNavMesh.DT_MAX_AREAS; ++i) + for (int i = 0; i < DT_MAX_AREAS; ++i) { m_areaCost[i] = 1.0f; } @@ -68,12 +70,12 @@ namespace DotRecast.Detour { m_includeFlags = includeFlags; m_excludeFlags = excludeFlags; - for (int i = 0; i < Math.Min(DtNavMesh.DT_MAX_AREAS, areaCost.Length); ++i) + for (int i = 0; i < Math.Min(DT_MAX_AREAS, areaCost.Length); ++i) { m_areaCost[i] = areaCost[i]; } - for (int i = areaCost.Length; i < DtNavMesh.DT_MAX_AREAS; ++i) + for (int i = areaCost.Length; i < DT_MAX_AREAS; ++i) { m_areaCost[i] = 1.0f; } diff --git a/src/DotRecast.Detour/DtQueryEmptyFilter.cs b/src/DotRecast.Detour/DtQueryEmptyFilter.cs index 28a722c..35dd37e 100644 --- a/src/DotRecast.Detour/DtQueryEmptyFilter.cs +++ b/src/DotRecast.Detour/DtQueryEmptyFilter.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour { diff --git a/src/DotRecast.Detour/DtQueryNoOpFilter.cs b/src/DotRecast.Detour/DtQueryNoOpFilter.cs index f7e0574..659ff01 100644 --- a/src/DotRecast.Detour/DtQueryNoOpFilter.cs +++ b/src/DotRecast.Detour/DtQueryNoOpFilter.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Detour { diff --git a/src/DotRecast.Detour/DtRaycastHit.cs b/src/DotRecast.Detour/DtRaycastHit.cs index d087ed8..58c79ac 100644 --- a/src/DotRecast.Detour/DtRaycastHit.cs +++ b/src/DotRecast.Detour/DtRaycastHit.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtRaycastOptions.cs b/src/DotRecast.Detour/DtRaycastOptions.cs index 62bbef9..3d8470f 100644 --- a/src/DotRecast.Detour/DtRaycastOptions.cs +++ b/src/DotRecast.Detour/DtRaycastOptions.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { /// Options for dtNavMeshQuery::raycast public static class DtRaycastOptions diff --git a/src/DotRecast.Detour/DtSegInterval.cs b/src/DotRecast.Detour/DtSegInterval.cs index 179c198..72240c2 100644 --- a/src/DotRecast.Detour/DtSegInterval.cs +++ b/src/DotRecast.Detour/DtSegInterval.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { public readonly struct DtSegInterval { diff --git a/src/DotRecast.Detour/DtStatus.cs b/src/DotRecast.Detour/DtStatus.cs index d025408..998783b 100644 --- a/src/DotRecast.Detour/DtStatus.cs +++ b/src/DotRecast.Detour/DtStatus.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/DtStraightPath.cs b/src/DotRecast.Detour/DtStraightPath.cs index 2ad3313..5bb9116 100644 --- a/src/DotRecast.Detour/DtStraightPath.cs +++ b/src/DotRecast.Detour/DtStraightPath.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,11 +25,16 @@ namespace DotRecast.Detour //TODO: (PP) Add comments public readonly struct DtStraightPath { + /// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners] public readonly RcVec3f pos; - public readonly int flags; + + /// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners] + public readonly byte flags; + + /// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners] public readonly long refs; - public DtStraightPath(RcVec3f pos, int flags, long refs) + public DtStraightPath(RcVec3f pos, byte flags, long refs) { this.pos = pos; this.flags = flags; diff --git a/src/DotRecast.Detour/DtStraightPathFlags.cs b/src/DotRecast.Detour/DtStraightPathFlags.cs index a0ea3a3..09c6139 100644 --- a/src/DotRecast.Detour/DtStraightPathFlags.cs +++ b/src/DotRecast.Detour/DtStraightPathFlags.cs @@ -1,10 +1,10 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { /// Vertex flags returned by dtNavMeshQuery::findStraightPath. public static class DtStraightPathFlags { - public const int DT_STRAIGHTPATH_START = 0x01; //< The vertex is the start position in the path. - public const int DT_STRAIGHTPATH_END = 0x02; //< The vertex is the end position in the path. - public const int DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04; //< The vertex is the start of an off-mesh connection. + public const byte DT_STRAIGHTPATH_START = 0x01; //< The vertex is the start position in the path. + public const byte DT_STRAIGHTPATH_END = 0x02; //< The vertex is the end position in the path. + public const byte DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04; //< The vertex is the start of an off-mesh connection. } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtStraightPathOption.cs b/src/DotRecast.Detour/DtStraightPathOption.cs index 9696c0d..79577d9 100644 --- a/src/DotRecast.Detour/DtStraightPathOption.cs +++ b/src/DotRecast.Detour/DtStraightPathOption.cs @@ -1,4 +1,4 @@ -using DotRecast.Core; +using DotRecast.Core; using DotRecast.Core.Collections; namespace DotRecast.Detour diff --git a/src/DotRecast.Detour/DtStraightPathOptions.cs b/src/DotRecast.Detour/DtStraightPathOptions.cs index c2428f9..ef907e0 100644 --- a/src/DotRecast.Detour/DtStraightPathOptions.cs +++ b/src/DotRecast.Detour/DtStraightPathOptions.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Detour +namespace DotRecast.Detour { /// Options for dtNavMeshQuery::findStraightPath. public static class DtStraightPathOptions diff --git a/src/DotRecast.Detour/DtStrictDtPolygonByCircleConstraint.cs b/src/DotRecast.Detour/DtStrictDtPolygonByCircleConstraint.cs index bf5266a..47bf30e 100644 --- a/src/DotRecast.Detour/DtStrictDtPolygonByCircleConstraint.cs +++ b/src/DotRecast.Detour/DtStrictDtPolygonByCircleConstraint.cs @@ -1,15 +1,13 @@ -using System; +using System; using DotRecast.Core.Numerics; namespace DotRecast.Detour { - /** - * Calculate the intersection between a polygon and a circle. A dodecagon is used as an approximation of the circle. - */ + // Calculate the intersection between a polygon and a circle. A dodecagon is used as an approximation of the circle. public class DtStrictDtPolygonByCircleConstraint : IDtPolygonByCircleConstraint { private const int CIRCLE_SEGMENTS = 12; - private static readonly float[] UnitCircle = MakeUnitCircle(); + private static readonly float[] UnitCircle = CreateCircle(); public static readonly IDtPolygonByCircleConstraint Shared = new DtStrictDtPolygonByCircleConstraint(); @@ -17,7 +15,7 @@ namespace DotRecast.Detour { } - private static float[] MakeUnitCircle() + public static float[] CreateCircle() { var temp = new float[CIRCLE_SEGMENTS * 3]; for (int i = 0; i < CIRCLE_SEGMENTS; i++) @@ -31,13 +29,24 @@ namespace DotRecast.Detour return temp; } + public static void ScaleCircle(Span src, RcVec3f center, float radius, Span dst) + { + for (int i = 0; i < CIRCLE_SEGMENTS; i++) + { + dst[3 * i] = src[3 * i] * radius + center.X; + dst[3 * i + 1] = center.Y; + dst[3 * i + 2] = src[3 * i + 2] * radius + center.Z; + } + } + + public float[] Apply(float[] verts, RcVec3f center, float radius) { float radiusSqr = radius * radius; int outsideVertex = -1; for (int pv = 0; pv < verts.Length; pv += 3) { - if (RcVecUtils.Dist2DSqr(center, verts, pv) > radiusSqr) + if (RcVec.Dist2DSqr(center, verts, pv) > radiusSqr) { outsideVertex = pv; break; @@ -50,29 +59,16 @@ namespace DotRecast.Detour return verts; } - float[] qCircle = Circle(center, radius); + Span qCircle = stackalloc float[UnitCircle.Length]; + ScaleCircle(UnitCircle, center, radius, qCircle); float[] intersection = DtConvexConvexIntersections.Intersect(verts, qCircle); if (intersection == null && DtUtils.PointInPolygon(center, verts, verts.Length / 3)) { // circle inside polygon - return qCircle; + return qCircle.ToArray(); } return intersection; } - - - private float[] Circle(RcVec3f center, float radius) - { - float[] circle = new float[12 * 3]; - for (int i = 0; i < CIRCLE_SEGMENTS * 3; i += 3) - { - circle[i] = UnitCircle[i] * radius + center.X; - circle[i + 1] = center.Y; - circle[i + 2] = UnitCircle[i + 2] * radius + center.Z; - } - - return circle; - } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/DtUtils.cs b/src/DotRecast.Detour/DtUtils.cs index 91644e5..14fc291 100644 --- a/src/DotRecast.Detour/DtUtils.cs +++ b/src/DotRecast.Detour/DtUtils.cs @@ -1,13 +1,10 @@ -using System; -using DotRecast.Core; +using System; using DotRecast.Core.Numerics; namespace DotRecast.Detour { public static class DtUtils { - private static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f); - public static int NextPow2(int v) { v--; @@ -39,24 +36,6 @@ namespace DotRecast.Detour return r; } - /// Performs a 'sloppy' colocation check of the specified points. - /// @param[in] p0 A point. [(x, y, z)] - /// @param[in] p1 A point. [(x, y, z)] - /// @return True if the points are considered to be at the same location. - /// - /// Basically, this function will return true if the specified points are - /// close enough to eachother to be considered colocated. - public static bool VEqual(RcVec3f p0, RcVec3f p1) - { - return VEqual(p0, p1, EQUAL_THRESHOLD); - } - - public static bool VEqual(RcVec3f p0, RcVec3f p1, float thresholdSqr) - { - float d = RcVec3f.DistanceSquared(p0, p1); - return d < thresholdSqr; - } - /// Determines if two axis-aligned bounding boxes overlap. /// @param[in] amin Minimum bounds of box A. [(x, y, z)] /// @param[in] amax Maximum bounds of box A. [(x, y, z)] @@ -64,12 +43,12 @@ namespace DotRecast.Detour /// @param[in] bmax Maximum bounds of box B. [(x, y, z)] /// @return True if the two AABB's overlap. /// @see dtOverlapBounds - public static bool OverlapQuantBounds(Span amin, Span amax, Span bmin, Span bmax) + public static bool OverlapQuantBounds(ref RcVec3i amin, ref RcVec3i amax, ref RcVec3i bmin, ref RcVec3i bmax) { bool overlap = true; - overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; - overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; - overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; + overlap = (amin.X > bmax.X || amax.X < bmin.X) ? false : overlap; + overlap = (amin.Y > bmax.Y || amax.Y < bmin.Y) ? false : overlap; + overlap = (amin.Z > bmax.Z || amax.Z < bmin.Z) ? false : overlap; return overlap; } @@ -97,7 +76,7 @@ namespace DotRecast.Detour /// @par /// /// All vertices are projected onto the xz-plane, so the y-values are ignored. - public static bool OverlapPolyPoly2D(float[] polya, int npolya, float[] polyb, int npolyb) + public static bool OverlapPolyPoly2D(Span polya, int npolya, Span polyb, int npolyb) { const float eps = 1e-4f; for (int i = 0, j = npolya - 1; i < npolya; j = i++) @@ -145,7 +124,7 @@ namespace DotRecast.Detour /// @param[in] b Vertex B. [(x, y, z)] /// @param[in] c Vertex C. [(x, y, z)] /// @return The signed xz-plane area of the triangle. - public static float TriArea2D(float[] verts, int a, int b, int c) + public static float TriArea2D(Span verts, int a, int b, int c) { float abx = verts[b] - verts[a]; float abz = verts[b + 2] - verts[a + 2]; @@ -165,7 +144,7 @@ namespace DotRecast.Detour // Returns a random point in a convex polygon. // Adapted from Graphics Gems article. - public static RcVec3f RandomPointInConvexPoly(float[] pts, int npts, float[] areas, float s, float t) + public static void RandomPointInConvexPoly(Span pts, int npts, Span areas, float s, float t, out RcVec3f @out) { // Calc triangle araes float areasum = 0.0f; @@ -202,7 +181,7 @@ namespace DotRecast.Detour int pb = (tri - 1) * 3; int pc = tri * 3; - return new RcVec3f() + @out = new RcVec3f() { X = a * pts[pa] + b * pts[pb] + c * pts[pc], Y = a * pts[pa + 1] + b * pts[pb + 1] + c * pts[pc + 1], @@ -246,13 +225,13 @@ namespace DotRecast.Detour return false; } - public static RcVec2f ProjectPoly(RcVec3f axis, float[] poly, int npoly) + public static RcVec2f ProjectPoly(RcVec3f axis, Span poly, int npoly) { float rmin, rmax; - rmin = rmax = axis.Dot2D(poly, 0); + rmin = rmax = axis.Dot2D(new RcVec3f(poly)); for (int i = 1; i < npoly; ++i) { - float d = axis.Dot2D(poly, i * 3); + float d = axis.Dot2D(RcVec.Create(poly, i * 3)); rmin = Math.Min(rmin, d); rmax = Math.Max(rmax, d); } @@ -267,7 +246,7 @@ namespace DotRecast.Detour /// @par /// /// All points are projected onto the xz-plane, so the y-values are ignored. - public static bool PointInPolygon(RcVec3f pt, float[] verts, int nverts) + public static bool PointInPolygon(RcVec3f pt, Span verts, int nverts) { // TODO: Replace pnpoly with triArea2D tests? int i, j; @@ -286,7 +265,7 @@ namespace DotRecast.Detour return c; } - public static bool DistancePtPolyEdgesSqr(RcVec3f pt, float[] verts, int nverts, float[] ed, float[] et) + public static bool DistancePtPolyEdgesSqr(RcVec3f pt, Span verts, int nverts, Span ed, Span et) { // TODO: Replace pnpoly with triArea2D tests? int i, j; @@ -307,10 +286,10 @@ namespace DotRecast.Detour return c; } - public static float DistancePtSegSqr2D(RcVec3f pt, float[] verts, int p, int q, out float t) + public static float DistancePtSegSqr2D(RcVec3f pt, Span verts, int p, int q, out float t) { - var vp = RcVecUtils.Create(verts, p); - var vq = RcVecUtils.Create(verts, q); + var vp = RcVec.Create(verts, p); + var vq = RcVec.Create(verts, q); return DistancePtSegSqr2D(pt, vp, vq, out t); } @@ -342,7 +321,7 @@ namespace DotRecast.Detour } public static bool IntersectSegmentPoly2D(RcVec3f p0, RcVec3f p1, - RcVec3f[] verts, int nverts, + Span verts, int nverts, out float tmin, out float tmax, out int segMin, out int segMax) { @@ -362,8 +341,8 @@ namespace DotRecast.Detour RcVec3f vpi = verts[i]; var edge = RcVec3f.Subtract(vpi, vpj); var diff = RcVec3f.Subtract(p0v, vpj); - float n = RcVecUtils.Perp2D(edge, diff); - float d = RcVecUtils.Perp2D(dir, edge); + float n = RcVec.Perp2D(edge, diff); + float d = RcVec.Perp2D(dir, edge); if (MathF.Abs(d) < EPS) { // S is nearly parallel to this edge @@ -425,14 +404,14 @@ namespace DotRecast.Detour RcVec3f u = RcVec3f.Subtract(aq, ap); RcVec3f v = RcVec3f.Subtract(bq, bp); RcVec3f w = RcVec3f.Subtract(ap, bp); - float d = RcVecUtils.PerpXZ(u, v); + float d = RcVec.PerpXZ(u, v); if (MathF.Abs(d) < 1e-6f) { return false; } - s = RcVecUtils.PerpXZ(v, w) / d; - t = RcVecUtils.PerpXZ(u, w) / d; + s = RcVec.PerpXZ(v, w) / d; + t = RcVec.PerpXZ(u, w) / d; return true; } diff --git a/src/DotRecast.Detour/IDtPolyQuery.cs b/src/DotRecast.Detour/IDtPolyQuery.cs index 56fb244..890d171 100644 --- a/src/DotRecast.Detour/IDtPolyQuery.cs +++ b/src/DotRecast.Detour/IDtPolyQuery.cs @@ -1,7 +1,14 @@ +using System; + namespace DotRecast.Detour { + /// Provides custom polygon query behavior. + /// Used by dtNavMeshQuery::queryPolygons. + /// @ingroup detour public interface IDtPolyQuery { - void Process(DtMeshTile tile, DtPoly poly, long refs); + /// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons. + /// This can be called multiple times for a single query. + void Process(DtMeshTile tile, DtPoly[] poly, Span refs, int count); } } \ No newline at end of file diff --git a/src/DotRecast.Detour/IDtPolygonByCircleConstraint.cs b/src/DotRecast.Detour/IDtPolygonByCircleConstraint.cs index e0927a4..9f46ff3 100644 --- a/src/DotRecast.Detour/IDtPolygonByCircleConstraint.cs +++ b/src/DotRecast.Detour/IDtPolygonByCircleConstraint.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/IDtQueryFilter.cs b/src/DotRecast.Detour/IDtQueryFilter.cs index a51213d..f4f5572 100644 --- a/src/DotRecast.Detour/IDtQueryFilter.cs +++ b/src/DotRecast.Detour/IDtQueryFilter.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/IDtQueryHeuristic.cs b/src/DotRecast.Detour/IDtQueryHeuristic.cs index aa71931..2600dd4 100644 --- a/src/DotRecast.Detour/IDtQueryHeuristic.cs +++ b/src/DotRecast.Detour/IDtQueryHeuristic.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Detour/Io/DtMeshDataReader.cs b/src/DotRecast.Detour/Io/DtMeshDataReader.cs index 4d83ef2..0afe161 100644 --- a/src/DotRecast.Detour/Io/DtMeshDataReader.cs +++ b/src/DotRecast.Detour/Io/DtMeshDataReader.cs @@ -21,13 +21,17 @@ using DotRecast.Core; namespace DotRecast.Detour.Io { + using static DtDetour; + public class DtMeshDataReader { public const int DT_POLY_DETAIL_SIZE = 10; + public const int LINK_SIZEOF = 16; + public const int LINK_SIZEOF32BIT = 12; public DtMeshData Read(BinaryReader stream, int maxVertPerPoly) { - RcByteBuffer buf = IOUtils.ToByteBuffer(stream); + RcByteBuffer buf = RcIO.ToByteBuffer(stream); return Read(buf, maxVertPerPoly, false); } @@ -38,7 +42,7 @@ namespace DotRecast.Detour.Io public DtMeshData Read32Bit(BinaryReader stream, int maxVertPerPoly) { - RcByteBuffer buf = IOUtils.ToByteBuffer(stream); + RcByteBuffer buf = RcIO.ToByteBuffer(stream); return Read(buf, maxVertPerPoly, true); } @@ -53,10 +57,10 @@ namespace DotRecast.Detour.Io DtMeshHeader header = new DtMeshHeader(); data.header = header; header.magic = buf.GetInt(); - if (header.magic != DtNavMesh.DT_NAVMESH_MAGIC) + if (header.magic != DT_NAVMESH_MAGIC) { - header.magic = IOUtils.SwapEndianness(header.magic); - if (header.magic != DtNavMesh.DT_NAVMESH_MAGIC) + header.magic = RcIO.SwapEndianness(header.magic); + if (header.magic != DT_NAVMESH_MAGIC) { throw new IOException("Invalid magic"); } @@ -65,16 +69,16 @@ namespace DotRecast.Detour.Io } header.version = buf.GetInt(); - if (header.version != DtNavMesh.DT_NAVMESH_VERSION) + if (header.version != DT_NAVMESH_VERSION) { - if (header.version < DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_FIRST - || header.version > DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_LAST) + if (header.version < DT_NAVMESH_VERSION_RECAST4J_FIRST + || header.version > DT_NAVMESH_VERSION_RECAST4J_LAST) { throw new IOException("Invalid version " + header.version); } } - bool cCompatibility = header.version == DtNavMesh.DT_NAVMESH_VERSION; + bool cCompatibility = header.version == DT_NAVMESH_VERSION; header.x = buf.GetInt(); header.y = buf.GetInt(); header.layer = buf.GetInt(); @@ -91,7 +95,7 @@ namespace DotRecast.Detour.Io header.walkableHeight = buf.GetFloat(); header.walkableRadius = buf.GetFloat(); header.walkableClimb = buf.GetFloat(); - + header.bmin.X = buf.GetFloat(); header.bmin.Y = buf.GetFloat(); header.bmin.Z = buf.GetFloat(); @@ -116,8 +120,6 @@ namespace DotRecast.Detour.Io return data; } - public const int LINK_SIZEOF = 16; - public const int LINK_SIZEOF32BIT = 12; public static int GetSizeofLink(bool is32Bit) { @@ -141,7 +143,7 @@ namespace DotRecast.Detour.Io for (int i = 0; i < polys.Length; i++) { polys[i] = new DtPoly(i, maxVertPerPoly); - if (header.version < DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_NO_POLY_FIRSTLINK) + if (header.version < DT_NAVMESH_VERSION_RECAST4J_NO_POLY_FIRSTLINK) { buf.GetInt(); // polys[i].firstLink } @@ -171,8 +173,8 @@ namespace DotRecast.Detour.Io { int vertBase = buf.GetInt(); int triBase = buf.GetInt(); - int vertCount = buf.Get() & 0xFF; - int triCount = buf.Get() & 0xFF; + byte vertCount = (byte)(buf.Get() & 0xFF); + byte triCount = (byte)(buf.Get() & 0xFF); polys[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); if (cCompatibility) { @@ -200,29 +202,25 @@ namespace DotRecast.Detour.Io for (int i = 0; i < nodes.Length; i++) { nodes[i] = new DtBVNode(); - if (header.version < DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_32BIT_BVTREE) + if (header.version < DT_NAVMESH_VERSION_RECAST4J_32BIT_BVTREE) { - for (int j = 0; j < 3; j++) - { - nodes[i].bmin[j] = buf.GetShort() & 0xFFFF; - } + nodes[i].bmin.X = buf.GetShort() & 0xFFFF; + nodes[i].bmin.Y = buf.GetShort() & 0xFFFF; + nodes[i].bmin.Z = buf.GetShort() & 0xFFFF; - for (int j = 0; j < 3; j++) - { - nodes[i].bmax[j] = buf.GetShort() & 0xFFFF; - } + nodes[i].bmax.X = buf.GetShort() & 0xFFFF; + nodes[i].bmax.Y = buf.GetShort() & 0xFFFF; + nodes[i].bmax.Z = buf.GetShort() & 0xFFFF; } else { - for (int j = 0; j < 3; j++) - { - nodes[i].bmin[j] = buf.GetInt(); - } + nodes[i].bmin.X = buf.GetInt(); + nodes[i].bmin.Y = buf.GetInt(); + nodes[i].bmin.Z = buf.GetInt(); - for (int j = 0; j < 3; j++) - { - nodes[i].bmax[j] = buf.GetInt(); - } + nodes[i].bmax.X = buf.GetInt(); + nodes[i].bmax.Y = buf.GetInt(); + nodes[i].bmax.Z = buf.GetInt(); } nodes[i].i = buf.GetInt(); diff --git a/src/DotRecast.Detour/Io/DtMeshDataWriter.cs b/src/DotRecast.Detour/Io/DtMeshDataWriter.cs index fd9cf65..75ab3c7 100644 --- a/src/DotRecast.Detour/Io/DtMeshDataWriter.cs +++ b/src/DotRecast.Detour/Io/DtMeshDataWriter.cs @@ -21,36 +21,38 @@ using DotRecast.Core; namespace DotRecast.Detour.Io { - public class DtMeshDataWriter : DtWriter + using static DtDetour; + + public class DtMeshDataWriter { public void Write(BinaryWriter stream, DtMeshData data, RcByteOrder order, bool cCompatibility) { DtMeshHeader header = data.header; - Write(stream, header.magic, order); - Write(stream, cCompatibility ? DtNavMesh.DT_NAVMESH_VERSION : DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_LAST, order); - Write(stream, header.x, order); - Write(stream, header.y, order); - Write(stream, header.layer, order); - Write(stream, header.userId, order); - Write(stream, header.polyCount, order); - Write(stream, header.vertCount, order); - Write(stream, header.maxLinkCount, order); - Write(stream, header.detailMeshCount, order); - Write(stream, header.detailVertCount, order); - Write(stream, header.detailTriCount, order); - Write(stream, header.bvNodeCount, order); - Write(stream, header.offMeshConCount, order); - Write(stream, header.offMeshBase, order); - Write(stream, header.walkableHeight, order); - Write(stream, header.walkableRadius, order); - Write(stream, header.walkableClimb, order); - Write(stream, header.bmin.X, order); - Write(stream, header.bmin.Y, order); - Write(stream, header.bmin.Z, order); - Write(stream, header.bmax.X, order); - Write(stream, header.bmax.Y, order); - Write(stream, header.bmax.Z, order); - Write(stream, header.bvQuantFactor, order); + RcIO.Write(stream, header.magic, order); + RcIO.Write(stream, cCompatibility ? DT_NAVMESH_VERSION : DT_NAVMESH_VERSION_RECAST4J_LAST, order); + RcIO.Write(stream, header.x, order); + RcIO.Write(stream, header.y, order); + RcIO.Write(stream, header.layer, order); + RcIO.Write(stream, header.userId, order); + RcIO.Write(stream, header.polyCount, order); + RcIO.Write(stream, header.vertCount, order); + RcIO.Write(stream, header.maxLinkCount, order); + RcIO.Write(stream, header.detailMeshCount, order); + RcIO.Write(stream, header.detailVertCount, order); + RcIO.Write(stream, header.detailTriCount, order); + RcIO.Write(stream, header.bvNodeCount, order); + RcIO.Write(stream, header.offMeshConCount, order); + RcIO.Write(stream, header.offMeshBase, order); + RcIO.Write(stream, header.walkableHeight, order); + RcIO.Write(stream, header.walkableRadius, order); + RcIO.Write(stream, header.walkableClimb, order); + RcIO.Write(stream, header.bmin.X, order); + RcIO.Write(stream, header.bmin.Y, order); + RcIO.Write(stream, header.bmin.Z, order); + RcIO.Write(stream, header.bmax.X, order); + RcIO.Write(stream, header.bmax.Y, order); + RcIO.Write(stream, header.bmax.Z, order); + RcIO.Write(stream, header.bvQuantFactor, order); WriteVerts(stream, data.verts, header.vertCount, order); WritePolys(stream, data, order, cCompatibility); if (cCompatibility) @@ -70,7 +72,7 @@ namespace DotRecast.Detour.Io { for (int i = 0; i < count * 3; i++) { - Write(stream, verts[i], order); + RcIO.Write(stream, verts[i], order); } } @@ -80,22 +82,22 @@ namespace DotRecast.Detour.Io { if (cCompatibility) { - Write(stream, 0xFFFF, order); + RcIO.Write(stream, 0xFFFF, order); } for (int j = 0; j < data.polys[i].verts.Length; j++) { - Write(stream, (short)data.polys[i].verts[j], order); + RcIO.Write(stream, (short)data.polys[i].verts[j], order); } for (int j = 0; j < data.polys[i].neis.Length; j++) { - Write(stream, (short)data.polys[i].neis[j], order); + RcIO.Write(stream, (short)data.polys[i].neis[j], order); } - Write(stream, (short)data.polys[i].flags, order); - Write(stream, (byte)data.polys[i].vertCount); - Write(stream, (byte)data.polys[i].areaAndtype); + RcIO.Write(stream, (short)data.polys[i].flags, order); + RcIO.Write(stream, (byte)data.polys[i].vertCount); + RcIO.Write(stream, (byte)data.polys[i].areaAndtype); } } @@ -103,13 +105,13 @@ namespace DotRecast.Detour.Io { for (int i = 0; i < data.header.detailMeshCount; i++) { - Write(stream, data.detailMeshes[i].vertBase, order); - Write(stream, data.detailMeshes[i].triBase, order); - Write(stream, (byte)data.detailMeshes[i].vertCount); - Write(stream, (byte)data.detailMeshes[i].triCount); + RcIO.Write(stream, data.detailMeshes[i].vertBase, order); + RcIO.Write(stream, data.detailMeshes[i].triBase, order); + RcIO.Write(stream, (byte)data.detailMeshes[i].vertCount); + RcIO.Write(stream, (byte)data.detailMeshes[i].triCount); if (cCompatibility) { - Write(stream, (short)0, order); + RcIO.Write(stream, (short)0, order); } } } @@ -118,7 +120,7 @@ namespace DotRecast.Detour.Io { for (int i = 0; i < data.header.detailTriCount * 4; i++) { - Write(stream, (byte)data.detailTris[i]); + RcIO.Write(stream, (byte)data.detailTris[i]); } } @@ -128,30 +130,26 @@ namespace DotRecast.Detour.Io { if (cCompatibility) { - for (int j = 0; j < 3; j++) - { - Write(stream, (short)data.bvTree[i].bmin[j], order); - } + RcIO.Write(stream, (short)data.bvTree[i].bmin.X, order); + RcIO.Write(stream, (short)data.bvTree[i].bmin.Y, order); + RcIO.Write(stream, (short)data.bvTree[i].bmin.Z, order); - for (int j = 0; j < 3; j++) - { - Write(stream, (short)data.bvTree[i].bmax[j], order); - } + RcIO.Write(stream, (short)data.bvTree[i].bmax.X, order); + RcIO.Write(stream, (short)data.bvTree[i].bmax.Y, order); + RcIO.Write(stream, (short)data.bvTree[i].bmax.Z, order); } else { - for (int j = 0; j < 3; j++) - { - Write(stream, data.bvTree[i].bmin[j], order); - } + RcIO.Write(stream, data.bvTree[i].bmin.X, order); + RcIO.Write(stream, data.bvTree[i].bmin.Y, order); + RcIO.Write(stream, data.bvTree[i].bmin.Z, order); - for (int j = 0; j < 3; j++) - { - Write(stream, data.bvTree[i].bmax[j], order); - } + RcIO.Write(stream, data.bvTree[i].bmax.X, order); + RcIO.Write(stream, data.bvTree[i].bmax.Y, order); + RcIO.Write(stream, data.bvTree[i].bmax.Z, order); } - Write(stream, data.bvTree[i].i, order); + RcIO.Write(stream, data.bvTree[i].i, order); } } @@ -161,16 +159,16 @@ namespace DotRecast.Detour.Io { for (int j = 0; j < 2; j++) { - Write(stream, data.offMeshCons[i].pos[j].X, order); - Write(stream, data.offMeshCons[i].pos[j].Y, order); - Write(stream, data.offMeshCons[i].pos[j].Z, order); + RcIO.Write(stream, data.offMeshCons[i].pos[j].X, order); + RcIO.Write(stream, data.offMeshCons[i].pos[j].Y, order); + RcIO.Write(stream, data.offMeshCons[i].pos[j].Z, order); } - Write(stream, data.offMeshCons[i].rad, order); - Write(stream, (short)data.offMeshCons[i].poly, order); - Write(stream, (byte)data.offMeshCons[i].flags); - Write(stream, (byte)data.offMeshCons[i].side); - Write(stream, data.offMeshCons[i].userId, order); + RcIO.Write(stream, data.offMeshCons[i].rad, order); + RcIO.Write(stream, (short)data.offMeshCons[i].poly, order); + RcIO.Write(stream, (byte)data.offMeshCons[i].flags); + RcIO.Write(stream, (byte)data.offMeshCons[i].side); + RcIO.Write(stream, data.offMeshCons[i].userId, order); } } } diff --git a/src/DotRecast.Detour/Io/DtMeshSetReader.cs b/src/DotRecast.Detour/Io/DtMeshSetReader.cs index a02e020..8e0714d 100644 --- a/src/DotRecast.Detour/Io/DtMeshSetReader.cs +++ b/src/DotRecast.Detour/Io/DtMeshSetReader.cs @@ -22,6 +22,8 @@ using DotRecast.Core; namespace DotRecast.Detour.Io { + using static DtDetour; + public class DtMeshSetReader { private readonly DtMeshDataReader meshReader = new DtMeshDataReader(); @@ -29,7 +31,7 @@ namespace DotRecast.Detour.Io public DtNavMesh Read(BinaryReader @is, int maxVertPerPoly) { - return Read(IOUtils.ToByteBuffer(@is), maxVertPerPoly, false); + return Read(RcIO.ToByteBuffer(@is), maxVertPerPoly, false); } public DtNavMesh Read(RcByteBuffer bb, int maxVertPerPoly) @@ -39,7 +41,7 @@ namespace DotRecast.Detour.Io public DtNavMesh Read32Bit(BinaryReader @is, int maxVertPerPoly) { - return Read(IOUtils.ToByteBuffer(@is), maxVertPerPoly, true); + return Read(RcIO.ToByteBuffer(@is), maxVertPerPoly, true); } public DtNavMesh Read32Bit(RcByteBuffer bb, int maxVertPerPoly) @@ -49,7 +51,7 @@ namespace DotRecast.Detour.Io public DtNavMesh Read(BinaryReader @is) { - return Read(IOUtils.ToByteBuffer(@is)); + return Read(RcIO.ToByteBuffer(@is)); } public DtNavMesh Read(RcByteBuffer bb) @@ -66,7 +68,8 @@ namespace DotRecast.Detour.Io } bool cCompatibility = header.version == NavMeshSetHeader.NAVMESHSET_VERSION; - DtNavMesh mesh = new DtNavMesh(header.option, header.maxVertsPerPoly); + DtNavMesh mesh = new DtNavMesh(); + mesh.Init(header.option, header.maxVertsPerPoly); ReadTiles(bb, is32Bit, ref header, cCompatibility, mesh); return mesh; } @@ -77,7 +80,7 @@ namespace DotRecast.Detour.Io header.magic = bb.GetInt(); if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC) { - header.magic = IOUtils.SwapEndianness(header.magic); + header.magic = RcIO.SwapEndianness(header.magic); if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC) { throw new IOException("Invalid magic " + header.magic); @@ -131,7 +134,7 @@ namespace DotRecast.Detour.Io } DtMeshData data = meshReader.Read(bb, mesh.GetMaxVertsPerPoly(), is32Bit); - mesh.AddTile(data, i, tileHeader.tileRef); + mesh.AddTile(data, i, tileHeader.tileRef, out _); } } @@ -147,7 +150,7 @@ namespace DotRecast.Detour.Io int salt = ((refs >> (m_polyBits + m_tileBits)) & saltMask); int it = ((refs >> m_polyBits) & tileMask); int ip = refs & polyMask; - return DtNavMesh.EncodePolyId(salt, it, ip); + return EncodePolyId(salt, it, ip); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/Io/DtMeshSetWriter.cs b/src/DotRecast.Detour/Io/DtMeshSetWriter.cs index 4568813..0d0ec88 100644 --- a/src/DotRecast.Detour/Io/DtMeshSetWriter.cs +++ b/src/DotRecast.Detour/Io/DtMeshSetWriter.cs @@ -22,7 +22,7 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour.Io { - public class DtMeshSetWriter : DtWriter + public class DtMeshSetWriter { private readonly DtMeshDataWriter writer = new DtMeshDataWriter(); private readonly DtNavMeshParamWriter paramWriter = new DtNavMeshParamWriter(); @@ -35,8 +35,8 @@ namespace DotRecast.Detour.Io private void WriteHeader(BinaryWriter stream, DtNavMesh mesh, RcByteOrder order, bool cCompatibility) { - Write(stream, NavMeshSetHeader.NAVMESHSET_MAGIC, order); - Write(stream, cCompatibility ? NavMeshSetHeader.NAVMESHSET_VERSION : NavMeshSetHeader.NAVMESHSET_VERSION_RECAST4J, order); + RcIO.Write(stream, NavMeshSetHeader.NAVMESHSET_MAGIC, order); + RcIO.Write(stream, cCompatibility ? NavMeshSetHeader.NAVMESHSET_VERSION : NavMeshSetHeader.NAVMESHSET_VERSION_RECAST4J, order); int numTiles = 0; for (int i = 0; i < mesh.GetMaxTiles(); ++i) { @@ -49,11 +49,11 @@ namespace DotRecast.Detour.Io numTiles++; } - Write(stream, numTiles, order); + RcIO.Write(stream, numTiles, order); paramWriter.Write(stream, mesh.GetParams(), order); if (!cCompatibility) { - Write(stream, mesh.GetMaxVertsPerPoly(), order); + RcIO.Write(stream, mesh.GetMaxVertsPerPoly(), order); } } @@ -77,11 +77,11 @@ namespace DotRecast.Detour.Io byte[] ba = msw.ToArray(); tileHeader.dataSize = ba.Length; - Write(stream, tileHeader.tileRef, order); - Write(stream, tileHeader.dataSize, order); + RcIO.Write(stream, tileHeader.tileRef, order); + RcIO.Write(stream, tileHeader.dataSize, order); if (cCompatibility) { - Write(stream, 0, order); // C struct padding + RcIO.Write(stream, 0, order); // C struct padding } stream.Write(ba); diff --git a/src/DotRecast.Detour/Io/DtNavMeshParamWriter.cs b/src/DotRecast.Detour/Io/DtNavMeshParamWriter.cs index 9aab820..75bdff0 100644 --- a/src/DotRecast.Detour/Io/DtNavMeshParamWriter.cs +++ b/src/DotRecast.Detour/Io/DtNavMeshParamWriter.cs @@ -4,17 +4,17 @@ using DotRecast.Core.Numerics; namespace DotRecast.Detour.Io { - public class DtNavMeshParamWriter : DtWriter + public class DtNavMeshParamWriter { public void Write(BinaryWriter stream, DtNavMeshParams option, RcByteOrder order) { - Write(stream, option.orig.X, order); - Write(stream, option.orig.Y, order); - Write(stream, option.orig.Z, order); - Write(stream, option.tileWidth, order); - Write(stream, option.tileHeight, order); - Write(stream, option.maxTiles, order); - Write(stream, option.maxPolys, order); + RcIO.Write(stream, option.orig.X, order); + RcIO.Write(stream, option.orig.Y, order); + RcIO.Write(stream, option.orig.Z, order); + RcIO.Write(stream, option.tileWidth, order); + RcIO.Write(stream, option.tileHeight, order); + RcIO.Write(stream, option.maxTiles, order); + RcIO.Write(stream, option.maxPolys, order); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour/Io/DtWriter.cs b/src/DotRecast.Detour/Io/DtWriter.cs deleted file mode 100644 index 30eb56a..0000000 --- a/src/DotRecast.Detour/Io/DtWriter.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* -Recast4J Copyright (c) 2015 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.IO; -using DotRecast.Core; - -namespace DotRecast.Detour.Io -{ - public abstract class DtWriter - { - protected void Write(BinaryWriter stream, float value, RcByteOrder order) - { - byte[] bytes = BitConverter.GetBytes(value); - int i = BitConverter.ToInt32(bytes, 0); - Write(stream, i, order); - } - - protected void Write(BinaryWriter stream, short value, RcByteOrder order) - { - if (order == RcByteOrder.BIG_ENDIAN) - { - stream.Write((byte)((value >> 8) & 0xFF)); - stream.Write((byte)(value & 0xFF)); - } - else - { - stream.Write((byte)(value & 0xFF)); - stream.Write((byte)((value >> 8) & 0xFF)); - } - } - - protected void Write(BinaryWriter stream, long value, RcByteOrder order) - { - if (order == RcByteOrder.BIG_ENDIAN) - { - Write(stream, (int)((ulong)value >> 32), order); - Write(stream, (int)(value & 0xFFFFFFFF), order); - } - else - { - Write(stream, (int)(value & 0xFFFFFFFF), order); - Write(stream, (int)((ulong)value >> 32), order); - } - } - - protected void Write(BinaryWriter stream, int value, RcByteOrder order) - { - if (order == RcByteOrder.BIG_ENDIAN) - { - stream.Write((byte)((value >> 24) & 0xFF)); - stream.Write((byte)((value >> 16) & 0xFF)); - stream.Write((byte)((value >> 8) & 0xFF)); - stream.Write((byte)(value & 0xFF)); - } - else - { - stream.Write((byte)(value & 0xFF)); - stream.Write((byte)((value >> 8) & 0xFF)); - stream.Write((byte)((value >> 16) & 0xFF)); - stream.Write((byte)((value >> 24) & 0xFF)); - } - } - - protected void Write(BinaryWriter stream, bool @bool) - { - Write(stream, (byte)(@bool ? 1 : 0)); - } - - protected void Write(BinaryWriter stream, byte value) - { - stream.Write(value); - } - - protected void Write(BinaryWriter stream, MemoryStream data) - { - data.Position = 0; - byte[] buffer = new byte[data.Length]; - data.Read(buffer, 0, buffer.Length); - stream.Write(buffer); - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour/Io/IOUtils.cs b/src/DotRecast.Detour/Io/IOUtils.cs deleted file mode 100644 index 2c5f7d2..0000000 --- a/src/DotRecast.Detour/Io/IOUtils.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* -Recast4J Copyright (c) 2015 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.IO; -using DotRecast.Core; - -namespace DotRecast.Detour.Io -{ - public static class IOUtils - { - public static RcByteBuffer ToByteBuffer(BinaryReader @is, bool direct) - { - byte[] data = ToByteArray(@is); - if (direct) - { - Array.Reverse(data); - } - - return new RcByteBuffer(data); - } - - public static byte[] ToByteArray(BinaryReader inputStream) - { - using var msw = new MemoryStream(); - byte[] buffer = new byte[4096]; - int l; - while ((l = inputStream.Read(buffer)) > 0) - { - msw.Write(buffer, 0, l); - } - - return msw.ToArray(); - } - - - public static RcByteBuffer ToByteBuffer(BinaryReader inputStream) - { - var bytes = ToByteArray(inputStream); - return new RcByteBuffer(bytes); - } - - public static int SwapEndianness(int i) - { - var s = (((uint)i >> 24) & 0xFF) | (((uint)i >> 8) & 0xFF00) | (((uint)i << 8) & 0xFF0000) | ((i << 24) & 0xFF000000); - return (int)s; - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/DemoSample.cs b/src/DotRecast.Recast.Demo/DemoSample.cs index ee62edf..be365d2 100644 --- a/src/DotRecast.Recast.Demo/DemoSample.cs +++ b/src/DotRecast.Recast.Demo/DemoSample.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -32,6 +32,7 @@ namespace DotRecast.Recast.Demo private DtNavMesh _navMesh; private DtNavMeshQuery _navMeshQuery; private readonly RcNavMeshBuildSettings _settings; + private RcConfig _cfg; private IList _recastResults; private bool _changed; @@ -56,6 +57,11 @@ namespace DotRecast.Recast.Demo return _geom; } + public RcConfig GetRecastConfig() + { + return _cfg; + } + public IList GetRecastResults() { return _recastResults; @@ -86,9 +92,10 @@ namespace DotRecast.Recast.Demo _changed = changed; } - public void Update(DemoInputGeomProvider geom, IList recastResults, DtNavMesh navMesh) + public void Update(DemoInputGeomProvider geom, RcConfig cfg, IList recastResults, DtNavMesh navMesh) { _geom = geom; + _cfg = cfg; _recastResults = recastResults; _navMesh = navMesh; SetQuery(navMesh); diff --git a/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj b/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj index b231327..fa14c2e 100644 --- a/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj +++ b/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj @@ -20,15 +20,15 @@ - - - - - - + + + + + + - - + + diff --git a/src/DotRecast.Recast.Demo/Draw/ArrayBuffer.cs b/src/DotRecast.Recast.Demo/Draw/ArrayBuffer.cs index 3002693..59ad39f 100644 --- a/src/DotRecast.Recast.Demo/Draw/ArrayBuffer.cs +++ b/src/DotRecast.Recast.Demo/Draw/ArrayBuffer.cs @@ -1,4 +1,4 @@ -using System; +using System; using DotRecast.Core; namespace DotRecast.Recast.Demo.Draw; @@ -9,19 +9,19 @@ public class ArrayBuffer private T[] _items; public int Count => _size; - public ArrayBuffer() + public ArrayBuffer() : this(512) { } + + public ArrayBuffer(int capacity) { + if (capacity <= 0) + throw new ArgumentOutOfRangeException(); + _size = 0; - _items = Array.Empty(); + _items = new T[capacity]; } public void Add(T item) { - if (0 >= _items.Length) - { - _items = new T[256]; - } - if (_items.Length <= _size) { var temp = new T[(int)(_size * 1.5)]; @@ -37,8 +37,8 @@ public class ArrayBuffer _size = 0; } - public T[] AsArray() + public Span AsArray() { - return _items; + return _items.AsSpan(0, _size); } } \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/Draw/DebugDraw.cs b/src/DotRecast.Recast.Demo/Draw/DebugDraw.cs index 875f17b..97a5e09 100644 --- a/src/DotRecast.Recast.Demo/Draw/DebugDraw.cs +++ b/src/DotRecast.Recast.Demo/Draw/DebugDraw.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -326,9 +326,9 @@ public class DebugDraw } } - private static readonly int NUM_ARC_PTS = 8; - private static readonly float PAD = 0.05f; - private static readonly float ARC_PTS_SCALE = (1.0f - PAD * 2) / NUM_ARC_PTS; + private const int NUM_ARC_PTS = 8; + private const float PAD = 0.05f; + private const float ARC_PTS_SCALE = (1.0f - PAD * 2) / NUM_ARC_PTS; public void AppendArc(float x0, float y0, float z0, float x1, float y1, float z1, float h, float as0, float as1, int col) { @@ -694,7 +694,7 @@ public class DebugDraw plane[3] = pw; } - public bool FrustumTest(float[] bounds) + public bool FrustumTest(Span bounds) { foreach (float[] plane in frustumPlanes) { @@ -748,6 +748,6 @@ public class DebugDraw public bool FrustumTest(RcVec3f bmin, RcVec3f bmax) { - return FrustumTest(new float[] { bmin.X, bmin.Y, bmin.Z, bmax.X, bmax.Y, bmax.Z }); + return FrustumTest(stackalloc float[] { bmin.X, bmin.Y, bmin.Z, bmax.X, bmax.Y, bmax.Z }); } } \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/Draw/DebugDrawPrimitives.cs b/src/DotRecast.Recast.Demo/Draw/DebugDrawPrimitives.cs index e77dbe4..9278210 100644 --- a/src/DotRecast.Recast.Demo/Draw/DebugDrawPrimitives.cs +++ b/src/DotRecast.Recast.Demo/Draw/DebugDrawPrimitives.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Draw/DrawMode.cs b/src/DotRecast.Recast.Demo/Draw/DrawMode.cs index ff2911d..2fd15fa 100644 --- a/src/DotRecast.Recast.Demo/Draw/DrawMode.cs +++ b/src/DotRecast.Recast.Demo/Draw/DrawMode.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Draw/GLCheckerTexture.cs b/src/DotRecast.Recast.Demo/Draw/GLCheckerTexture.cs index e044757..66d0cea 100644 --- a/src/DotRecast.Recast.Demo/Draw/GLCheckerTexture.cs +++ b/src/DotRecast.Recast.Demo/Draw/GLCheckerTexture.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Draw/GLU.cs b/src/DotRecast.Recast.Demo/Draw/GLU.cs index f14aa22..6a7510f 100644 --- a/src/DotRecast.Recast.Demo/Draw/GLU.cs +++ b/src/DotRecast.Recast.Demo/Draw/GLU.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -68,8 +69,8 @@ public static class GLU public static int GlhUnProjectf(float winx, float winy, float winz, float[] modelview, float[] projection, int[] viewport, ref RcVec3f objectCoordinate) { // Transformation matrices - float[] m = new float[16], A = new float[16]; - float[] @in = new float[4], @out = new float[4]; + Span m = stackalloc float[16], A = stackalloc float[16]; + Span @in = stackalloc float[4], @out = stackalloc float[4]; // Calculation for inverting a matrix, compute projection x modelview // and store in A[16] MultiplyMatrices4by4OpenGL_FLOAT(A, projection, modelview); @@ -92,7 +93,7 @@ public static class GLU return 1; } - static void MultiplyMatrices4by4OpenGL_FLOAT(float[] result, float[] matrix1, float[] matrix2) + static void MultiplyMatrices4by4OpenGL_FLOAT(Span result, float[] matrix1, float[] matrix2) { result[0] = matrix1[0] * matrix2[0] + matrix1[4] * matrix2[1] + matrix1[8] * matrix2[2] + matrix1[12] * matrix2[3]; result[4] = matrix1[0] * matrix2[4] + matrix1[4] * matrix2[5] + matrix1[8] * matrix2[6] + matrix1[12] * matrix2[7]; @@ -115,7 +116,7 @@ public static class GLU result[15] = matrix1[3] * matrix2[12] + matrix1[7] * matrix2[13] + matrix1[11] * matrix2[14] + matrix1[15] * matrix2[15]; } - static void MultiplyMatrixByVector4by4OpenGL_FLOAT(float[] resultvector, float[] matrix, float[] pvector) + static void MultiplyMatrixByVector4by4OpenGL_FLOAT(Span resultvector, Span matrix, Span pvector) { resultvector[0] = matrix[0] * pvector[0] + matrix[4] * pvector[1] + matrix[8] * pvector[2] + matrix[12] * pvector[3]; resultvector[1] = matrix[1] * pvector[0] + matrix[5] * pvector[1] + matrix[9] * pvector[2] + matrix[13] * pvector[3]; @@ -124,15 +125,13 @@ public static class GLU } // This code comes directly from GLU except that it is for float - static int GlhInvertMatrixf2(float[] m, float[] @out) + static int GlhInvertMatrixf2(Span m, Span @out) { - float[][] wtmp = RcArrays.Of(4, 8); float m0, m1, m2, m3, s; - float[] r0, r1, r2, r3; - r0 = wtmp[0]; - r1 = wtmp[1]; - r2 = wtmp[2]; - r3 = wtmp[3]; + Span r0 = stackalloc float[8]; + Span r1 = stackalloc float[8]; + Span r2 = stackalloc float[8]; + Span r3 = stackalloc float[8]; r0[0] = MAT(m, 0, 0); r0[1] = MAT(m, 0, 1); r0[2] = MAT(m, 0, 2); @@ -160,27 +159,28 @@ public static class GLU /* choose pivot - or die */ if (MathF.Abs(r3[0]) > MathF.Abs(r2[0])) { - float[] r = r2; + Span r = r2; r2 = r3; r3 = r; } if (MathF.Abs(r2[0]) > MathF.Abs(r1[0])) { - float[] r = r2; + Span r = r2; r2 = r1; r1 = r; } if (MathF.Abs(r1[0]) > MathF.Abs(r0[0])) { - float[] r = r1; + Span r = r1; r1 = r0; r0 = r; } if (0.0 == r0[0]) return 0; + /* eliminate first variable */ m1 = r1[0] / r0[0]; m2 = r2[0] / r0[0]; @@ -232,14 +232,14 @@ public static class GLU /* choose pivot - or die */ if (MathF.Abs(r3[1]) > MathF.Abs(r2[1])) { - float[] r = r2; + Span r = r2; r2 = r3; r3 = r; } if (MathF.Abs(r2[1]) > MathF.Abs(r1[1])) { - float[] r = r2; + Span r = r2; r2 = r1; r1 = r; } @@ -284,7 +284,7 @@ public static class GLU /* choose pivot - or die */ if (MathF.Abs(r3[2]) > MathF.Abs(r2[2])) { - float[] r = r2; + Span r = r2; r2 = r3; r3 = r; } @@ -358,12 +358,12 @@ public static class GLU return 1; } - static float MAT(float[] m, int r, int c) + static float MAT(Span m, int r, int c) { return m[(c) * 4 + (r)]; } - static void MAT(float[] m, int r, int c, float v) + static void MAT(Span m, int r, int c, float v) { m[(c) * 4 + (r)] = v; } diff --git a/src/DotRecast.Recast.Demo/Draw/IOpenGLDraw.cs b/src/DotRecast.Recast.Demo/Draw/IOpenGLDraw.cs index 0558c41..8086962 100644 --- a/src/DotRecast.Recast.Demo/Draw/IOpenGLDraw.cs +++ b/src/DotRecast.Recast.Demo/Draw/IOpenGLDraw.cs @@ -1,3 +1,4 @@ +using System.Numerics; using DotRecast.Core.Numerics; namespace DotRecast.Recast.Demo.Draw; diff --git a/src/DotRecast.Recast.Demo/Draw/ModernOpenGLDraw.cs b/src/DotRecast.Recast.Demo/Draw/ModernOpenGLDraw.cs index 8ca2ed6..d850f9e 100644 --- a/src/DotRecast.Recast.Demo/Draw/ModernOpenGLDraw.cs +++ b/src/DotRecast.Recast.Demo/Draw/ModernOpenGLDraw.cs @@ -1,10 +1,11 @@ using System; -using System.IO; using Silk.NET.OpenGL; using DotRecast.Core.Numerics; namespace DotRecast.Recast.Demo.Draw; +// TODO use a lot of Memory, 2GB+ + public class ModernOpenGLDraw : IOpenGLDraw { private GL _gl; @@ -19,8 +20,8 @@ public class ModernOpenGLDraw : IOpenGLDraw private float fogEnd; private bool fogEnabled; private int uniformViewMatrix; - private readonly ArrayBuffer vertices = new(); - private readonly ArrayBuffer elements = new(); + private readonly ArrayBuffer vertices = new(512); + private readonly ArrayBuffer elements = new(512); private GLCheckerTexture _texture; private readonly float[] _viewMatrix = new float[16]; private readonly float[] _projectionMatrix = new float[16]; @@ -36,36 +37,42 @@ public class ModernOpenGLDraw : IOpenGLDraw public unsafe void Init() { - string SHADER_VERSION = "#version 400\n"; - string vertex_shader = SHADER_VERSION + "uniform mat4 ProjMtx;\n" // - + "uniform mat4 ViewMtx;\n" // - + "in vec3 Position;\n" // - + "in vec2 TexCoord;\n" // - + "in vec4 Color;\n" // - + "out vec2 Frag_UV;\n" // - + "out vec4 Frag_Color;\n" // - + "out float Frag_Depth;\n" // - + "void main() {\n" // - + " Frag_UV = TexCoord;\n" // - + " Frag_Color = Color;\n" // - + " vec4 VSPosition = ViewMtx * vec4(Position, 1);\n" // - + " Frag_Depth = -VSPosition.z;\n" // - + " gl_Position = ProjMtx * VSPosition;\n" // - + "}\n"; - string fragment_shader = SHADER_VERSION + "precision mediump float;\n" // - + "uniform sampler2D Texture;\n" // - + "uniform float UseTexture;\n" // - + "uniform float EnableFog;\n" // - + "uniform float FogStart;\n" // - + "uniform float FogEnd;\n" // - + "const vec4 FogColor = vec4(0.3f, 0.3f, 0.32f, 1.0f);\n" // - + "in vec2 Frag_UV;\n" // - + "in vec4 Frag_Color;\n" // - + "in float Frag_Depth;\n" // - + "out vec4 Out_Color;\n" // - + "void main(){\n" // - + " Out_Color = mix(FogColor, Frag_Color * mix(vec4(1), texture(Texture, Frag_UV.st), UseTexture), 1.0 - EnableFog * clamp( (Frag_Depth - FogStart) / (FogEnd - FogStart), 0.0, 1.0) );\n" // - + "}\n"; + const string SHADER_VERSION = "#version 400\n"; + const string vertex_shader = $@" +{SHADER_VERSION} +uniform mat4 ProjMtx; +uniform mat4 ViewMtx; +in vec3 Position; +in vec2 TexCoord; +in vec4 Color; +out vec2 Frag_UV; +out vec4 Frag_Color; +out float Frag_Depth; +void main() {{ + Frag_UV = TexCoord; + Frag_Color = Color; + vec4 VSPosition = ViewMtx * vec4(Position, 1); + Frag_Depth = -VSPosition.z; + gl_Position = ProjMtx * VSPosition; +}} +"; + const string fragment_shader = $@" +{SHADER_VERSION} +precision mediump float; +uniform sampler2D Texture; +uniform float UseTexture; +uniform float EnableFog; +uniform float FogStart; +uniform float FogEnd; +const vec4 FogColor = vec4(0.3f, 0.3f, 0.32f, 1.0f); +in vec2 Frag_UV; +in vec4 Frag_Color; +in float Frag_Depth; +out vec4 Out_Color; +void main(){{ + Out_Color = mix(FogColor, Frag_Color * mix(vec4(1), texture(Texture, Frag_UV.st), UseTexture), 1.0 - EnableFog * clamp( (Frag_Depth - FogStart) / (FogEnd - FogStart), 0.0, 1.0) ); +}} +"; program = _gl.CreateProgram(); uint vert_shdr = _gl.CreateShader(GLEnum.VertexShader); @@ -150,6 +157,10 @@ public class ModernOpenGLDraw : IOpenGLDraw _gl.BindVertexArray(0); _gl.BindBuffer(GLEnum.ArrayBuffer, 0); _gl.BindBuffer(GLEnum.ElementArrayBuffer, 0); + + //int* range = stackalloc int[2]; + //_gl.GetInteger(GetPName.LineWidthRange, range); + //Console.WriteLine($"\nLineWidthRange: {range[0]} {range[1]}"); } public void Clear() @@ -191,53 +202,30 @@ public class ModernOpenGLDraw : IOpenGLDraw // GlBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_BUFFER, GL_STREAM_DRAW); // GlBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_BUFFER, GL_STREAM_DRAW); - uint vboSize = (uint)vertices.Count * 24; - uint eboSize = currentPrim == DebugDrawPrimitives.QUADS ? (uint)vertices.Count * 6 : (uint)vertices.Count * 4; - - _gl.BufferData(GLEnum.ArrayBuffer, vboSize, null, GLEnum.StreamDraw); - _gl.BufferData(GLEnum.ElementArrayBuffer, eboSize, null, GLEnum.StreamDraw); - // load draw vertices & elements directly into vertex + element buffer + _gl.BufferData(GLEnum.ArrayBuffer, vertices.AsArray(), GLEnum.DynamicDraw); + if (currentPrim == DebugDrawPrimitives.QUADS) { - byte* pVerts = (byte*)_gl.MapBuffer(GLEnum.ArrayBuffer, GLEnum.WriteOnly); - byte* pElems = (byte*)_gl.MapBuffer(GLEnum.ElementArrayBuffer, GLEnum.WriteOnly); - - //vertices.ForEach(v => v.Store(verts)); - fixed (void* v = vertices.AsArray()) + for (int i = 0; i < vertices.Count; i += 4) { - System.Buffer.MemoryCopy(v, pVerts, vboSize, vboSize); + elements.Add(i); + elements.Add(i + 1); + elements.Add(i + 2); + elements.Add(i); + elements.Add(i + 2); + elements.Add(i + 3); } - - if (currentPrim == DebugDrawPrimitives.QUADS) - { - using var unmanagedElems = new UnmanagedMemoryStream(pElems, eboSize, eboSize, FileAccess.Write); - using var bw = new BinaryWriter(unmanagedElems); - for (int i = 0; i < vertices.Count; i += 4) - { - bw.Write(i); - bw.Write(i + 1); - bw.Write(i + 2); - bw.Write(i); - bw.Write(i + 2); - bw.Write(i + 3); - } - } - else - { - for (int i = elements.Count; i < vertices.Count; i++) - { - elements.Add(i); - } - - fixed (void* e = elements.AsArray()) - { - System.Buffer.MemoryCopy(e, pElems, eboSize, eboSize); - } - } - - _gl.UnmapBuffer(GLEnum.ElementArrayBuffer); - _gl.UnmapBuffer(GLEnum.ArrayBuffer); } + else + { + for (int i = elements.Count; i < vertices.Count; i++) + { + elements.Add(i); + } + } + + _gl.BufferData(GLEnum.ElementArrayBuffer, elements.AsArray(), GLEnum.DynamicDraw); + if (_texture != null) { _texture.Bind(); @@ -271,15 +259,22 @@ public class ModernOpenGLDraw : IOpenGLDraw _gl.BindBuffer(GLEnum.ArrayBuffer, 0); _gl.BindBuffer(GLEnum.ElementArrayBuffer, 0); vertices.Clear(); + elements.Clear(); _gl.LineWidth(1.0f); _gl.PointSize(1.0f); } + public void Vertex(float x, float y, float z, int color) { vertices.Add(new OpenGLVertex(x, y, z, color)); } + public unsafe void Vertex(float* pos, int color) + { + vertices.Add(new OpenGLVertex(pos[0], pos[1], pos[2], color)); + } + public void Vertex(float[] pos, int color) { vertices.Add(new OpenGLVertex(pos, color)); diff --git a/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs b/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs index 14562b0..c6b9dec 100644 --- a/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs +++ b/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,14 +23,14 @@ using DotRecast.Core.Numerics; using DotRecast.Detour; using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Toolset.Geom; -using static DotRecast.Recast.RcCommons; +using static DotRecast.Recast.RcRecast; namespace DotRecast.Recast.Demo.Draw; public class NavMeshRenderer { private readonly RecastDebugDraw _debugDraw; - private readonly int _navMeshDrawFlags = RecastDebugDraw.DRAWNAVMESH_OFFMESHCONS | RecastDebugDraw.DRAWNAVMESH_CLOSEDLIST; + private readonly int _navMeshDrawFlags = RecastDebugDraw.DU_DRAWNAVMESH_OFFMESHCONS | RecastDebugDraw.DU_DRAWNAVMESH_CLOSEDLIST; public NavMeshRenderer(RecastDebugDraw debugDraw) { diff --git a/src/DotRecast.Recast.Demo/Draw/RecastDebugDraw.cs b/src/DotRecast.Recast.Demo/Draw/RecastDebugDraw.cs index 62ff866..b20fdac 100644 --- a/src/DotRecast.Recast.Demo/Draw/RecastDebugDraw.cs +++ b/src/DotRecast.Recast.Demo/Draw/RecastDebugDraw.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -19,7 +19,6 @@ freely, subject to the following restrictions: */ using System; -using System.Collections.Generic; using DotRecast.Core.Numerics; using DotRecast.Detour; using DotRecast.Recast.Toolset.Builder; @@ -27,11 +26,13 @@ using Silk.NET.OpenGL; namespace DotRecast.Recast.Demo.Draw; +using static DtDetour; + public class RecastDebugDraw : DebugDraw { - public static readonly int DRAWNAVMESH_OFFMESHCONS = 0x01; - public static readonly int DRAWNAVMESH_CLOSEDLIST = 0x02; - public static readonly int DRAWNAVMESH_COLOR_TILES = 0x04; + public const int DU_DRAWNAVMESH_OFFMESHCONS = 0x01; + public const int DU_DRAWNAVMESH_CLOSEDLIST = 0x02; + public const int DU_DRAWNAVMESH_COLOR_TILES = 0x04; public RecastDebugDraw(GL gl) : base(gl) { @@ -101,7 +102,7 @@ public class RecastDebugDraw : DebugDraw public void DebugDrawNavMeshWithClosedList(DtNavMesh mesh, DtNavMeshQuery query, int flags) { - DtNavMeshQuery q = (flags & DRAWNAVMESH_CLOSEDLIST) != 0 ? query : null; + DtNavMeshQuery q = (flags & DU_DRAWNAVMESH_CLOSEDLIST) != 0 ? query : null; for (int i = 0; i < mesh.GetMaxTiles(); ++i) { DtMeshTile tile = mesh.GetTile(i); @@ -116,7 +117,7 @@ public class RecastDebugDraw : DebugDraw { long @base = mesh.GetPolyRefBase(tile); - int tileNum = DtNavMesh.DecodePolyIdTile(@base); + int tileNum = DecodePolyIdTile(@base); int tileColor = DuIntToCol(tileNum, 128); DepthMask(false); Begin(DebugDrawPrimitives.TRIS); @@ -135,7 +136,7 @@ public class RecastDebugDraw : DebugDraw } else { - if ((flags & DRAWNAVMESH_COLOR_TILES) != 0) + if ((flags & DU_DRAWNAVMESH_COLOR_TILES) != 0) { col = tileColor; } @@ -163,7 +164,7 @@ public class RecastDebugDraw : DebugDraw // Draw outer poly boundaries DrawPolyBoundaries(tile, DuRGBA(0, 48, 64, 220), 2.5f, false); - if ((flags & DRAWNAVMESH_OFFMESHCONS) != 0) + if ((flags & DU_DRAWNAVMESH_OFFMESHCONS) != 0) { Begin(DebugDrawPrimitives.LINES, 2.0f); for (int i = 0; i < tile.data.header.polyCount; ++i) @@ -198,7 +199,7 @@ public class RecastDebugDraw : DebugDraw // Check to see if start and end end-points have links. bool startSet = false; bool endSet = false; - for (int k = tile.polyLinks[p.index]; k != DtNavMesh.DT_NULL_LINK; k = tile.links[k].next) + for (int k = p.firstLink; k != DT_NULL_LINK; k = tile.links[k].next) { if (tile.links[k].edge == 0) { @@ -299,6 +300,7 @@ public class RecastDebugDraw : DebugDraw Begin(DebugDrawPrimitives.LINES, linew); + Span tv = stackalloc RcVec3f[3]; for (int i = 0; i < tile.data.header.polyCount; ++i) { DtPoly p = tile.data.polys[i]; @@ -318,10 +320,10 @@ public class RecastDebugDraw : DebugDraw continue; } - if ((p.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) + if ((p.neis[j] & DT_EXT_LINK) != 0) { bool con = false; - for (int k = tile.polyLinks[p.index]; k != DtNavMesh.DT_NULL_LINK; k = tile.links[k].next) + for (int k = p.firstLink; k != DT_NULL_LINK; k = tile.links[k].next) { if (tile.links[k].edge == j) { @@ -370,14 +372,14 @@ public class RecastDebugDraw : DebugDraw for (int k = 0; k < pd.triCount; ++k) { int t = (pd.triBase + k) * 4; - RcVec3f[] tv = new RcVec3f[3]; for (int m = 0; m < 3; ++m) { int v = tile.data.detailTris[t + m]; if (v < p.vertCount) { tv[m] = new RcVec3f( - tile.data.verts[p.verts[v] * 3], tile.data.verts[p.verts[v] * 3 + 1], + tile.data.verts[p.verts[v] * 3], + tile.data.verts[p.verts[v] * 3 + 1], tile.data.verts[p.verts[v] * 3 + 2] ); } @@ -393,7 +395,7 @@ public class RecastDebugDraw : DebugDraw for (int m = 0, n = 2; m < 3; n = m++) { - if ((DtNavMesh.GetDetailTriEdgeFlags(tile.data.detailTris[t + 3], n) & DtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY) == 0) + if ((GetDetailTriEdgeFlags(tile.data.detailTris[t + 3], n) & DtDetailTriEdgeFlags.DT_DETAIL_EDGE_BOUNDARY) == 0) continue; if (((tile.data.detailTris[t + 3] >> (n * 2)) & 0x3) == 0) @@ -463,9 +465,13 @@ public class RecastDebugDraw : DebugDraw continue; } - AppendBoxWire(tile.data.header.bmin.X + n.bmin[0] * cs, tile.data.header.bmin.Y + n.bmin[1] * cs, - tile.data.header.bmin.Z + n.bmin[2] * cs, tile.data.header.bmin.X + n.bmax[0] * cs, - tile.data.header.bmin.Y + n.bmax[1] * cs, tile.data.header.bmin.Z + n.bmax[2] * cs, + AppendBoxWire( + tile.data.header.bmin.X + n.bmin.X * cs, + tile.data.header.bmin.Y + n.bmin.Y * cs, + tile.data.header.bmin.Z + n.bmin.Z * cs, + tile.data.header.bmin.X + n.bmax.X * cs, + tile.data.header.bmin.Y + n.bmax.Y * cs, + tile.data.header.bmin.Z + n.bmax.Z * cs, DuRGBA(255, 255, 255, 128)); } @@ -497,7 +503,7 @@ public class RecastDebugDraw : DebugDraw { color = DuRGBA(0, 192, 255, 64); } - else if (area == RcConstants.RC_NULL_AREA) + else if (area == RcRecast.RC_NULL_AREA) { color = DuRGBA(0, 0, 0, 64); } @@ -669,7 +675,7 @@ public class RecastDebugDraw : DebugDraw int v3 = c.rverts[j * 4 + 3]; float off = 0; int colv = color; - if ((v3 & RcConstants.RC_BORDER_VERTEX) != 0) + if ((v3 & RcRecast.RC_BORDER_VERTEX) != 0) { colv = DuRGBA(255, 255, 255, a); off = ch * 2; @@ -716,7 +722,7 @@ public class RecastDebugDraw : DebugDraw int vb0 = c.verts[j * 4]; int vb1 = c.verts[j * 4 + 1]; int vb2 = c.verts[j * 4 + 2]; - int col = (va3 & RcConstants.RC_AREA_BORDER) != 0 ? bcolor : color; + int col = (va3 & RcRecast.RC_AREA_BORDER) != 0 ? bcolor : color; float fx = orig.X + va0 * cs; float fy = orig.Y + (va1 + 1 + (i & 1)) * ch; @@ -747,7 +753,7 @@ public class RecastDebugDraw : DebugDraw int v3 = c.verts[j * 4 + 3]; float off = 0; int colv = color; - if ((v3 & RcConstants.RC_BORDER_VERTEX) != 0) + if ((v3 & RcRecast.RC_BORDER_VERTEX) != 0) { colv = DuRGBA(255, 255, 255, a); off = ch * 2; @@ -827,7 +833,7 @@ public class RecastDebugDraw : DebugDraw { fcol[0] = DuRGBA(64, 128, 160, 255); } - else if (s.area == RcConstants.RC_NULL_AREA) + else if (s.area == RcRecast.RC_NULL_AREA) { fcol[0] = DuRGBA(64, 64, 64, 255); } @@ -949,7 +955,7 @@ public class RecastDebugDraw : DebugDraw { color = DuRGBA(0, 192, 255, 64); } - else if (area == RcConstants.RC_NULL_AREA) + else if (area == RcRecast.RC_NULL_AREA) { color = DuRGBA(0, 0, 0, 64); } @@ -961,7 +967,7 @@ public class RecastDebugDraw : DebugDraw int[] vi = new int[3]; for (int j = 2; j < nvp; ++j) { - if (mesh.polys[p + j] == RcConstants.RC_MESH_NULL_IDX) + if (mesh.polys[p + j] == RcRecast.RC_MESH_NULL_IDX) { break; } @@ -992,7 +998,7 @@ public class RecastDebugDraw : DebugDraw int p = i * nvp * 2; for (int j = 0; j < nvp; ++j) { - if (mesh.polys[p + j] == RcConstants.RC_MESH_NULL_IDX) + if (mesh.polys[p + j] == RcRecast.RC_MESH_NULL_IDX) { break; } @@ -1002,7 +1008,7 @@ public class RecastDebugDraw : DebugDraw continue; } - int nj = (j + 1 >= nvp || mesh.polys[p + j + 1] == RcConstants.RC_MESH_NULL_IDX) ? 0 : j + 1; + int nj = (j + 1 >= nvp || mesh.polys[p + j + 1] == RcRecast.RC_MESH_NULL_IDX) ? 0 : j + 1; int[] vi = { mesh.polys[p + j], mesh.polys[p + nj] }; for (int k = 0; k < 2; ++k) @@ -1026,7 +1032,7 @@ public class RecastDebugDraw : DebugDraw int p = i * nvp * 2; for (int j = 0; j < nvp; ++j) { - if (mesh.polys[p + j] == RcConstants.RC_MESH_NULL_IDX) + if (mesh.polys[p + j] == RcRecast.RC_MESH_NULL_IDX) { break; } @@ -1036,7 +1042,7 @@ public class RecastDebugDraw : DebugDraw continue; } - int nj = (j + 1 >= nvp || mesh.polys[p + j + 1] == RcConstants.RC_MESH_NULL_IDX) ? 0 : j + 1; + int nj = (j + 1 >= nvp || mesh.polys[p + j + 1] == RcRecast.RC_MESH_NULL_IDX) ? 0 : j + 1; int[] vi = { mesh.polys[p + j], mesh.polys[p + nj] }; int col = colb; @@ -1327,7 +1333,7 @@ public class RecastDebugDraw : DebugDraw for (int side = 0; side < 8; ++side) { - int m = DtNavMesh.DT_EXT_LINK | (short)side; + int m = DT_EXT_LINK | (short)side; for (int i = 0; i < tile.data.header.polyCount; ++i) { diff --git a/src/DotRecast.Recast.Demo/DtVoxelTileLZ4DemoCompressor.cs b/src/DotRecast.Recast.Demo/DtVoxelTileLZ4DemoCompressor.cs index 8085dfb..a2c4b50 100644 --- a/src/DotRecast.Recast.Demo/DtVoxelTileLZ4DemoCompressor.cs +++ b/src/DotRecast.Recast.Demo/DtVoxelTileLZ4DemoCompressor.cs @@ -1,4 +1,4 @@ -using System; +using System; using DotRecast.Core; using K4os.Compression.LZ4; diff --git a/src/DotRecast.Recast.Demo/KeyModState.cs b/src/DotRecast.Recast.Demo/KeyModState.cs index c0af30b..220dec6 100644 --- a/src/DotRecast.Recast.Demo/KeyModState.cs +++ b/src/DotRecast.Recast.Demo/KeyModState.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo; +namespace DotRecast.Recast.Demo; public static class KeyModState { diff --git a/src/DotRecast.Recast.Demo/Messages/GeomLoadBeganEvent.cs b/src/DotRecast.Recast.Demo/Messages/GeomLoadBeganEvent.cs index c4fffb8..901659e 100644 --- a/src/DotRecast.Recast.Demo/Messages/GeomLoadBeganEvent.cs +++ b/src/DotRecast.Recast.Demo/Messages/GeomLoadBeganEvent.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public class GeomLoadBeganEvent : IRecastDemoMessage { diff --git a/src/DotRecast.Recast.Demo/Messages/IRecastDemoChannel.cs b/src/DotRecast.Recast.Demo/Messages/IRecastDemoChannel.cs index 677f2f0..a4d25ae 100644 --- a/src/DotRecast.Recast.Demo/Messages/IRecastDemoChannel.cs +++ b/src/DotRecast.Recast.Demo/Messages/IRecastDemoChannel.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public interface IRecastDemoChannel { diff --git a/src/DotRecast.Recast.Demo/Messages/IRecastDemoMessage.cs b/src/DotRecast.Recast.Demo/Messages/IRecastDemoMessage.cs index 810ffc9..9c62a7d 100644 --- a/src/DotRecast.Recast.Demo/Messages/IRecastDemoMessage.cs +++ b/src/DotRecast.Recast.Demo/Messages/IRecastDemoMessage.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public class IRecastDemoMessage { diff --git a/src/DotRecast.Recast.Demo/Messages/NavMeshBuildBeganEvent.cs b/src/DotRecast.Recast.Demo/Messages/NavMeshBuildBeganEvent.cs index 2388c72..c321a26 100644 --- a/src/DotRecast.Recast.Demo/Messages/NavMeshBuildBeganEvent.cs +++ b/src/DotRecast.Recast.Demo/Messages/NavMeshBuildBeganEvent.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public class NavMeshBuildBeganEvent : IRecastDemoMessage { diff --git a/src/DotRecast.Recast.Demo/Messages/NavMeshLoadBeganEvent.cs b/src/DotRecast.Recast.Demo/Messages/NavMeshLoadBeganEvent.cs index e549788..4bd41f6 100644 --- a/src/DotRecast.Recast.Demo/Messages/NavMeshLoadBeganEvent.cs +++ b/src/DotRecast.Recast.Demo/Messages/NavMeshLoadBeganEvent.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public class NavMeshLoadBeganEvent : IRecastDemoMessage { diff --git a/src/DotRecast.Recast.Demo/Messages/NavMeshSaveBeganEvent.cs b/src/DotRecast.Recast.Demo/Messages/NavMeshSaveBeganEvent.cs index 1f32146..7f34e4a 100644 --- a/src/DotRecast.Recast.Demo/Messages/NavMeshSaveBeganEvent.cs +++ b/src/DotRecast.Recast.Demo/Messages/NavMeshSaveBeganEvent.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.Messages; +namespace DotRecast.Recast.Demo.Messages; public class NavMeshSaveBeganEvent : IRecastDemoMessage { diff --git a/src/DotRecast.Recast.Demo/Messages/RaycastEvent.cs b/src/DotRecast.Recast.Demo/Messages/RaycastEvent.cs index da45ce3..cf64ece 100644 --- a/src/DotRecast.Recast.Demo/Messages/RaycastEvent.cs +++ b/src/DotRecast.Recast.Demo/Messages/RaycastEvent.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Demo.Messages; diff --git a/src/DotRecast.Recast.Demo/Program.cs b/src/DotRecast.Recast.Demo/Program.cs index b2696b7..adab02a 100644 --- a/src/DotRecast.Recast.Demo/Program.cs +++ b/src/DotRecast.Recast.Demo/Program.cs @@ -1,8 +1,8 @@ -using System.IO; +using System.IO; +using System.Threading; using DotRecast.Core; using DotRecast.Recast.Demo.Logging.Sinks; using Serilog; -using Serilog.Enrichers; namespace DotRecast.Recast.Demo; @@ -10,6 +10,8 @@ public static class Program { public static void Main(string[] args) { + Thread.CurrentThread.Name ??= "main"; + InitializeWorkingDirectory(); InitializeLogger(); StartDemo(); @@ -22,7 +24,6 @@ public static class Program .MinimumLevel.Verbose() .Enrich.WithThreadId() .Enrich.WithThreadName() - .Enrich.WithProperty(ThreadNameEnricher.ThreadNamePropertyName, "main") .WriteTo.Async(c => c.LogMessageBroker(outputTemplate: format)) .WriteTo.Async(c => c.Console(outputTemplate: format)) .WriteTo.Async(c => c.File( diff --git a/src/DotRecast.Recast.Demo/RecastDemo.cs b/src/DotRecast.Recast.Demo/RecastDemo.cs index fbddfe9..e5d42df 100644 --- a/src/DotRecast.Recast.Demo/RecastDemo.cs +++ b/src/DotRecast.Recast.Demo/RecastDemo.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,6 +25,8 @@ using System.Globalization; using System.IO; using System.Linq; using System.Numerics; +using System.Runtime; +using System.Runtime.InteropServices; using DotRecast.Core; using Serilog; using Silk.NET.Input; @@ -318,7 +320,7 @@ public class RecastDemo : IRecastDemoChannel if (null != mesh) { - _sample.Update(_sample.GetInputGeom(), ImmutableArray.Empty, mesh); + _sample.Update(_sample.GetInputGeom(), _sample.GetRecastConfig(), ImmutableArray.Empty, mesh); _toolsetView.SetEnabled(true); } } @@ -409,9 +411,17 @@ public class RecastDemo : IRecastDemoChannel var workingDirectory = Directory.GetCurrentDirectory(); Logger.Information($"Working directory - {workingDirectory}"); - Logger.Information($"ImGui.Net - version({ImGui.GetVersion()}) UI scale({scale}) fontSize({fontSize})"); - Logger.Information($"Dotnet - {Environment.Version.ToString()} culture({currentCulture.Name})"); Logger.Information($"OS Version - {Environment.OSVersion} {bitness}"); + Logger.Information($"{RuntimeInformation.OSArchitecture} {RuntimeInformation.OSDescription}"); + Logger.Information($"{RuntimeInformation.ProcessArchitecture} {RuntimeInformation.FrameworkDescription}"); + Logger.Information($"Dotnet - {Environment.Version.ToString()} culture({currentCulture.Name})"); + Logger.Information($"Processor Count : {Environment.ProcessorCount}"); + + Logger.Information($"Server garbage collection : {(GCSettings.IsServerGC ? "Enabled" : "Disabled")}"); + Logger.Information($"Current latency mode for garbage collection: {GCSettings.LatencyMode}"); + Logger.Information(""); + + Logger.Information($"ImGui.Net - version({ImGui.GetVersion()}) UI scale({scale}) fontSize({fontSize})"); Logger.Information($"{vendor} {rendererGl}"); Logger.Information($"gl version({version}) lang version({glslString})"); } @@ -462,7 +472,7 @@ public class RecastDemo : IRecastDemoChannel var settings = _sample.GetSettings(); RcVec3f bmin = _sample.GetInputGeom().GetMeshBoundsMin(); RcVec3f bmax = _sample.GetInputGeom().GetMeshBoundsMax(); - RcCommons.CalcGridSize(bmin, bmax, settings.cellSize, out var gw, out var gh); + RcRecast.CalcGridSize(bmin, bmax, settings.cellSize, out var gw, out var gh); settingsView.SetVoxels(gw, gh); settingsView.SetTiles(tileNavMeshBuilder.GetTiles(_sample.GetInputGeom(), settings.cellSize, settings.tileSize)); settingsView.SetMaxTiles(tileNavMeshBuilder.GetMaxTiles(_sample.GetInputGeom(), settings.cellSize, settings.tileSize)); @@ -674,7 +684,7 @@ public class RecastDemo : IRecastDemoChannel { var geom = LoadInputMesh(args.FilePath); - _sample.Update(geom, ImmutableArray.Empty, null); + _sample.Update(geom, null, ImmutableArray.Empty, null); } private void OnNavMeshBuildBegan(NavMeshBuildBeganEvent args) @@ -709,7 +719,7 @@ public class RecastDemo : IRecastDemoChannel return; } - _sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh); + _sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh); _sample.SetChanged(false); settingsView.SetBuildTime((RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond); //settingsUI.SetBuildTelemetry(buildResult.Item1.Select(x => x.GetTelemetry()).ToList()); diff --git a/src/DotRecast.Recast.Demo/Tools/ConvexVolumeSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/ConvexVolumeSampleTool.cs index 48d7317..1ed196f 100644 --- a/src/DotRecast.Recast.Demo/Tools/ConvexVolumeSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/ConvexVolumeSampleTool.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Tools/CrowdAgentProfilingSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/CrowdAgentProfilingSampleTool.cs index b682662..0adef28 100644 --- a/src/DotRecast.Recast.Demo/Tools/CrowdAgentProfilingSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/CrowdAgentProfilingSampleTool.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -18,12 +18,9 @@ freely, subject to the following restrictions: */ using System; -using System.Collections.Generic; -using System.Linq; using DotRecast.Core.Numerics; using DotRecast.Detour; using DotRecast.Detour.Crowd; -using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset.Tools; @@ -98,6 +95,11 @@ public class CrowdAgentProfilingSampleTool : ISampleTool ImGui.SliderInt("Max Iterations", ref toolCfg.maxIterations, 0, 4000); ImGui.NewLine(); + ImGui.Text("Debug Draw"); + ImGui.Separator(); + ImGui.Checkbox("Show Agents", ref toolCfg.showAgents); + ImGui.NewLine(); + if (ImGui.Button("Start Crowd Profiling")) { var settings = _sample.GetSettings(); @@ -118,11 +120,11 @@ public class CrowdAgentProfilingSampleTool : ISampleTool ImGui.Text($"{rtt.Key}: {rtt.Micros} us"); } - ImGui.Text($"Sampling Time: {_tool.GetCrowdUpdateSamplingTime()} ms"); - ImGui.Text($"Current Update Time: {_tool.GetCrowdUpdateTime()} ms"); - ImGui.Text($"Avg Update Time: {_tool.GetCrowdUpdateAvgTime()} ms"); - ImGui.Text($"Max Update Time: {_tool.GetCrowdUpdateMaxTime()} ms"); - ImGui.Text($"Min Update Time: {_tool.GetCrowdUpdateMinTime()} ms"); + ImGui.Text($"Sampling Time: {_tool.GetCrowdUpdateSamplingTime():0.00} ms"); + ImGui.Text($"Current Update Time: {_tool.GetCrowdUpdateTime():0.00} ms"); + ImGui.Text($"Avg Update Time: {_tool.GetCrowdUpdateAvgTime():0.00} ms"); + ImGui.Text($"Max Update Time: {_tool.GetCrowdUpdateMaxTime():0.00} ms"); + ImGui.Text($"Min Update Time: {_tool.GetCrowdUpdateMinTime():0.00} ms"); } } @@ -132,7 +134,7 @@ public class CrowdAgentProfilingSampleTool : ISampleTool dd.DepthMask(false); var crowd = _tool.GetCrowd(); - if (crowd != null) + if (crowd != null && _tool.GetToolConfig().showAgents) { foreach (DtCrowdAgent ag in crowd.GetActiveAgents()) { diff --git a/src/DotRecast.Recast.Demo/Tools/CrowdSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/CrowdSampleTool.cs index e33ec2c..2100bd8 100644 --- a/src/DotRecast.Recast.Demo/Tools/CrowdSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/CrowdSampleTool.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -234,7 +234,7 @@ public class CrowdSampleTool : ISampleTool dd.Vertex(prev.X, prev.Y + 0.1f, prev.Z, DuRGBA(0, 0, 0, (int)(128 * preva))); dd.Vertex(trail.trail[v], trail.trail[v + 1] + 0.1f, trail.trail[v + 2], DuRGBA(0, 0, 0, (int)(128 * a))); preva = a; - prev = RcVecUtils.Create(trail.trail, v); + prev = RcVec.Create(trail.trail, v); } dd.End(); @@ -251,10 +251,10 @@ public class CrowdSampleTool : ISampleTool if (_showCorners) { - if (0 < ag.corners.Count) + if (0 < ag.ncorners) { dd.Begin(LINES, 2.0f); - for (int j = 0; j < ag.corners.Count; ++j) + for (int j = 0; j < ag.ncorners; ++j) { RcVec3f va = j == 0 ? pos : ag.corners[j - 1].pos; RcVec3f vb = ag.corners[j].pos; @@ -262,10 +262,10 @@ public class CrowdSampleTool : ISampleTool dd.Vertex(vb.X, vb.Y + radius, vb.Z, DuRGBA(128, 0, 0, 192)); } - if ((ag.corners[ag.corners.Count - 1].flags + if ((ag.corners[ag.ncorners - 1].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) { - RcVec3f v = ag.corners[ag.corners.Count - 1].pos; + RcVec3f v = ag.corners[ag.ncorners - 1].pos; dd.Vertex(v.X, v.Y, v.Z, DuRGBA(192, 0, 0, 192)); dd.Vertex(v.X, v.Y + radius * 2, v.Z, DuRGBA(192, 0, 0, 192)); } @@ -325,7 +325,7 @@ public class CrowdSampleTool : ISampleTool 2.0f); dd.Begin(LINES, 2.0f); - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; if (nei != null) diff --git a/src/DotRecast.Recast.Demo/Tools/DynamicUpdateSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/DynamicUpdateSampleTool.cs index 4d6c3d6..c3d33cf 100644 --- a/src/DotRecast.Recast.Demo/Tools/DynamicUpdateSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/DynamicUpdateSampleTool.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -25,6 +25,7 @@ using DotRecast.Core; using DotRecast.Core.Collections; using DotRecast.Core.Numerics; using DotRecast.Detour.Dynamic; +using DotRecast.Detour.Dynamic.Io; using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset.Tools; using DotRecast.Recast.Demo.Draw; @@ -116,8 +117,17 @@ public class DynamicUpdateSampleTool : ISampleTool if (mode == RcDynamicUpdateToolMode.BUILD) { - var loadVoxelPopupStrId = "Load Voxels Popup"; + const string loadVoxelPopupStrId = "Load Voxels Popup"; + bool isLoadVoxelPopup = true; + if (_sample.GetRecastResults() != null && _sample.GetRecastConfig() != null) + { + if (ImGui.Button("Import Voxels")) + { + Copy(); + } + } + if (ImGui.Button("Load Voxels...")) { ImGui.OpenPopup(loadVoxelPopupStrId); @@ -135,7 +145,7 @@ public class DynamicUpdateSampleTool : ISampleTool ImGui.EndPopup(); } - var saveVoxelPopupStrId = "Save Voxels Popup"; + const string saveVoxelPopupStrId = "Save Voxels Popup"; bool isSaveVoxelPopup = true; var dynaMesh = _tool.GetDynamicNavMesh(); @@ -144,7 +154,7 @@ public class DynamicUpdateSampleTool : ISampleTool ImGui.Checkbox("Compression", ref compression); if (ImGui.Button("Save Voxels...")) { - ImGui.BeginPopup(saveVoxelPopupStrId); + ImGui.OpenPopup(saveVoxelPopupStrId); } if (ImGui.BeginPopupModal(saveVoxelPopupStrId, ref isSaveVoxelPopup, ImGuiWindowFlags.NoTitleBar)) @@ -152,9 +162,7 @@ public class DynamicUpdateSampleTool : ISampleTool var picker = ImFilePicker.GetFilePicker(saveVoxelPopupStrId, Path.Combine(Environment.CurrentDirectory), ".voxels"); if (picker.Draw()) { - if (string.IsNullOrEmpty(picker.SelectedFile)) - Save(picker.SelectedFile); - + Save(picker.SelectedFile); ImFilePicker.RemoveFilePicker(saveVoxelPopupStrId); } @@ -411,7 +419,7 @@ public class DynamicUpdateSampleTool : ISampleTool { buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond; var dynaMesh = _tool.GetDynamicNavMesh(); - _sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh()); + _sample.Update(null, null, dynaMesh.RecastResults(), dynaMesh.NavMesh()); _sample.SetChanged(false); } } @@ -421,6 +429,15 @@ public class DynamicUpdateSampleTool : ISampleTool } } + private void Copy() + { + if (_sample.GetRecastResults() != null && _sample.GetRecastConfig() != null) + { + var dynaMesh = _tool.Copy(_sample.GetRecastConfig(), _sample.GetRecastResults()); + UpdateFrom(dynaMesh.config); + BuildDynaMesh(); + } + } private void Load(string filename) { @@ -458,7 +475,7 @@ public class DynamicUpdateSampleTool : ISampleTool } buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond; - _sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh()); + _sample.Update(null, null, dynaMesh.RecastResults(), dynaMesh.NavMesh()); } private void UpdateTo(DtDynamicNavMeshConfig config) diff --git a/src/DotRecast.Recast.Demo/Tools/GizmoRenderer.cs b/src/DotRecast.Recast.Demo/Tools/GizmoRenderer.cs index 2b8cf87..3d118d1 100644 --- a/src/DotRecast.Recast.Demo/Tools/GizmoRenderer.cs +++ b/src/DotRecast.Recast.Demo/Tools/GizmoRenderer.cs @@ -1,4 +1,4 @@ -using System; +using System; using DotRecast.Core.Collections; using DotRecast.Core.Numerics; using DotRecast.Recast.Demo.Draw; @@ -36,16 +36,11 @@ public static class GizmoRenderer } } - public static int GetColorByNormal(float[] vertices, int v0, int v1, int v2) + public static int GetColorByNormal(RcVec3f v0, RcVec3f v1, RcVec3f v2) { - RcVec3f e0 = new RcVec3f(); - RcVec3f e1 = new RcVec3f(); RcVec3f normal = new RcVec3f(); - for (int j = 0; j < 3; ++j) - { - e0 = RcVecUtils.Subtract(vertices, v1, v0); - e1 = RcVecUtils.Subtract(vertices, v2, v0); - } + RcVec3f e0 = v1 - v0; + RcVec3f e1 = v2 - v0; normal.X = e0.Y * e1.Z - e0.Z * e1.Y; normal.Y = e0.Z * e1.X - e0.X * e1.Z; @@ -65,7 +60,7 @@ public static class GizmoRenderer var trX = new RcVec3f(box.halfEdges[0].X, box.halfEdges[1].X, box.halfEdges[2].X); var trY = new RcVec3f(box.halfEdges[0].Y, box.halfEdges[1].Y, box.halfEdges[2].Y); var trZ = new RcVec3f(box.halfEdges[0].Z, box.halfEdges[1].Z, box.halfEdges[2].Z); - float[] vertices = new float[8 * 3]; + Span vertices = stackalloc float[8 * 3]; for (int i = 0; i < 8; i++) { vertices[i * 3 + 0] = RcVec3f.Dot(RcBoxGizmo.VERTS[i], trX) + box.center.X; @@ -160,13 +155,13 @@ public static class GizmoRenderer debugDraw.Begin(DebugDrawPrimitives.TRIS); for (int i = 0; i < trimesh.triangles.Length; i += 3) { - int v0 = 3 * trimesh.triangles[i]; - int v1 = 3 * trimesh.triangles[i + 1]; - int v2 = 3 * trimesh.triangles[i + 2]; - int col = GetColorByNormal(trimesh.vertices, v0, v1, v2); - debugDraw.Vertex(trimesh.vertices[v0], trimesh.vertices[v0 + 1], trimesh.vertices[v0 + 2], col); - debugDraw.Vertex(trimesh.vertices[v1], trimesh.vertices[v1 + 1], trimesh.vertices[v1 + 2], col); - debugDraw.Vertex(trimesh.vertices[v2], trimesh.vertices[v2 + 1], trimesh.vertices[v2 + 2], col); + RcVec3f v0 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i]); + RcVec3f v1 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i + 1]); + RcVec3f v2 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i + 2]); + int col = GetColorByNormal(v0, v1, v2); + debugDraw.Vertex(v0.X, v0.Y, v0.Z, col); + debugDraw.Vertex(v1.X, v1.Y, v1.Z, col); + debugDraw.Vertex(v2.X, v2.Y, v2.Z, col); } debugDraw.End(); diff --git a/src/DotRecast.Recast.Demo/Tools/ISampleTool.cs b/src/DotRecast.Recast.Demo/Tools/ISampleTool.cs index bd9490d..5fef1ba 100644 --- a/src/DotRecast.Recast.Demo/Tools/ISampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/ISampleTool.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Tools/JumpLinkBuilderSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/JumpLinkBuilderSampleTool.cs index 4b31c47..db2fc07 100644 --- a/src/DotRecast.Recast.Demo/Tools/JumpLinkBuilderSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/JumpLinkBuilderSampleTool.cs @@ -417,7 +417,7 @@ public class JumpLinkBuilderSampleTool : ISampleTool } - private void DrawTrajectory(RecastDebugDraw dd, JumpLink link, RcVec3f pa, RcVec3f pb, Trajectory tra, int cola) + private void DrawTrajectory(RecastDebugDraw dd, JumpLink link, RcVec3f pa, RcVec3f pb, ITrajectory tra, int cola) { } diff --git a/src/DotRecast.Recast.Demo/Tools/ObstacleSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/ObstacleSampleTool.cs index 216f7f3..8661122 100644 --- a/src/DotRecast.Recast.Demo/Tools/ObstacleSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/ObstacleSampleTool.cs @@ -1,4 +1,4 @@ -using DotRecast.Core; +using DotRecast.Core; using DotRecast.Core.Numerics; using DotRecast.Detour.TileCache; using DotRecast.Detour.TileCache.Io.Compress; @@ -32,7 +32,7 @@ public class ObstacleSampleTool : ISampleTool var buildResult = _tool.Build(geom, settings, RcByteOrder.LITTLE_ENDIAN, true); if (buildResult.Success) { - _sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh); + _sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh); } } diff --git a/src/DotRecast.Recast.Demo/Tools/OffMeshConnectionSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/OffMeshConnectionSampleTool.cs index feb38f9..0e302d1 100644 --- a/src/DotRecast.Recast.Demo/Tools/OffMeshConnectionSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/OffMeshConnectionSampleTool.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/Tools/TestNavmeshSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/TestNavmeshSampleTool.cs index b9552dd..79eb9b5 100644 --- a/src/DotRecast.Recast.Demo/Tools/TestNavmeshSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/TestNavmeshSampleTool.cs @@ -57,7 +57,8 @@ public class TestNavmeshSampleTool : ISampleTool private bool m_hitResult; private float m_distanceToWall; - private List m_straightPath; + private DtStraightPath[] m_straightPath; + private int m_straightPathCount; private List m_polys; private List m_parent; private float m_neighbourhoodRadius; @@ -77,6 +78,8 @@ public class TestNavmeshSampleTool : ISampleTool SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, new float[] { 1f, 1f, 1f, 1f, 2f, 1.5f } ); + m_straightPath = new DtStraightPath[MAX_POLYS]; + m_straightPathCount = 0; } public void Layout() @@ -137,22 +140,22 @@ public class TestNavmeshSampleTool : ISampleTool ImGui.Text("Common"); ImGui.Separator(); - ImGui.Text("Include Flags"); + ImGui.Text("+ Include Flags"); ImGui.Separator(); - ImGui.CheckboxFlags("Walk", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); - ImGui.CheckboxFlags("Swim", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); - ImGui.CheckboxFlags("Door", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); - ImGui.CheckboxFlags("Jump", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); + ImGui.CheckboxFlags("+ Walk", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); + ImGui.CheckboxFlags("+ Swim", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); + ImGui.CheckboxFlags("+ Door", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); + ImGui.CheckboxFlags("+ Jump", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); ImGui.NewLine(); m_filter.SetIncludeFlags(_includeFlags); - ImGui.Text("Exclude Flags"); + ImGui.Text("- Exclude Flags"); ImGui.Separator(); - ImGui.CheckboxFlags("Walk", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); - ImGui.CheckboxFlags("Swim", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); - ImGui.CheckboxFlags("Door", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); - ImGui.CheckboxFlags("Jump", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); + ImGui.CheckboxFlags("- Walk", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); + ImGui.CheckboxFlags("- Swim", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); + ImGui.CheckboxFlags("- Door", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); + ImGui.CheckboxFlags("- Jump", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); ImGui.NewLine(); m_filter.SetExcludeFlags(_excludeFlags); @@ -284,7 +287,7 @@ public class TestNavmeshSampleTool : ISampleTool int spathCol = DuRGBA(64, 16, 0, 220); int offMeshCol = DuRGBA(128, 96, 0, 220); dd.Begin(LINES, 2.0f); - for (int i = 0; i < m_straightPath.Count - 1; ++i) + for (int i = 0; i < m_straightPathCount - 1; ++i) { DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem2 = m_straightPath[i + 1]; @@ -304,7 +307,7 @@ public class TestNavmeshSampleTool : ISampleTool dd.End(); dd.Begin(POINTS, 6.0f); - for (int i = 0; i < m_straightPath.Count; ++i) + for (int i = 0; i < m_straightPathCount; ++i) { DtStraightPath straightPathItem = m_straightPath[i]; int col; @@ -349,7 +352,7 @@ public class TestNavmeshSampleTool : ISampleTool dd.DepthMask(false); int spathCol = m_hitResult ? DuRGBA(64, 16, 0, 220) : DuRGBA(240, 240, 240, 220); dd.Begin(LINES, 2.0f); - for (int i = 0; i < m_straightPath.Count - 1; ++i) + for (int i = 0; i < m_straightPathCount - 1; ++i) { DtStraightPath straightPathItem = m_straightPath[i]; DtStraightPath straightPathItem2 = m_straightPath[i + 1]; @@ -359,7 +362,7 @@ public class TestNavmeshSampleTool : ISampleTool dd.End(); dd.Begin(POINTS, 4.0f); - for (int i = 0; i < m_straightPath.Count; ++i) + for (int i = 0; i < m_straightPathCount; ++i) { DtStraightPath straightPathItem = m_straightPath[i]; dd.Vertex(straightPathItem.pos.X, straightPathItem.pos.Y + 0.4f, straightPathItem.pos.Z, spathCol); @@ -469,8 +472,9 @@ public class TestNavmeshSampleTool : ISampleTool { if (m_polys != null) { - var segmentVerts = new List(); - var segmentRefs = new List(); + const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4; + Span segs = stackalloc RcSegmentVert[MAX_SEGS]; + Span refs = stackalloc long[MAX_SEGS]; for (int i = 0; i < m_polys.Count; i++) { @@ -488,18 +492,20 @@ public class TestNavmeshSampleTool : ISampleTool dd.DepthMask(true); if (_sample.GetNavMeshQuery() != null) { + int nsegs = 0; var result = _sample .GetNavMeshQuery() - .GetPolyWallSegments(m_polys[i], false, m_filter, ref segmentVerts, ref segmentRefs); + .GetPolyWallSegments(m_polys[i], m_filter, segs, refs, ref nsegs, MAX_SEGS); if (result.Succeeded()) { dd.Begin(LINES, 2.0f); - for (int j = 0; j < segmentVerts.Count; ++j) + for (int j = 0; j < nsegs; ++j) { - RcSegmentVert s = segmentVerts[j]; + ref RcSegmentVert s = ref segs[j]; var v0 = s.vmin; var s3 = s.vmax; + // Skip too distant segments. var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg); if (distSqr > RcMath.Sqr(m_neighbourhoodRadius)) @@ -508,12 +514,13 @@ public class TestNavmeshSampleTool : ISampleTool } RcVec3f delta = RcVec3f.Subtract(s3, s.vmin); - RcVec3f p0 = RcVecUtils.Mad(s.vmin, delta, 0.5f); + RcVec3f p0 = RcVec.Mad(s.vmin, delta, 0.5f); RcVec3f norm = new RcVec3f(delta.Z, 0, -delta.X); norm = RcVec3f.Normalize(norm); - RcVec3f p1 = RcVecUtils.Mad(p0, norm, agentRadius * 0.5f); + RcVec3f p1 = RcVec.Mad(p0, norm, agentRadius * 0.5f); + // Skip backfacing segments. - if (segmentRefs[j] != 0) + if (refs[j] != 0) { int col = DuRGBA(255, 255, 255, 32); dd.Vertex(s.vmin.X, s.vmin.Y + agentClimb, s.vmin.Z, col); @@ -661,23 +668,23 @@ public class TestNavmeshSampleTool : ISampleTool if (_mode == RcTestNavmeshToolMode.PATHFIND_FOLLOW) { _tool.FindFollowPath(navMesh, navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast, - ref m_polys, ref m_smoothPath); + ref m_polys, m_polys?.Count ?? 0, ref m_smoothPath); } else if (_mode == RcTestNavmeshToolMode.PATHFIND_STRAIGHT) { _tool.FindStraightPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast, - ref m_polys, ref m_straightPath, _straightPathOption); + ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS, _straightPathOption); } else if (_mode == RcTestNavmeshToolMode.PATHFIND_SLICED) { m_polys?.Clear(); - m_straightPath?.Clear(); + m_straightPathCount = 0; m_pathFindStatus = _tool.InitSlicedFindPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast); } else if (_mode == RcTestNavmeshToolMode.RAYCAST) { _tool.Raycast(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, - ref m_polys, ref m_straightPath, ref m_hitPos, ref m_hitNormal, ref m_hitResult); + ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS, ref m_hitPos, ref m_hitNormal, ref m_hitResult); } else if (_mode == RcTestNavmeshToolMode.DISTANCE_TO_WALL) { @@ -712,7 +719,7 @@ public class TestNavmeshSampleTool : ISampleTool if (m_pathFindStatus.InProgress()) { - m_pathFindStatus = _tool.UpdateSlicedFindPath(navQuery, 1, m_endRef, m_spos, m_epos, ref m_polys, ref m_straightPath); + m_pathFindStatus = _tool.UpdateSlicedFindPath(navQuery, 1, m_endRef, m_spos, m_epos, ref m_polys, m_straightPath, out m_straightPathCount, MAX_POLYS); } } } diff --git a/src/DotRecast.Recast.Demo/Tools/TileSampleTool.cs b/src/DotRecast.Recast.Demo/Tools/TileSampleTool.cs index 5b33b99..40dd239 100644 --- a/src/DotRecast.Recast.Demo/Tools/TileSampleTool.cs +++ b/src/DotRecast.Recast.Demo/Tools/TileSampleTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using DotRecast.Core.Numerics; using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Toolset; diff --git a/src/DotRecast.Recast.Demo/UI/IRcView.cs b/src/DotRecast.Recast.Demo/UI/IRcView.cs index 677e441..c3d6b93 100644 --- a/src/DotRecast.Recast.Demo/UI/IRcView.cs +++ b/src/DotRecast.Recast.Demo/UI/IRcView.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/UI/ImFilePicker.cs b/src/DotRecast.Recast.Demo/UI/ImFilePicker.cs index 9ab2547..a49c02a 100644 --- a/src/DotRecast.Recast.Demo/UI/ImFilePicker.cs +++ b/src/DotRecast.Recast.Demo/UI/ImFilePicker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -69,7 +69,7 @@ public class ImFilePicker ImGui.Text("Current Folder: " + CurrentFolder); bool result = false; - if (ImGui.BeginChildFrame(1, new Vector2(1024, 400))) + if (ImGui.BeginChild(1, new Vector2(1024, 400))) { var di = new DirectoryInfo(CurrentFolder); if (di.Exists) @@ -111,7 +111,7 @@ public class ImFilePicker } } - ImGui.EndChildFrame(); + ImGui.EndChild(); if (ImGui.Button("Cancel")) diff --git a/src/DotRecast.Recast.Demo/UI/RcCanvas.cs b/src/DotRecast.Recast.Demo/UI/RcCanvas.cs index 2e6c756..c24503d 100644 --- a/src/DotRecast.Recast.Demo/UI/RcCanvas.cs +++ b/src/DotRecast.Recast.Demo/UI/RcCanvas.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/UI/RcLogView.cs b/src/DotRecast.Recast.Demo/UI/RcLogView.cs index a308895..2659c3e 100644 --- a/src/DotRecast.Recast.Demo/UI/RcLogView.cs +++ b/src/DotRecast.Recast.Demo/UI/RcLogView.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -80,7 +80,7 @@ public class RcLogView : IRcView } - if (ImGui.BeginChild("scrolling", Vector2.Zero, false, ImGuiWindowFlags.HorizontalScrollbar)) + if (ImGui.BeginChild("scrolling", Vector2.Zero, ImGuiChildFlags.None, ImGuiWindowFlags.HorizontalScrollbar)) { _isHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.RectOnly | ImGuiHoveredFlags.RootAndChildWindows); diff --git a/src/DotRecast.Recast.Demo/UI/RcMenuView.cs b/src/DotRecast.Recast.Demo/UI/RcMenuView.cs index e727a82..6723951 100644 --- a/src/DotRecast.Recast.Demo/UI/RcMenuView.cs +++ b/src/DotRecast.Recast.Demo/UI/RcMenuView.cs @@ -1,4 +1,4 @@ -using DotRecast.Core; +using DotRecast.Core; using ImGuiNET; namespace DotRecast.Recast.Demo.UI; diff --git a/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs b/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs index 5ca0de5..eedcd12 100644 --- a/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs +++ b/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/UI/RcToolsetView.cs b/src/DotRecast.Recast.Demo/UI/RcToolsetView.cs index 97f165b..4b65f8d 100644 --- a/src/DotRecast.Recast.Demo/UI/RcToolsetView.cs +++ b/src/DotRecast.Recast.Demo/UI/RcToolsetView.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast.Demo/UI/ViewModels/LogMessageItem.cs b/src/DotRecast.Recast.Demo/UI/ViewModels/LogMessageItem.cs index cba0b0d..19cf614 100644 --- a/src/DotRecast.Recast.Demo/UI/ViewModels/LogMessageItem.cs +++ b/src/DotRecast.Recast.Demo/UI/ViewModels/LogMessageItem.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Demo.UI.ViewModels; +namespace DotRecast.Recast.Demo.UI.ViewModels; public class LogMessageItem { diff --git a/src/DotRecast.Recast.Toolset/Builder/NavMeshBuildResult.cs b/src/DotRecast.Recast.Toolset/Builder/NavMeshBuildResult.cs index 6c93ad0..a86f222 100644 --- a/src/DotRecast.Recast.Toolset/Builder/NavMeshBuildResult.cs +++ b/src/DotRecast.Recast.Toolset/Builder/NavMeshBuildResult.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using DotRecast.Detour; @@ -7,6 +7,7 @@ namespace DotRecast.Recast.Toolset.Builder public class NavMeshBuildResult { public readonly bool Success; + public readonly RcConfig Cfg; public readonly IList RecastBuilderResults; public readonly DtNavMesh NavMesh; @@ -17,11 +18,22 @@ namespace DotRecast.Recast.Toolset.Builder NavMesh = null; } - public NavMeshBuildResult(IList recastBuilderResults, DtNavMesh navMesh) + // for solo + public NavMeshBuildResult(RcConfig cfg, IList recastBuilderResults, DtNavMesh navMesh) { Success = true; + Cfg = cfg; RecastBuilderResults = recastBuilderResults; NavMesh = navMesh; } + + // for tiles + public NavMeshBuildResult(RcConfig cfg, IList recastBuilderResults) + { + Success = true; + Cfg = cfg; + RecastBuilderResults = recastBuilderResults; + NavMesh = null; + } } } \ No newline at end of file diff --git a/src/DotRecast.Recast.Toolset/Builder/SampleAreaModifications.cs b/src/DotRecast.Recast.Toolset/Builder/SampleAreaModifications.cs index 367698c..9269952 100644 --- a/src/DotRecast.Recast.Toolset/Builder/SampleAreaModifications.cs +++ b/src/DotRecast.Recast.Toolset/Builder/SampleAreaModifications.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -34,12 +34,12 @@ namespace DotRecast.Recast.Toolset.Builder 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 const int SAMPLE_POLYFLAGS_WALK = 0x01; // Ability to walk (ground, grass, road) + public const int SAMPLE_POLYFLAGS_SWIM = 0x02; // Ability to swim (water). + public const int SAMPLE_POLYFLAGS_DOOR = 0x04; // Ability to move through doors. + public const int SAMPLE_POLYFLAGS_JUMP = 0x08; // Ability to jump. + public const int SAMPLE_POLYFLAGS_DISABLED = 0x10; // Disabled polygon + public const 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); diff --git a/src/DotRecast.Recast.Toolset/Builder/SoloNavMeshBuilder.cs b/src/DotRecast.Recast.Toolset/Builder/SoloNavMeshBuilder.cs index 10b4c09..401802c 100644 --- a/src/DotRecast.Recast.Toolset/Builder/SoloNavMeshBuilder.cs +++ b/src/DotRecast.Recast.Toolset/Builder/SoloNavMeshBuilder.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -68,12 +69,19 @@ namespace DotRecast.Recast.Toolset.Builder } var navMesh = BuildNavMesh(meshData, vertsPerPoly); - return new NavMeshBuildResult(RcImmutableArray.Create(rcResult), navMesh); + return new NavMeshBuildResult(cfg, RcImmutableArray.Create(rcResult), navMesh); } private DtNavMesh BuildNavMesh(DtMeshData meshData, int vertsPerPoly) { - return new DtNavMesh(meshData, vertsPerPoly, 0); + var mesh = new DtNavMesh(); + var status = mesh.Init(meshData, vertsPerPoly, 0); + if (status.Failed()) + { + return null; + } + + return mesh; } private RcBuilderResult BuildRecastResult(DemoInputGeomProvider geom, RcConfig cfg, bool keepInterResults) diff --git a/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs b/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs index 5542c25..e328985 100644 --- a/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs +++ b/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -57,7 +58,7 @@ namespace DotRecast.Recast.Toolset.Builder bool filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans, bool keepInterResults, bool buildAll) { - List results = BuildRecastResult( + NavMeshBuildResult result = BuildRecastResult( geom, tileSize, partitionType, @@ -71,12 +72,12 @@ namespace DotRecast.Recast.Toolset.Builder keepInterResults, buildAll ); - var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, results); + var tileMeshData = BuildMeshData(geom, cellSize, cellHeight, agentHeight, agentRadius, agentMaxClimb, result.RecastBuilderResults); var tileNavMesh = BuildNavMesh(geom, tileMeshData, cellSize, tileSize, vertsPerPoly); - return new NavMeshBuildResult(results, tileNavMesh); + return new NavMeshBuildResult(result.Cfg, result.RecastBuilderResults, tileNavMesh); } - public List BuildRecastResult(IInputGeomProvider geom, + public NavMeshBuildResult BuildRecastResult(IInputGeomProvider geom, int tileSize, RcPartition partitionType, float cellSize, float cellHeight, @@ -101,7 +102,8 @@ namespace DotRecast.Recast.Toolset.Builder filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); RcBuilder rcBuilder = new RcBuilder(); - return rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1, Task.Factory); + var results = rcBuilder.BuildTiles(geom, cfg, keepInterResults, buildAll, Environment.ProcessorCount + 1, Task.Factory); + return new NavMeshBuildResult(cfg, results); } public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List meshData, float cellSize, int tileSize, int vertsPerPoly) @@ -115,8 +117,9 @@ namespace DotRecast.Recast.Toolset.Builder 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)); + DtNavMesh navMesh = new DtNavMesh(); + navMesh.Init(navMeshParams, vertsPerPoly); + meshData.ForEach(md => navMesh.AddTile(md, 0, 0, out _)); return navMesh; } @@ -158,7 +161,7 @@ namespace DotRecast.Recast.Toolset.Builder private int GetTileBits(IInputGeomProvider geom, float cellSize, int tileSize) { - RcCommons.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out var gw, out var gh); + RcRecast.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 = Math.Min(DtUtils.Ilog2(DtUtils.NextPow2(tw * th)), 14); @@ -167,7 +170,7 @@ namespace DotRecast.Recast.Toolset.Builder public int[] GetTiles(DemoInputGeomProvider geom, float cellSize, int tileSize) { - RcCommons.CalcGridSize(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), cellSize, out var gw, out var gh); + RcRecast.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 }; diff --git a/src/DotRecast.Recast.Toolset/Geom/DemoDtTileCacheMeshProcess.cs b/src/DotRecast.Recast.Toolset/Geom/DemoDtTileCacheMeshProcess.cs index f738a97..e2e0795 100644 --- a/src/DotRecast.Recast.Toolset/Geom/DemoDtTileCacheMeshProcess.cs +++ b/src/DotRecast.Recast.Toolset/Geom/DemoDtTileCacheMeshProcess.cs @@ -1,4 +1,4 @@ -using DotRecast.Detour; +using DotRecast.Detour; using DotRecast.Detour.TileCache; using DotRecast.Recast.Geom; using DotRecast.Recast.Toolset.Builder; diff --git a/src/DotRecast.Recast.Toolset/Geom/DemoInputGeomProvider.cs b/src/DotRecast.Recast.Toolset/Geom/DemoInputGeomProvider.cs index 50b3390..9c48c7f 100644 --- a/src/DotRecast.Recast.Toolset/Geom/DemoInputGeomProvider.cs +++ b/src/DotRecast.Recast.Toolset/Geom/DemoInputGeomProvider.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -41,7 +41,7 @@ namespace DotRecast.Recast.Toolset.Geom public static DemoInputGeomProvider LoadFile(string objFilePath) { - byte[] chunk = RcResources.Load(objFilePath); + byte[] chunk = RcIO.ReadFileIfFound(objFilePath); var context = RcObjImporter.LoadContext(chunk); return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces); } @@ -57,12 +57,12 @@ namespace DotRecast.Recast.Toolset.Geom this.faces = faces; normals = new float[faces.Length]; CalculateNormals(); - bmin = RcVecUtils.Create(vertices); - bmax = RcVecUtils.Create(vertices); + bmin = new RcVec3f(vertices); + bmax = new RcVec3f(vertices); for (int i = 1; i < vertices.Length / 3; i++) { - bmin = RcVecUtils.Min(bmin, vertices, i * 3); - bmax = RcVecUtils.Max(bmax, vertices, i * 3); + bmin = RcVec3f.Min(bmin, RcVec.Create(vertices, i * 3)); + bmax = RcVec3f.Max(bmax, RcVec.Create(vertices, i * 3)); } _mesh = new RcTriMesh(vertices, faces); @@ -87,11 +87,11 @@ namespace DotRecast.Recast.Toolset.Geom { 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); + RcVec3f v0 = RcVec.Create(vertices, faces[i] * 3); + RcVec3f v1 = RcVec.Create(vertices, faces[i + 1] * 3); + RcVec3f v2 = RcVec.Create(vertices, faces[i + 2] * 3); + RcVec3f e0 = v1 - v0; + RcVec3f e1 = v2 - v0; normals[i] = e0.Y * e1.Z - e0.Z * e1.Y; normals[i + 1] = e0.Z * e1.X - e0.X * e1.Z; @@ -150,7 +150,7 @@ namespace DotRecast.Recast.Toolset.Geom q.X = src.X + (dst.X - src.X) * btmax; q.Y = src.Z + (dst.Z - src.Z) * btmax; - List chunks = _mesh.chunkyTriMesh.GetChunksOverlappingSegment(p, q); + List chunks = RcChunkyTriMeshs.GetChunksOverlappingSegment(_mesh.chunkyTriMesh, p, q); if (0 == chunks.Count) { return false; diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcCapsuleGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcCapsuleGizmo.cs index 90a10a7..1e86135 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcCapsuleGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcCapsuleGizmo.cs @@ -19,7 +19,7 @@ namespace DotRecast.Recast.Toolset.Gizmos 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]; + Span normals = stackalloc 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); diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcCompositeGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcCompositeGizmo.cs index 525d38c..7d75f8c 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcCompositeGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcCompositeGizmo.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Gizmos +namespace DotRecast.Recast.Toolset.Gizmos { public class RcCompositeGizmo : IRcGizmoMeshFilter { diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcCylinderGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcCylinderGizmo.cs index 2183bbf..841e7f4 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcCylinderGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcCylinderGizmo.cs @@ -19,7 +19,7 @@ namespace DotRecast.Recast.Toolset.Gizmos 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]; + Span normals = stackalloc 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); diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmo.cs index da94d8d..2b8ffec 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmo.cs @@ -1,4 +1,4 @@ -using DotRecast.Detour.Dynamic.Colliders; +using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Recast.Toolset.Gizmos; namespace DotRecast.Recast.Toolset.Gizmos diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoFactory.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoFactory.cs index c1b86be..fe45554 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoFactory.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoFactory.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Toolset.Gizmos { diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoHelper.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoHelper.cs index 72ce931..c814b27 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoHelper.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcGizmoHelper.cs @@ -4,8 +4,8 @@ namespace DotRecast.Recast.Toolset.Gizmos { public static class RcGizmoHelper { - private static readonly int SEGMENTS = 16; - private static readonly int RINGS = 8; + private const int SEGMENTS = 16; + private const int RINGS = 8; private static float[] sphericalVertices; diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcSphereGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcSphereGizmo.cs index 06a93be..bd899a3 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcSphereGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcSphereGizmo.cs @@ -1,7 +1,6 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper; - namespace DotRecast.Recast.Toolset.Gizmos { public class RcSphereGizmo : IRcGizmoMeshFilter diff --git a/src/DotRecast.Recast.Toolset/Gizmos/RcTrimeshGizmo.cs b/src/DotRecast.Recast.Toolset/Gizmos/RcTrimeshGizmo.cs index 4860e96..c8738fa 100644 --- a/src/DotRecast.Recast.Toolset/Gizmos/RcTrimeshGizmo.cs +++ b/src/DotRecast.Recast.Toolset/Gizmos/RcTrimeshGizmo.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Gizmos +namespace DotRecast.Recast.Toolset.Gizmos { public class RcTrimeshGizmo : IRcGizmoMeshFilter { diff --git a/src/DotRecast.Recast.Toolset/IRcToolable.cs b/src/DotRecast.Recast.Toolset/IRcToolable.cs index 564ead1..25aa12b 100644 --- a/src/DotRecast.Recast.Toolset/IRcToolable.cs +++ b/src/DotRecast.Recast.Toolset/IRcToolable.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset +namespace DotRecast.Recast.Toolset { public interface IRcToolable { diff --git a/src/DotRecast.Recast.Toolset/RcNavMeshBuildSettings.cs b/src/DotRecast.Recast.Toolset/RcNavMeshBuildSettings.cs index d097b1a..64f4985 100644 --- a/src/DotRecast.Recast.Toolset/RcNavMeshBuildSettings.cs +++ b/src/DotRecast.Recast.Toolset/RcNavMeshBuildSettings.cs @@ -1,8 +1,5 @@ -using System; - namespace DotRecast.Recast.Toolset { - [Serializable] public class RcNavMeshBuildSettings { public float cellSize = 0.3f; @@ -35,7 +32,7 @@ namespace DotRecast.Recast.Toolset public bool tiled = false; public int tileSize = 32; - public bool keepInterResults = false; + public bool keepInterResults = true; // full memory public bool buildAll = true; } } \ No newline at end of file diff --git a/src/DotRecast.Recast.Toolset/Tools/RcConvexVolumeTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcConvexVolumeTool.cs index 21e131c..94674f1 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcConvexVolumeTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcConvexVolumeTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using DotRecast.Core; using DotRecast.Core.Numerics; diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentData.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentData.cs index 96b6e22..800381b 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentData.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentData.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Toolset.Tools { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingTool.cs index 6a5227b..4f4adea 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -26,12 +26,12 @@ namespace DotRecast.Recast.Toolset.Tools private readonly List _polyPoints; private const int SamplingCount = 500; - private long _samplingUpdateTime; + private double _samplingUpdateTime; private readonly RcCyclicBuffer _updateTimes; - private long _curUpdateTime; - private long _avgUpdateTime; - private long _minUpdateTime; - private long _maxUpdateTime; + private double _curUpdateTime; + private double _avgUpdateTime; + private double _minUpdateTime; + private double _maxUpdateTime; public RcCrowdAgentProfilingTool() { @@ -269,11 +269,11 @@ namespace DotRecast.Recast.Toolset.Tools _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; + _samplingUpdateTime = _updateTimes.Sum() / (double)TimeSpan.TicksPerMillisecond; + _curUpdateTime = currentTime / (double)TimeSpan.TicksPerMillisecond; + _avgUpdateTime = (_updateTimes.Average() / (double)TimeSpan.TicksPerMillisecond); + _minUpdateTime = _updateTimes.Min() / (double)TimeSpan.TicksPerMillisecond; + _maxUpdateTime = _updateTimes.Max() / (double)TimeSpan.TicksPerMillisecond; } private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData) @@ -374,27 +374,27 @@ namespace DotRecast.Recast.Toolset.Tools } } - public long GetCrowdUpdateSamplingTime() + public double GetCrowdUpdateSamplingTime() { return _samplingUpdateTime; } - public long GetCrowdUpdateTime() + public double GetCrowdUpdateTime() { return _curUpdateTime; } - public long GetCrowdUpdateAvgTime() + public double GetCrowdUpdateAvgTime() { return _avgUpdateTime; } - public long GetCrowdUpdateMinTime() + public double GetCrowdUpdateMinTime() { return _minUpdateTime; } - public long GetCrowdUpdateMaxTime() + public double GetCrowdUpdateMaxTime() { return _maxUpdateTime; } diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingToolConfig.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingToolConfig.cs index 65794d1..6b6e753 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingToolConfig.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentProfilingToolConfig.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Tools +namespace DotRecast.Recast.Toolset.Tools { public class RcCrowdAgentProfilingToolConfig { @@ -12,5 +12,7 @@ public float percentTravellers = 15f; public int pathQueueSize = 32; public int maxIterations = 300; + + public bool showAgents = true; } } \ No newline at end of file diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentTrail.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentTrail.cs index 1717620..cb4f52d 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentTrail.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentTrail.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Tools +namespace DotRecast.Recast.Toolset.Tools { public class RcCrowdAgentTrail { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentType.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentType.cs index 1fce64c..ee0685b 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentType.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdAgentType.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Tools +namespace DotRecast.Recast.Toolset.Tools { public enum RcCrowdAgentType { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdTool.cs index 4bfbd5f..e79a902 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using DotRecast.Core; using DotRecast.Core.Numerics; @@ -297,7 +297,7 @@ namespace DotRecast.Recast.Toolset.Tools RcVec3f vel = RcVec3f.Subtract(tgt, pos); vel.Y = 0.0f; vel = RcVec3f.Normalize(vel); - return vel.Scale(speed); + return vel * speed; } public long GetCrowdUpdateTime() diff --git a/src/DotRecast.Recast.Toolset/Tools/RcCrowdToolMode.cs b/src/DotRecast.Recast.Toolset/Tools/RcCrowdToolMode.cs index 65f9440..7ac09f1 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcCrowdToolMode.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcCrowdToolMode.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Collections; +using DotRecast.Core.Collections; namespace DotRecast.Recast.Toolset.Tools { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcDynamicColliderShape.cs b/src/DotRecast.Recast.Toolset/Tools/RcDynamicColliderShape.cs index 9ea240a..55733d5 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcDynamicColliderShape.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcDynamicColliderShape.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast.Toolset.Tools +namespace DotRecast.Recast.Toolset.Tools { public enum RcDynamicColliderShape { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateTool.cs index 83cc474..a27cb40 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -141,6 +141,16 @@ namespace DotRecast.Recast.Toolset.Tools return colliderWithGizmo; } + public DtDynamicNavMesh Copy(RcConfig cfg, IList results) + { + var voxelFile = DtVoxelFile.From(cfg, results); + dynaMesh = new DtDynamicNavMesh(voxelFile); + dynaMesh.config.keepIntermediateResults = true; + colliderGizmos.Clear(); + + return dynaMesh; + } + public DtDynamicNavMesh Load(string filename, IRcCompressor compressor) { using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); @@ -159,7 +169,7 @@ namespace DotRecast.Recast.Toolset.Tools 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 fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); using var bw = new BinaryWriter(fs); DtVoxelFileWriter writer = new DtVoxelFileWriter(compressor); writer.Write(bw, voxelFile, compression); @@ -184,7 +194,7 @@ namespace DotRecast.Recast.Toolset.Tools (1f - 2 * (float)random.NextDouble()) ); a = RcVec3f.Normalize(a); - + float len = 1f + (float)random.NextDouble() * 20f; a.X *= len; a.Y *= len; @@ -235,7 +245,7 @@ namespace DotRecast.Recast.Toolset.Tools RcVec3f baseUp = new RcVec3f(0, 1, 0); RcVec3f forward = new RcVec3f((1f - 2 * (float)random.NextDouble()), 0, (1f - 2 * (float)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); diff --git a/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateToolMode.cs b/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateToolMode.cs index a7125d5..c2525d8 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateToolMode.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcDynamicUpdateToolMode.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Collections; +using DotRecast.Core.Collections; namespace DotRecast.Recast.Toolset.Tools { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcJumpLinkBuilderTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcJumpLinkBuilderTool.cs index 105ca24..41cc023 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcJumpLinkBuilderTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcJumpLinkBuilderTool.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using DotRecast.Core.Numerics; using DotRecast.Detour.Extras.Jumplink; using DotRecast.Recast.Geom; @@ -116,7 +116,7 @@ namespace DotRecast.Recast.Toolset.Tools { RcVec3f p = link.startSamples[i].p; RcVec3f q = link.endSamples[i].p; - if (i == 0 || RcVecUtils.Dist2D(prev, p) > agentRadius) + if (i == 0 || RcVec.Dist2D(prev, p) > agentRadius) { geom.AddOffMeshConnection(p, q, agentRadius, false, area, flags); prev = p; diff --git a/src/DotRecast.Recast.Toolset/Tools/RcObstacleTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcObstacleTool.cs index 68e22e1..53b4b88 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcObstacleTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcObstacleTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using DotRecast.Core; using DotRecast.Core.Collections; @@ -42,7 +42,7 @@ namespace DotRecast.Recast.Toolset.Tools // Init cache var bmin = geom.GetMeshBoundsMin(); var bmax = geom.GetMeshBoundsMax(); - RcCommons.CalcGridSize(bmin, bmax, setting.cellSize, out var gw, out var gh); + RcRecast.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; @@ -78,7 +78,7 @@ namespace DotRecast.Recast.Toolset.Tools _tc.BuildNavMeshTile(refs); } - return new NavMeshBuildResult(RcImmutableArray.Empty, _tc.GetNavMesh()); + return new NavMeshBuildResult(cfg, RcImmutableArray.Empty, _tc.GetNavMesh()); } public void ClearAllTempObstacles() @@ -141,7 +141,8 @@ namespace DotRecast.Recast.Toolset.Tools navMeshParams.maxTiles = 256; // .. navMeshParams.maxPolys = 16384; - var navMesh = new DtNavMesh(navMeshParams, 6); + var navMesh = new DtNavMesh(); + navMesh.Init(navMeshParams, 6); var comp = _comp.Create(cCompatibility ? 0 : 1); var storageParams = new DtTileCacheStorageParams(order, cCompatibility); DtTileCache tc = new DtTileCache(option, storageParams, navMesh, comp, _proc); diff --git a/src/DotRecast.Recast.Toolset/Tools/RcOffMeshConnectionTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcOffMeshConnectionTool.cs index 2d7a960..e179693 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcOffMeshConnectionTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcOffMeshConnectionTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using DotRecast.Core.Numerics; using DotRecast.Recast.Geom; using DotRecast.Recast.Toolset.Builder; @@ -34,7 +34,7 @@ namespace DotRecast.Recast.Toolset.Tools RcOffMeshConnection nearestConnection = null; foreach (RcOffMeshConnection offMeshCon in geom.GetOffMeshConnections()) { - float d = Math.Min(RcVecUtils.DistanceSquared(p, offMeshCon.verts, 0), RcVecUtils.DistanceSquared(p, offMeshCon.verts, 3)); + float d = Math.Min(RcVec.DistanceSquared(p, offMeshCon.verts, 0), RcVec.DistanceSquared(p, offMeshCon.verts, 3)); if (d < nearestDist && Math.Sqrt(d) < settings.agentRadius) { nearestDist = d; diff --git a/src/DotRecast.Recast.Toolset/Tools/RcTestNavMeshTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcTestNavMeshTool.cs index a9c9b62..4199c74 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcTestNavMeshTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcTestNavMeshTool.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using DotRecast.Core; using DotRecast.Core.Numerics; @@ -11,7 +11,6 @@ namespace DotRecast.Recast.Toolset.Tools public const int MAX_POLYS = 256; public const int MAX_SMOOTH = 2048; - public RcTestNavMeshTool() { } @@ -22,7 +21,7 @@ namespace DotRecast.Recast.Toolset.Tools } public DtStatus FindFollowPath(DtNavMesh navMesh, DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast, - ref List pathIterPolys, ref List smoothPath) + ref List pathIterPolys, int pathIterPolyCount, ref List smoothPath) { if (startRef == 0 || endRef == 0) { @@ -36,6 +35,8 @@ namespace DotRecast.Recast.Toolset.Tools smoothPath ??= new List(); pathIterPolys.Clear(); + pathIterPolyCount = 0; + smoothPath.Clear(); var opt = new DtFindPathOption(enableRaycast ? DtFindPathOptions.DT_FINDPATH_ANY_ANGLE : 0, float.MaxValue); @@ -43,24 +44,29 @@ namespace DotRecast.Recast.Toolset.Tools 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 _); - float STEP_SIZE = 0.5f; - float SLOP = 0.01f; + const float STEP_SIZE = 0.5f; + const float SLOP = 0.01f; smoothPath.Clear(); smoothPath.Add(iterPos); - var visited = new List(); + + Span 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 < pathIterPolys.Count && smoothPath.Count < MAX_SMOOTH) + while (0 < pathIterPolyCount && smoothPath.Count < MAX_SMOOTH) { // Find location to steer towards. if (!DtPathUtils.GetSteerTarget(navQuery, iterPos, targetPos, SLOP, - pathIterPolys, out var steerPos, out var steerPosFlag, out var steerPosRef)) + pathIterPolys, pathIterPolyCount, out var steerPos, out var steerPosFlag, out var steerPosRef)) { break; } @@ -85,15 +91,15 @@ namespace DotRecast.Recast.Toolset.Tools len = STEP_SIZE / len; } - RcVec3f moveTgt = RcVecUtils.Mad(iterPos, delta, len); + RcVec3f moveTgt = RcVec.Mad(iterPos, delta, len); // Move - navQuery.MoveAlongSurface(pathIterPolys[0], iterPos, moveTgt, filter, out var result, ref visited); + navQuery.MoveAlongSurface(pathIterPolys[0], iterPos, moveTgt, filter, out var result, visited, out nvisited, 16); iterPos = result; - pathIterPolys = DtPathUtils.MergeCorridorStartMoved(pathIterPolys, pathIterPolys.Count, MAX_POLYS, visited); - pathIterPolys = DtPathUtils.FixupShortcuts(pathIterPolys, navQuery); + 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()) @@ -123,7 +129,7 @@ namespace DotRecast.Recast.Toolset.Tools long prevRef = 0; long polyRef = pathIterPolys[0]; int npos = 0; - while (npos < pathIterPolys.Count && polyRef != steerPosRef) + while (npos < pathIterPolyCount && polyRef != steerPosRef) { prevRef = polyRef; polyRef = pathIterPolys[npos]; @@ -131,6 +137,7 @@ namespace DotRecast.Recast.Toolset.Tools } pathIterPolys = pathIterPolys.GetRange(npos, pathIterPolys.Count - npos); + pathIterPolyCount -= npos; // Handle the connection. var status2 = navMesh.GetOffMeshConnectionPolyEndPoints(prevRef, polyRef, ref startPos, ref endPos); @@ -164,15 +171,15 @@ namespace DotRecast.Recast.Toolset.Tools } public DtStatus FindStraightPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast, - ref List polys, ref List straightPath, int straightPathOptions) + ref List polys, Span straightPath, out int straightPathCount, int maxStraightPath, int straightPathOptions) { + straightPathCount = 0; if (startRef == 0 || endRef == 0) { return DtStatus.DT_FAILURE; } polys ??= new List(); - straightPath ??= new List(); polys.Clear(); straightPath.Clear(); @@ -194,7 +201,7 @@ namespace DotRecast.Recast.Toolset.Tools } } - navQuery.FindStraightPath(startPt, epos, polys, ref straightPath, MAX_POLYS, straightPathOptions); + navQuery.FindStraightPath(startPt, epos, polys, polys.Count, straightPath, out straightPathCount, maxStraightPath, straightPathOptions); return DtStatus.DT_SUCCESS; } @@ -213,8 +220,9 @@ namespace DotRecast.Recast.Toolset.Tools } public DtStatus UpdateSlicedFindPath(DtNavMeshQuery navQuery, int maxIter, long endRef, RcVec3f startPos, RcVec3f endPos, - ref List path, ref List straightPath) + ref List path, Span straightPath, out int straightPathCount, int maxStraightPath) { + straightPathCount = 0; var status = navQuery.UpdateSlicedFindPath(maxIter, out _); if (!status.Succeeded()) @@ -224,7 +232,6 @@ namespace DotRecast.Recast.Toolset.Tools 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. @@ -238,8 +245,7 @@ namespace DotRecast.Recast.Toolset.Tools } } - straightPath = new List(MAX_POLYS); - navQuery.FindStraightPath(startPos, epos, path, ref straightPath, MAX_POLYS, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS); + navQuery.FindStraightPath(startPos, epos, path, path.Count, straightPath, out straightPathCount, maxStraightPath, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS); } return DtStatus.DT_SUCCESS; @@ -247,13 +253,12 @@ namespace DotRecast.Recast.Toolset.Tools public DtStatus Raycast(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter, - ref List polys, ref List straightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult) + ref List polys, Span straightPath, out int straightPathCount, int maxStraightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult) { + straightPathCount = 0; if (startRef == 0 || endRef == 0) { polys?.Clear(); - straightPath?.Clear(); - return DtStatus.DT_FAILURE; } @@ -267,7 +272,7 @@ namespace DotRecast.Recast.Toolset.Tools // results ... polys = path; - if (t > 1) + if (t >= 1) { // No hit hitPos = endPos; @@ -291,10 +296,8 @@ namespace DotRecast.Recast.Toolset.Tools } } - straightPath ??= new List(); - straightPath.Clear(); - straightPath.Add(new DtStraightPath(startPos, 0, 0)); - straightPath.Add(new DtStraightPath(hitPos, 0, 0)); + straightPath[straightPathCount++] = new DtStraightPath(startPos, 0, 0); + straightPath[straightPathCount++] = new DtStraightPath(hitPos, 0, 0); return status; } @@ -377,7 +380,7 @@ namespace DotRecast.Recast.Toolset.Tools float nx = (epos.Z - spos.Z) * 0.25f; float nz = -(epos.X - spos.X) * 0.25f; - var tempQueryPoly = new RcVec3f[4]; + RcVec3f[] tempQueryPoly = new RcVec3f[4]; tempQueryPoly[0].X = spos.X + nx * 1.2f; tempQueryPoly[0].Y = spos.Y + agentHeight / 2; tempQueryPoly[0].Z = spos.Z + nz * 1.2f; diff --git a/src/DotRecast.Recast.Toolset/Tools/RcTestNavmeshToolMode.cs b/src/DotRecast.Recast.Toolset/Tools/RcTestNavmeshToolMode.cs index 64159a9..fe829ef 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcTestNavmeshToolMode.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcTestNavmeshToolMode.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Collections; +using DotRecast.Core.Collections; namespace DotRecast.Recast.Toolset.Tools { diff --git a/src/DotRecast.Recast.Toolset/Tools/RcTileTool.cs b/src/DotRecast.Recast.Toolset/Tools/RcTileTool.cs index f20f763..c59ab28 100644 --- a/src/DotRecast.Recast.Toolset/Tools/RcTileTool.cs +++ b/src/DotRecast.Recast.Toolset/Tools/RcTileTool.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using DotRecast.Core; using DotRecast.Core.Collections; using DotRecast.Core.Numerics; @@ -23,7 +23,7 @@ namespace DotRecast.Recast.Toolset.Tools var bmin = geom.GetMeshBoundsMin(); var bmax = geom.GetMeshBoundsMax(); int gw = 0, gh = 0; - RcCommons.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh); + RcRecast.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh); int ts = settings.tileSize; int tw = (gw + ts - 1) / ts; @@ -47,7 +47,7 @@ namespace DotRecast.Recast.Toolset.Tools var bmin = geom.GetMeshBoundsMin(); var bmax = geom.GetMeshBoundsMax(); int gw = 0, gh = 0; - RcCommons.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh); + RcRecast.CalcGridSize(bmin, bmax, settings.cellSize, out gw, out gh); int ts = settings.tileSize; int tw = (gw + ts - 1) / ts; @@ -68,8 +68,8 @@ namespace DotRecast.Recast.Toolset.Tools tileTriCount = 0; // ... tileMemUsage = 0; // ... - var availableTileCount = navMesh.GetAvailableTileCount(); - if (0 >= availableTileCount) + var availableTile = navMesh.IsAvailableTileCount(); + if (!availableTile) { return false; } diff --git a/src/DotRecast.Recast/EdgeValues.cs b/src/DotRecast.Recast/EdgeValues.cs new file mode 100644 index 0000000..823f50a --- /dev/null +++ b/src/DotRecast.Recast/EdgeValues.cs @@ -0,0 +1,8 @@ +namespace DotRecast.Recast +{ + public static class EdgeValues + { + public const int EV_UNDEF = -1; + public const int EV_HULL = -2; + } +} \ No newline at end of file diff --git a/src/DotRecast.Recast/Geom/BoundsItem.cs b/src/DotRecast.Recast/Geom/BoundsItem.cs index d9e9f99..5255ebe 100644 --- a/src/DotRecast.Recast/Geom/BoundsItem.cs +++ b/src/DotRecast.Recast/Geom/BoundsItem.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Geom { diff --git a/src/DotRecast.Recast/Geom/BoundsItemXComparer.cs b/src/DotRecast.Recast/Geom/BoundsItemXComparer.cs index 133a672..3d630f3 100644 --- a/src/DotRecast.Recast/Geom/BoundsItemXComparer.cs +++ b/src/DotRecast.Recast/Geom/BoundsItemXComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast.Geom { diff --git a/src/DotRecast.Recast/Geom/BoundsItemYComparer.cs b/src/DotRecast.Recast/Geom/BoundsItemYComparer.cs index e97966f..78a8970 100644 --- a/src/DotRecast.Recast/Geom/BoundsItemYComparer.cs +++ b/src/DotRecast.Recast/Geom/BoundsItemYComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast.Geom { diff --git a/src/DotRecast.Recast/Geom/IInputGeomProvider.cs b/src/DotRecast.Recast/Geom/IInputGeomProvider.cs index 32215bb..2b9e28e 100644 --- a/src/DotRecast.Recast/Geom/IInputGeomProvider.cs +++ b/src/DotRecast.Recast/Geom/IInputGeomProvider.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/Geom/RcChunkyTriMesh.cs b/src/DotRecast.Recast/Geom/RcChunkyTriMesh.cs index 361483a..4e7a2ad 100644 --- a/src/DotRecast.Recast/Geom/RcChunkyTriMesh.cs +++ b/src/DotRecast.Recast/Geom/RcChunkyTriMesh.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,278 +18,14 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using System; using System.Collections.Generic; -using DotRecast.Core.Numerics; namespace DotRecast.Recast.Geom { public class RcChunkyTriMesh { - private List nodes; - private int ntris; - private int maxTrisPerChunk; - - private void CalcExtends(BoundsItem[] items, int imin, int imax, ref RcVec2f bmin, ref RcVec2f bmax) - { - bmin.X = items[imin].bmin.X; - bmin.Y = items[imin].bmin.Y; - - bmax.X = items[imin].bmax.X; - bmax.Y = items[imin].bmax.Y; - - for (int i = imin + 1; i < imax; ++i) - { - BoundsItem it = items[i]; - if (it.bmin.X < bmin.X) - { - bmin.X = it.bmin.X; - } - - if (it.bmin.Y < bmin.Y) - { - bmin.Y = it.bmin.Y; - } - - if (it.bmax.X > bmax.X) - { - bmax.X = it.bmax.X; - } - - if (it.bmax.Y > bmax.Y) - { - bmax.Y = it.bmax.Y; - } - } - } - - private int LongestAxis(float x, float y) - { - return y > x ? 1 : 0; - } - - private void Subdivide(BoundsItem[] items, int imin, int imax, int trisPerChunk, List nodes, int[] inTris) - { - int inum = imax - imin; - - RcChunkyTriMeshNode node = new RcChunkyTriMeshNode(); - nodes.Add(node); - - if (inum <= trisPerChunk) - { - // Leaf - CalcExtends(items, imin, imax, ref node.bmin, ref node.bmax); - - // Copy triangles. - node.i = nodes.Count; - node.tris = new int[inum * 3]; - - int dst = 0; - for (int i = imin; i < imax; ++i) - { - int src = items[i].i * 3; - node.tris[dst++] = inTris[src]; - node.tris[dst++] = inTris[src + 1]; - node.tris[dst++] = inTris[src + 2]; - } - } - else - { - // Split - CalcExtends(items, imin, imax, ref node.bmin, ref node.bmax); - - int axis = LongestAxis(node.bmax.X - node.bmin.X, node.bmax.Y - node.bmin.Y); - - if (axis == 0) - { - Array.Sort(items, imin, imax - imin, BoundsItemXComparer.Shared); - // Sort along x-axis - } - else if (axis == 1) - { - Array.Sort(items, imin, imax - imin, BoundsItemYComparer.Shared); - // Sort along y-axis - } - - int isplit = imin + inum / 2; - - // Left - Subdivide(items, imin, isplit, trisPerChunk, nodes, inTris); - // Right - Subdivide(items, isplit, imax, trisPerChunk, nodes, inTris); - - // Negative index means escape. - node.i = -nodes.Count; - } - } - - public RcChunkyTriMesh(float[] verts, int[] tris, int ntris, int trisPerChunk) - { - int nchunks = (ntris + trisPerChunk - 1) / trisPerChunk; - - nodes = new List(nchunks); - this.ntris = ntris; - - // Build tree - BoundsItem[] items = new BoundsItem[ntris]; - - for (int i = 0; i < ntris; i++) - { - int t = i * 3; - BoundsItem it = items[i] = new BoundsItem(); - it.i = i; - // Calc triangle XZ bounds. - it.bmin.X = it.bmax.X = verts[tris[t] * 3 + 0]; - it.bmin.Y = it.bmax.Y = verts[tris[t] * 3 + 2]; - for (int j = 1; j < 3; ++j) - { - int v = tris[t + j] * 3; - if (verts[v] < it.bmin.X) - { - it.bmin.X = verts[v]; - } - - if (verts[v + 2] < it.bmin.Y) - { - it.bmin.Y = verts[v + 2]; - } - - if (verts[v] > it.bmax.X) - { - it.bmax.X = verts[v]; - } - - if (verts[v + 2] > it.bmax.Y) - { - it.bmax.Y = verts[v + 2]; - } - } - } - - Subdivide(items, 0, ntris, trisPerChunk, nodes, tris); - - // Calc max tris per node. - maxTrisPerChunk = 0; - foreach (RcChunkyTriMeshNode node in nodes) - { - bool isLeaf = node.i >= 0; - if (!isLeaf) - { - continue; - } - - if (node.tris.Length / 3 > maxTrisPerChunk) - { - maxTrisPerChunk = node.tris.Length / 3; - } - } - } - - private bool CheckOverlapRect(float[] amin, float[] amax, RcVec2f bmin, RcVec2f bmax) - { - bool overlap = true; - overlap = (amin[0] > bmax.X || amax[0] < bmin.X) ? false : overlap; - overlap = (amin[1] > bmax.Y || amax[1] < bmin.Y) ? false : overlap; - return overlap; - } - - public List GetChunksOverlappingRect(float[] bmin, float[] bmax) - { - // Traverse tree - List ids = new List(); - int i = 0; - while (i < nodes.Count) - { - RcChunkyTriMeshNode node = nodes[i]; - bool overlap = CheckOverlapRect(bmin, bmax, node.bmin, node.bmax); - bool isLeafNode = node.i >= 0; - - if (isLeafNode && overlap) - { - ids.Add(node); - } - - if (overlap || isLeafNode) - { - i++; - } - else - { - i = -node.i; - } - } - - return ids; - } - - public List GetChunksOverlappingSegment(RcVec2f p, RcVec2f q) - { - // Traverse tree - List ids = new List(); - int i = 0; - while (i < nodes.Count) - { - RcChunkyTriMeshNode node = nodes[i]; - bool overlap = CheckOverlapSegment(p, q, node.bmin, node.bmax); - bool isLeafNode = node.i >= 0; - - if (isLeafNode && overlap) - { - ids.Add(node); - } - - if (overlap || isLeafNode) - { - i++; - } - else - { - i = -node.i; - } - } - - return ids; - } - - private bool CheckOverlapSegment(RcVec2f p, RcVec2f q, RcVec2f bmin, RcVec2f bmax) - { - const float EPSILON = 1e-6f; - - float tmin = 0; - float tmax = 1; - var d = new RcVec2f(); - d.X = q.X - p.X; - d.Y = q.Y - p.Y; - - for (int i = 0; i < 2; i++) - { - if (MathF.Abs(d.Get(i)) < EPSILON) - { - // Ray is parallel to slab. No hit if origin not within slab - if (p.Get(i) < bmin.Get(i) || p.Get(i) > bmax.Get(i)) - return false; - } - else - { - // Compute intersection t value of ray with near and far plane of slab - float ood = 1.0f / d.Get(i); - float t1 = (bmin.Get(i) - p.Get(i)) * ood; - float t2 = (bmax.Get(i) - p.Get(i)) * ood; - if (t1 > t2) - { - (t1, t2) = (t2, t1); - } - - if (t1 > tmin) - tmin = t1; - if (t2 < tmax) - tmax = t2; - if (tmin > tmax) - return false; - } - } - - return true; - } + public List nodes; + public int ntris; + public int maxTrisPerChunk; } } \ No newline at end of file diff --git a/src/DotRecast.Recast/Geom/RcChunkyTriMeshNode.cs b/src/DotRecast.Recast/Geom/RcChunkyTriMeshNode.cs index 830c6c1..0430c10 100644 --- a/src/DotRecast.Recast/Geom/RcChunkyTriMeshNode.cs +++ b/src/DotRecast.Recast/Geom/RcChunkyTriMeshNode.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Geom { diff --git a/src/DotRecast.Recast/Geom/RcChunkyTriMeshs.cs b/src/DotRecast.Recast/Geom/RcChunkyTriMeshs.cs new file mode 100644 index 0000000..25a3ff9 --- /dev/null +++ b/src/DotRecast.Recast/Geom/RcChunkyTriMeshs.cs @@ -0,0 +1,305 @@ +/* +Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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.Numerics; + +namespace DotRecast.Recast.Geom +{ + public static class RcChunkyTriMeshs + { + /// Creates partitioned triangle mesh (AABB tree), + /// where each node contains at max trisPerChunk triangles. + public static bool CreateChunkyTriMesh(float[] verts, int[] tris, int ntris, int trisPerChunk, RcChunkyTriMesh cm) + { + int nchunks = (ntris + trisPerChunk - 1) / trisPerChunk; + + cm.nodes = new List(nchunks); + cm.ntris = ntris; + + // Build tree + BoundsItem[] items = new BoundsItem[ntris]; + for (int i = 0; i < ntris; ++i) + { + items[i] = new BoundsItem(); + } + + for (int i = 0; i < ntris; i++) + { + int t = i * 3; + BoundsItem it = items[i]; + it.i = i; + // Calc triangle XZ bounds. + it.bmin.X = it.bmax.X = verts[tris[t] * 3 + 0]; + it.bmin.Y = it.bmax.Y = verts[tris[t] * 3 + 2]; + for (int j = 1; j < 3; ++j) + { + int v = tris[t + j] * 3; + if (verts[v] < it.bmin.X) + { + it.bmin.X = verts[v]; + } + + if (verts[v + 2] < it.bmin.Y) + { + it.bmin.Y = verts[v + 2]; + } + + if (verts[v] > it.bmax.X) + { + it.bmax.X = verts[v]; + } + + if (verts[v + 2] > it.bmax.Y) + { + it.bmax.Y = verts[v + 2]; + } + } + } + + Subdivide(items, 0, ntris, trisPerChunk, cm.nodes, tris); + + items = null; + + // Calc max tris per node. + cm.maxTrisPerChunk = 0; + foreach (RcChunkyTriMeshNode node in cm.nodes) + { + bool isLeaf = node.i >= 0; + if (!isLeaf) + { + continue; + } + + if (node.tris.Length / 3 > cm.maxTrisPerChunk) + { + cm.maxTrisPerChunk = node.tris.Length / 3; + } + } + + return true; + } + + /// Returns the chunk indices which overlap the input rectable. + public static List GetChunksOverlappingRect(RcChunkyTriMesh cm, RcVec2f bmin, RcVec2f bmax) + { + // Traverse tree + List ids = new List(); + int i = 0; + while (i < cm.nodes.Count) + { + RcChunkyTriMeshNode node = cm.nodes[i]; + bool overlap = CheckOverlapRect(bmin, bmax, node.bmin, node.bmax); + bool isLeafNode = node.i >= 0; + + if (isLeafNode && overlap) + { + ids.Add(node); + } + + if (overlap || isLeafNode) + { + i++; + } + else + { + i = -node.i; + } + } + + return ids; + } + + /// Returns the chunk indices which overlap the input segment. + public static List GetChunksOverlappingSegment(RcChunkyTriMesh cm, RcVec2f p, RcVec2f q) + { + // Traverse tree + List ids = new List(); + int i = 0; + while (i < cm.nodes.Count) + { + RcChunkyTriMeshNode node = cm.nodes[i]; + bool overlap = CheckOverlapSegment(p, q, node.bmin, node.bmax); + bool isLeafNode = node.i >= 0; + + if (isLeafNode && overlap) + { + ids.Add(node); + } + + if (overlap || isLeafNode) + { + i++; + } + else + { + i = -node.i; + } + } + + return ids; + } + + + private static void CalcExtends(BoundsItem[] items, int imin, int imax, ref RcVec2f bmin, ref RcVec2f bmax) + { + bmin.X = items[imin].bmin.X; + bmin.Y = items[imin].bmin.Y; + + bmax.X = items[imin].bmax.X; + bmax.Y = items[imin].bmax.Y; + + for (int i = imin + 1; i < imax; ++i) + { + BoundsItem it = items[i]; + if (it.bmin.X < bmin.X) + { + bmin.X = it.bmin.X; + } + + if (it.bmin.Y < bmin.Y) + { + bmin.Y = it.bmin.Y; + } + + if (it.bmax.X > bmax.X) + { + bmax.X = it.bmax.X; + } + + if (it.bmax.Y > bmax.Y) + { + bmax.Y = it.bmax.Y; + } + } + } + + private static int LongestAxis(float x, float y) + { + return y > x ? 1 : 0; + } + + private static void Subdivide(BoundsItem[] items, int imin, int imax, int trisPerChunk, List nodes, int[] inTris) + { + int inum = imax - imin; + + RcChunkyTriMeshNode node = new RcChunkyTriMeshNode(); + nodes.Add(node); + + if (inum <= trisPerChunk) + { + // Leaf + CalcExtends(items, imin, imax, ref node.bmin, ref node.bmax); + + // Copy triangles. + node.i = nodes.Count; + node.tris = new int[inum * 3]; + + int dst = 0; + for (int i = imin; i < imax; ++i) + { + int src = items[i].i * 3; + node.tris[dst++] = inTris[src]; + node.tris[dst++] = inTris[src + 1]; + node.tris[dst++] = inTris[src + 2]; + } + } + else + { + // Split + CalcExtends(items, imin, imax, ref node.bmin, ref node.bmax); + + int axis = LongestAxis(node.bmax.X - node.bmin.X, node.bmax.Y - node.bmin.Y); + + if (axis == 0) + { + Array.Sort(items, imin, imax - imin, BoundsItemXComparer.Shared); + // Sort along x-axis + } + else if (axis == 1) + { + Array.Sort(items, imin, imax - imin, BoundsItemYComparer.Shared); + // Sort along y-axis + } + + int isplit = imin + inum / 2; + + // Left + Subdivide(items, imin, isplit, trisPerChunk, nodes, inTris); + // Right + Subdivide(items, isplit, imax, trisPerChunk, nodes, inTris); + + // Negative index means escape. + node.i = -nodes.Count; + } + } + + private static bool CheckOverlapRect(RcVec2f amin, RcVec2f amax, RcVec2f bmin, RcVec2f bmax) + { + bool overlap = true; + overlap = (amin.X > bmax.X || amax.X < bmin.X) ? false : overlap; + overlap = (amin.Y > bmax.Y || amax.Y < bmin.Y) ? false : overlap; + return overlap; + } + + + private static bool CheckOverlapSegment(RcVec2f p, RcVec2f q, RcVec2f bmin, RcVec2f bmax) + { + const float EPSILON = 1e-6f; + + float tmin = 0; + float tmax = 1; + var d = new RcVec2f(); + d.X = q.X - p.X; + d.Y = q.Y - p.Y; + + for (int i = 0; i < 2; i++) + { + if (MathF.Abs(d.Get(i)) < EPSILON) + { + // Ray is parallel to slab. No hit if origin not within slab + if (p.Get(i) < bmin.Get(i) || p.Get(i) > bmax.Get(i)) + return false; + } + else + { + // Compute intersection t value of ray with near and far plane of slab + float ood = 1.0f / d.Get(i); + float t1 = (bmin.Get(i) - p.Get(i)) * ood; + float t2 = (bmax.Get(i) - p.Get(i)) * ood; + if (t1 > t2) + { + (t1, t2) = (t2, t1); + } + + if (t1 > tmin) + tmin = t1; + if (t2 < tmax) + tmax = t2; + if (tmin > tmax) + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Recast/Geom/RcOffMeshConnection.cs b/src/DotRecast.Recast/Geom/RcOffMeshConnection.cs index 1ef5060..aca8775 100644 --- a/src/DotRecast.Recast/Geom/RcOffMeshConnection.cs +++ b/src/DotRecast.Recast/Geom/RcOffMeshConnection.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/Geom/RcTriMesh.cs b/src/DotRecast.Recast/Geom/RcTriMesh.cs index c858260..1e45b6f 100644 --- a/src/DotRecast.Recast/Geom/RcTriMesh.cs +++ b/src/DotRecast.Recast/Geom/RcTriMesh.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -19,6 +19,7 @@ freely, subject to the following restrictions: */ using System.Collections.Generic; +using DotRecast.Core.Numerics; namespace DotRecast.Recast.Geom { @@ -32,7 +33,8 @@ namespace DotRecast.Recast.Geom { this.vertices = vertices; this.faces = faces; - chunkyTriMesh = new RcChunkyTriMesh(vertices, faces, faces.Length / 3, 32); + chunkyTriMesh = new RcChunkyTriMesh(); + RcChunkyTriMeshs.CreateChunkyTriMesh(vertices, faces, faces.Length / 3, 32, chunkyTriMesh); } public int[] GetTris() @@ -45,9 +47,9 @@ namespace DotRecast.Recast.Geom return vertices; } - public List GetChunksOverlappingRect(float[] bmin, float[] bmax) + public List GetChunksOverlappingRect(RcVec2f bmin, RcVec2f bmax) { - return chunkyTriMesh.GetChunksOverlappingRect(bmin, bmax); + return RcChunkyTriMeshs.GetChunksOverlappingRect(chunkyTriMesh, bmin, bmax); } } } \ No newline at end of file diff --git a/src/DotRecast.Recast/Geom/SimpleInputGeomProvider.cs b/src/DotRecast.Recast/Geom/SimpleInputGeomProvider.cs index ec08dd2..9c5700d 100644 --- a/src/DotRecast.Recast/Geom/SimpleInputGeomProvider.cs +++ b/src/DotRecast.Recast/Geom/SimpleInputGeomProvider.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -39,7 +39,7 @@ namespace DotRecast.Recast.Geom public static SimpleInputGeomProvider LoadFile(string objFilePath) { - byte[] chunk = RcResources.Load(objFilePath); + byte[] chunk = RcIO.ReadFileIfFound(objFilePath); var context = RcObjImporter.LoadContext(chunk); return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces); } @@ -77,12 +77,12 @@ namespace DotRecast.Recast.Geom this.faces = faces; normals = new float[faces.Length]; CalculateNormals(); - bmin = RcVecUtils.Create(vertices); - bmax = RcVecUtils.Create(vertices); + bmin = new RcVec3f(vertices); + bmax = new RcVec3f(vertices); for (int i = 1; i < vertices.Length / 3; i++) { - bmin = RcVecUtils.Min(bmin, vertices, i * 3); - bmax = RcVecUtils.Max(bmax, vertices, i * 3); + bmin = RcVec3f.Min(bmin, RcVec.Create(vertices, i * 3)); + bmax = RcVec3f.Max(bmax, RcVec.Create(vertices, i * 3)); } _mesh = new RcTriMesh(vertices, faces); diff --git a/src/DotRecast.Recast/IRcBuilderProgressListener.cs b/src/DotRecast.Recast/IRcBuilderProgressListener.cs index f10c423..498a517 100644 --- a/src/DotRecast.Recast/IRcBuilderProgressListener.cs +++ b/src/DotRecast.Recast/IRcBuilderProgressListener.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public interface IRcBuilderProgressListener { diff --git a/src/DotRecast.Recast/RcAreaModification.cs b/src/DotRecast.Recast/RcAreaModification.cs index 00df1f5..a7ba4e8 100644 --- a/src/DotRecast.Recast/RcAreaModification.cs +++ b/src/DotRecast.Recast/RcAreaModification.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcAreas.cs b/src/DotRecast.Recast/RcAreas.cs index 08c96f6..82524d1 100644 --- a/src/DotRecast.Recast/RcAreas.cs +++ b/src/DotRecast.Recast/RcAreas.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,8 +25,7 @@ using DotRecast.Core.Numerics; namespace DotRecast.Recast { - using static RcConstants; - using static RcCommons; + using static RcRecast; public static class RcAreas { @@ -457,12 +456,12 @@ namespace DotRecast.Recast int zStride = xSize; // For readability // Compute the bounding box of the polygon - RcVec3f bmin = RcVecUtils.Create(verts); - RcVec3f bmax = RcVecUtils.Create(verts); + RcVec3f bmin = new RcVec3f(verts); + RcVec3f bmax = new RcVec3f(verts); for (int i = 3; i < verts.Length; i += 3) { - bmin = RcVecUtils.Min(bmin, verts, i); - bmax = RcVecUtils.Max(bmax, verts, i); + bmin = RcVec3f.Min(bmin, RcVec.Create(verts, i)); + bmax = RcVec3f.Max(bmax, RcVec.Create(verts, i)); } bmin.Y = minY; @@ -753,19 +752,19 @@ namespace DotRecast.Recast int vertIndexB = vertIndex; int vertIndexC = (vertIndex + 1) % numVerts; - RcVec3f vertA = RcVecUtils.Create(verts, vertIndexA * 3); - RcVec3f vertB = RcVecUtils.Create(verts, vertIndexB * 3); - RcVec3f vertC = RcVecUtils.Create(verts, vertIndexC * 3); + RcVec3f vertA = RcVec.Create(verts, vertIndexA * 3); + RcVec3f vertB = RcVec.Create(verts, vertIndexB * 3); + RcVec3f vertC = RcVec.Create(verts, vertIndexC * 3); // From A to B on the x/z plane RcVec3f prevSegmentDir = RcVec3f.Subtract(vertB, vertA); prevSegmentDir.Y = 0; // Squash onto x/z plane - prevSegmentDir = RcVecUtils.SafeNormalize(prevSegmentDir); + prevSegmentDir = RcVec.SafeNormalize(prevSegmentDir); // From B to C on the x/z plane RcVec3f currSegmentDir = RcVec3f.Subtract(vertC, vertB); currSegmentDir.Y = 0; // Squash onto x/z plane - currSegmentDir = RcVecUtils.SafeNormalize(currSegmentDir); + currSegmentDir = RcVec.SafeNormalize(currSegmentDir); // The y component of the cross product of the two normalized segment directions. // The X and Z components of the cross product are both zero because the two @@ -792,7 +791,7 @@ namespace DotRecast.Recast bool bevel = cornerMiterSqMag * MITER_LIMIT * MITER_LIMIT < 1.0f; // Scale the corner miter so it's proportional to how much the corner should be offset compared to the edges. - if (cornerMiterSqMag > RcVecUtils.EPSILON) + if (cornerMiterSqMag > RcVec.EPSILON) { float scale = 1.0f / cornerMiterSqMag; cornerMiterX *= scale; diff --git a/src/DotRecast.Recast/RcAxis.cs b/src/DotRecast.Recast/RcAxis.cs index 7d957a7..5ec399b 100644 --- a/src/DotRecast.Recast/RcAxis.cs +++ b/src/DotRecast.Recast/RcAxis.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public static class RcAxis { diff --git a/src/DotRecast.Recast/RcBuildContoursFlags.cs b/src/DotRecast.Recast/RcBuildContoursFlags.cs index adcdc37..82a9f49 100644 --- a/src/DotRecast.Recast/RcBuildContoursFlags.cs +++ b/src/DotRecast.Recast/RcBuildContoursFlags.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { /// Contour build flags. /// @see rcBuildContours diff --git a/src/DotRecast.Recast/RcBuilder.cs b/src/DotRecast.Recast/RcBuilder.cs index 0620809..71acf7f 100644 --- a/src/DotRecast.Recast/RcBuilder.cs +++ b/src/DotRecast.Recast/RcBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -30,7 +30,7 @@ using DotRecast.Recast.Geom; namespace DotRecast.Recast { - using static RcCommons; + using static RcRecast; using static RcAreas; public class RcBuilder diff --git a/src/DotRecast.Recast/RcBuilderConfig.cs b/src/DotRecast.Recast/RcBuilderConfig.cs index f5bfb9e..2dd6b24 100644 --- a/src/DotRecast.Recast/RcBuilderConfig.cs +++ b/src/DotRecast.Recast/RcBuilderConfig.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -92,7 +92,7 @@ namespace DotRecast.Recast } else { - RcCommons.CalcGridSize(this.bmin, this.bmax, cfg.Cs, out width, out height); + RcRecast.CalcGridSize(this.bmin, this.bmax, cfg.Cs, out width, out height); } } } diff --git a/src/DotRecast.Recast/RcBuilderResult.cs b/src/DotRecast.Recast/RcBuilderResult.cs index ecb38b7..7af34b1 100644 --- a/src/DotRecast.Recast/RcBuilderResult.cs +++ b/src/DotRecast.Recast/RcBuilderResult.cs @@ -1,4 +1,4 @@ -using DotRecast.Core; +using DotRecast.Core; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcCompactCell.cs b/src/DotRecast.Recast/RcCompactCell.cs index befed70..43b2f8f 100644 --- a/src/DotRecast.Recast/RcCompactCell.cs +++ b/src/DotRecast.Recast/RcCompactCell.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcCompactHeightfield.cs b/src/DotRecast.Recast/RcCompactHeightfield.cs index 9148d24..d16c592 100644 --- a/src/DotRecast.Recast/RcCompactHeightfield.cs +++ b/src/DotRecast.Recast/RcCompactHeightfield.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcCompactSpan.cs b/src/DotRecast.Recast/RcCompactSpan.cs index 7f61001..8000520 100644 --- a/src/DotRecast.Recast/RcCompactSpan.cs +++ b/src/DotRecast.Recast/RcCompactSpan.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcCompactSpanBuilder.cs b/src/DotRecast.Recast/RcCompactSpanBuilder.cs index 00dd583..ca4a5f2 100644 --- a/src/DotRecast.Recast/RcCompactSpanBuilder.cs +++ b/src/DotRecast.Recast/RcCompactSpanBuilder.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcCompactSpanBuilder { diff --git a/src/DotRecast.Recast/RcCompacts.cs b/src/DotRecast.Recast/RcCompacts.cs index 5502b10..906d78d 100644 --- a/src/DotRecast.Recast/RcCompacts.cs +++ b/src/DotRecast.Recast/RcCompacts.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -20,17 +20,14 @@ freely, subject to the following restrictions: using System; using System.Linq; using DotRecast.Core; -using static DotRecast.Recast.RcConstants; - namespace DotRecast.Recast { - using static RcCommons; - + using static RcRecast; public static class RcCompacts { - private const int MAX_HEIGHT = RcConstants.RC_SPAN_MAX_HEIGHT; + private const int MAX_HEIGHT = RC_SPAN_MAX_HEIGHT; /// @} /// @name Compact Heightfield Functions diff --git a/src/DotRecast.Recast/RcConfig.cs b/src/DotRecast.Recast/RcConfig.cs index 98e7bb2..bedfbdb 100644 --- a/src/DotRecast.Recast/RcConfig.cs +++ b/src/DotRecast.Recast/RcConfig.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcConstants.cs b/src/DotRecast.Recast/RcConstants.cs deleted file mode 100644 index 15c99f2..0000000 --- a/src/DotRecast.Recast/RcConstants.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* -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. -*/ - -namespace DotRecast.Recast -{ - public static class RcConstants - { - /// Represents the null area. - /// When a data element is given this value it is considered to no longer be - /// assigned to a usable area. (E.g. It is un-walkable.) - public const int RC_NULL_AREA = 0; - - /// The default area id used to indicate a walkable polygon. - /// This is also the maximum allowed area id, and the only non-null area id - /// recognized by some steps in the build process. - public const int RC_WALKABLE_AREA = 63; - - /// The value returned by #rcGetCon if the specified direction is not connected - /// to another span. (Has no neighbor.) - public const int RC_NOT_CONNECTED = 0x3f; - - /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. - public const int RC_SPAN_HEIGHT_BITS = 20; - - /// Defines the maximum value for rcSpan::smin and rcSpan::smax. - public const int RC_SPAN_MAX_HEIGHT = (1 << RC_SPAN_HEIGHT_BITS) - 1; - - /// The number of spans allocated per span spool. - /// @see rcSpanPool - public const int RC_SPANS_PER_POOL = 2048; - - /// Heighfield border flag. - /// If a heightfield region ID has this bit set, then the region is a border - /// region and its spans are considered unwalkable. - /// (Used during the region and contour build process.) - /// @see rcCompactSpan::reg - public const int RC_BORDER_REG = 0x8000; - - /// Polygon touches multiple regions. - /// If a polygon has this region ID it was merged with or created - /// from polygons of different regions during the polymesh - /// build step that removes redundant border vertices. - /// (Used during the polymesh and detail polymesh build processes) - /// @see rcPolyMesh::regs - public const int RC_MULTIPLE_REGS = 0; - - // Border vertex flag. - /// If a region ID has this bit set, then the associated element lies on - /// a tile border. If a contour vertex's region ID has this bit set, the - /// vertex will later be removed in order to match the segments and vertices - /// at tile boundaries. - /// (Used during the build process.) - /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts - public const int RC_BORDER_VERTEX = 0x10000; - - /// Area border flag. - /// If a region ID has this bit set, then the associated element lies on - /// the border of an area. - /// (Used during the region and contour build process.) - /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts - public const int RC_AREA_BORDER = 0x20000; - - /// Applied to the region id field of contour vertices in order to extract the region id. - /// The region id field of a vertex may have several flags applied to it. So the - /// fields value can't be used directly. - /// @see rcContour::verts, rcContour::rverts - public const int RC_CONTOUR_REG_MASK = 0xffff; - - /// A value which indicates an invalid index within a mesh. - /// @note This does not necessarily indicate an error. - /// @see rcPolyMesh::polys - public const int RC_MESH_NULL_IDX = 0xffff; - - public const int RC_LOG_WARNING = 1; - } -} \ No newline at end of file diff --git a/src/DotRecast.Recast/RcContour.cs b/src/DotRecast.Recast/RcContour.cs index 2deb6d5..631f943 100644 --- a/src/DotRecast.Recast/RcContour.cs +++ b/src/DotRecast.Recast/RcContour.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -20,25 +20,14 @@ freely, subject to the following restrictions: namespace DotRecast.Recast { - /** Represents a simple, non-overlapping contour in field space. */ + /// Represents a simple, non-overlapping contour in field space. public class RcContour { - /** Simplified contour vertex and connection data. [Size: 4 * #nverts] */ - public int[] verts; - - /** The number of vertices in the simplified contour. */ - public int nverts; - - /** Raw contour vertex and connection data. [Size: 4 * #nrverts] */ - public int[] rverts; - - /** The number of vertices in the raw contour. */ - public int nrverts; - - /** The region id of the contour. */ - public int area; - - /** The area id of the contour. */ - public int reg; + public int[] verts; //< Simplified contour vertex and connection data. [Size: 4 * #nverts] + public int nverts; //< The number of vertices in the simplified contour. + public int[] rverts; //< Raw contour vertex and connection data. [Size: 4 * #nrverts] + public int nrverts; //< The number of vertices in the raw contour. + public int reg; //< The region id of the contour. + public int area; //< The area id of the contour. } } \ No newline at end of file diff --git a/src/DotRecast.Recast/RcContourHole.cs b/src/DotRecast.Recast/RcContourHole.cs index 6c3b027..f0c040f 100644 --- a/src/DotRecast.Recast/RcContourHole.cs +++ b/src/DotRecast.Recast/RcContourHole.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcContourHole { diff --git a/src/DotRecast.Recast/RcContourHoleComparer.cs b/src/DotRecast.Recast/RcContourHoleComparer.cs index ec21512..5817ee0 100644 --- a/src/DotRecast.Recast/RcContourHoleComparer.cs +++ b/src/DotRecast.Recast/RcContourHoleComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcContourRegion.cs b/src/DotRecast.Recast/RcContourRegion.cs index 9e8f715..17cd845 100644 --- a/src/DotRecast.Recast/RcContourRegion.cs +++ b/src/DotRecast.Recast/RcContourRegion.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcContourRegion { diff --git a/src/DotRecast.Recast/RcContourSet.cs b/src/DotRecast.Recast/RcContourSet.cs index ed0e6f1..466ed5f 100644 --- a/src/DotRecast.Recast/RcContourSet.cs +++ b/src/DotRecast.Recast/RcContourSet.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcContours.cs b/src/DotRecast.Recast/RcContours.cs index 2adba4f..3719d4a 100644 --- a/src/DotRecast.Recast/RcContours.cs +++ b/src/DotRecast.Recast/RcContours.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,8 +24,8 @@ using DotRecast.Core; namespace DotRecast.Recast { - using static RcConstants; - using static RcCommons; + + using static RcRecast; public static class RcContours { diff --git a/src/DotRecast.Recast/RcConvexVolume.cs b/src/DotRecast.Recast/RcConvexVolume.cs index 591859b..5dd3140 100644 --- a/src/DotRecast.Recast/RcConvexVolume.cs +++ b/src/DotRecast.Recast/RcConvexVolume.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcDirtyEntry.cs b/src/DotRecast.Recast/RcDirtyEntry.cs index 92e7caf..dd05d65 100644 --- a/src/DotRecast.Recast/RcDirtyEntry.cs +++ b/src/DotRecast.Recast/RcDirtyEntry.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { // Struct to keep track of entries in the region table that have been changed. public readonly struct RcDirtyEntry diff --git a/src/DotRecast.Recast/RcFilledVolumeRasterization.cs b/src/DotRecast.Recast/RcFilledVolumeRasterization.cs index ddc11ae..5d490b5 100644 --- a/src/DotRecast.Recast/RcFilledVolumeRasterization.cs +++ b/src/DotRecast.Recast/RcFilledVolumeRasterization.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -20,11 +20,11 @@ freely, subject to the following restrictions: using System; using DotRecast.Core; using DotRecast.Core.Numerics; -using static DotRecast.Recast.RcConstants; - namespace DotRecast.Recast { + using static RcRecast; + public static class RcFilledVolumeRasterization { private const float EPSILON = 0.00001f; @@ -176,7 +176,7 @@ namespace DotRecast.Recast private static void Plane(float[][] planes, int p, float[] v1, float[] v2, float[] vertices, int vert) { - RcVecUtils.Cross(planes[p], v1, v2); + RcVec.Cross(planes[p], v1, v2); planes[p][3] = planes[p][0] * vertices[vert] + planes[p][1] * vertices[vert + 1] + planes[p][2] * vertices[vert + 2]; } @@ -296,8 +296,8 @@ namespace DotRecast.Recast if (axis.Y * axis.Y > EPSILON) { - RcVec3f[] rectangleOnStartPlane = new RcVec3f[4]; - RcVec3f[] rectangleOnEndPlane = new RcVec3f[4]; + Span rectangleOnStartPlane = stackalloc RcVec3f[4]; + Span rectangleOnEndPlane = stackalloc RcVec3f[4]; float ds = RcVec3f.Dot(axis, start); float de = RcVec3f.Dot(axis, end); for (int i = 0; i < 4; i++) @@ -326,7 +326,7 @@ namespace DotRecast.Recast return s; } - private static float[] CylinderCapIntersection(RcVec3f start, float radiusSqr, float[] s, int i, RcVec3f[] rectangleOnPlane) + private static float[] CylinderCapIntersection(RcVec3f start, float radiusSqr, float[] s, int i, Span rectangleOnPlane) { int j = (i + 1) % 4; // Ray against sphere intersection @@ -507,8 +507,8 @@ namespace DotRecast.Recast for (int i = 0; i < 8; i++) { int vi = i * 3; - if (vertices[vi] >= rectangle[0] && vertices[vi] < rectangle[2] && vertices[vi + 2] >= rectangle[1] - && vertices[vi + 2] < rectangle[3]) + if (vertices[vi] >= rectangle[0] && vertices[vi] < rectangle[2] && + vertices[vi + 2] >= rectangle[1] && vertices[vi + 2] < rectangle[3]) { yMin = Math.Min(yMin, vertices[vi + 1]); yMax = Math.Max(yMax, vertices[vi + 1]); @@ -525,7 +525,7 @@ namespace DotRecast.Recast { if (MathF.Abs(planes[j][1]) > EPSILON) { - float dotNormalPoint = RcVecUtils.Dot(planes[j], point); + float dotNormalPoint = RcVec3f.Dot(new RcVec3f(planes[j]), point); float t = (planes[j][3] - dotNormalPoint) / planes[j][1]; float y = point.Y + t; bool valid = true; @@ -729,15 +729,15 @@ namespace DotRecast.Recast private static bool RayTriangleIntersection(RcVec3f point, int plane, float[][] planes, out float y) { y = 0.0f; - float t = (planes[plane][3] - RcVecUtils.Dot(planes[plane], point)) / planes[plane][1]; - float[] s = { point.X, point.Y + t, point.Z }; - float u = RcVecUtils.Dot(s, planes[plane + 1]) - planes[plane + 1][3]; + float t = (planes[plane][3] - RcVec3f.Dot(new RcVec3f(planes[plane]), point)) / planes[plane][1]; + RcVec3f s = new RcVec3f(point.X, point.Y + t, point.Z); + float u = RcVec3f.Dot(s, new RcVec3f(planes[plane + 1])) - planes[plane + 1][3]; if (u < 0.0f || u > 1.0f) { return false; } - float v = RcVecUtils.Dot(s, planes[plane + 2]) - planes[plane + 2][3]; + float v = RcVec3f.Dot(s, new RcVec3f(planes[plane + 2])) - planes[plane + 2][3]; if (v < 0.0f) { return false; @@ -749,7 +749,7 @@ namespace DotRecast.Recast return false; } - y = s[1]; + y = s.Y; return true; } diff --git a/src/DotRecast.Recast/RcFilters.cs b/src/DotRecast.Recast/RcFilters.cs index f373677..6e6842a 100644 --- a/src/DotRecast.Recast/RcFilters.cs +++ b/src/DotRecast.Recast/RcFilters.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -23,8 +23,8 @@ using DotRecast.Core; namespace DotRecast.Recast { - using static RcConstants; - using static RcCommons; + + using static RcRecast; public static class RcFilters { diff --git a/src/DotRecast.Recast/RcHeightPatch.cs b/src/DotRecast.Recast/RcHeightPatch.cs index 072aa13..e811937 100644 --- a/src/DotRecast.Recast/RcHeightPatch.cs +++ b/src/DotRecast.Recast/RcHeightPatch.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcHeightPatch { diff --git a/src/DotRecast.Recast/RcHeightfield.cs b/src/DotRecast.Recast/RcHeightfield.cs index b5eaba9..c71ef3c 100644 --- a/src/DotRecast.Recast/RcHeightfield.cs +++ b/src/DotRecast.Recast/RcHeightfield.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcHeightfieldLayer.cs b/src/DotRecast.Recast/RcHeightfieldLayer.cs index 83971dd..d8c1f4b 100644 --- a/src/DotRecast.Recast/RcHeightfieldLayer.cs +++ b/src/DotRecast.Recast/RcHeightfieldLayer.cs @@ -1,4 +1,4 @@ -using DotRecast.Core.Numerics; +using DotRecast.Core.Numerics; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcHeightfieldLayerSet.cs b/src/DotRecast.Recast/RcHeightfieldLayerSet.cs index 5348302..1bbf740 100644 --- a/src/DotRecast.Recast/RcHeightfieldLayerSet.cs +++ b/src/DotRecast.Recast/RcHeightfieldLayerSet.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcLayerRegion.cs b/src/DotRecast.Recast/RcLayerRegion.cs index 7c94007..873d02a 100644 --- a/src/DotRecast.Recast/RcLayerRegion.cs +++ b/src/DotRecast.Recast/RcLayerRegion.cs @@ -1,23 +1,23 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast { public class RcLayerRegion { - public int id; - public int layerId; // Layer ID - public bool @base; // Flag indicating if the region is the base of merged regions. + public readonly int index; + public List layers; + public List neis; public int ymin, ymax; - public List layers; // Layer count - public List neis; // Neighbour count + public byte layerId; // Layer ID + public bool @base; // Flag indicating if the region is the base of merged regions. public RcLayerRegion(int i) { - id = i; - ymin = 0xFFFF; - layerId = 0xff; + index = i; layers = new List(); neis = new List(); + ymin = 0xFFFF; + layerId = 0xff; } }; } \ No newline at end of file diff --git a/src/DotRecast.Recast/RcLayerSweepSpan.cs b/src/DotRecast.Recast/RcLayerSweepSpan.cs index f0c2a7d..95371c9 100644 --- a/src/DotRecast.Recast/RcLayerSweepSpan.cs +++ b/src/DotRecast.Recast/RcLayerSweepSpan.cs @@ -1,9 +1,9 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcLayerSweepSpan { public int ns; // number samples - public int id; // region id - public int nei; // neighbour id + public byte id; // region id + public byte nei; // neighbour id }; } \ No newline at end of file diff --git a/src/DotRecast.Recast/RcLayers.cs b/src/DotRecast.Recast/RcLayers.cs index cf22d2a..e8b5911 100644 --- a/src/DotRecast.Recast/RcLayers.cs +++ b/src/DotRecast.Recast/RcLayers.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,15 +25,10 @@ using DotRecast.Core.Numerics; namespace DotRecast.Recast { - using static RcConstants; - using static RcCommons; + using static RcRecast; public static class RcLayers { - const int RC_MAX_LAYERS = RcConstants.RC_NOT_CONNECTED; - const int RC_MAX_NEIS = 16; - - private static void AddUnique(List a, int v) { if (!a.Contains(v)) @@ -61,7 +56,6 @@ namespace DotRecast.Recast /// @name Layer, Contour, Polymesh, and Detail Mesh Functions /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail /// @{ - /// Builds a layer set from the specified compact heightfield. /// @ingroup recast /// @param[in,out] ctx The build context to use during the operation. @@ -79,11 +73,11 @@ namespace DotRecast.Recast int w = chf.width; int h = chf.height; - - int[] srcReg = new int[chf.spanCount]; - Array.Fill(srcReg, 0xFF); - - int nsweeps = chf.width; // Math.Max(chf.width, chf.height); + + Span srcReg = stackalloc byte[chf.spanCount]; + srcReg.Fill(0xFF); + + int nsweeps = chf.width; RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps]; for (int i = 0; i < sweeps.Length; i++) { @@ -91,15 +85,15 @@ namespace DotRecast.Recast } // Partition walkable area into monotone regions. - int[] prevCount = new int[256]; - int regId = 0; - + Span prevCount = stackalloc int[256]; + byte regId = 0; + // Sweep one line at a time. for (int y = borderSize; y < h - borderSize; ++y) { // Collect spans from this row. - Array.Fill(prevCount, 0); - int sweepId = 0; + prevCount.Fill(0); + byte sweepId = 0; for (int x = borderSize; x < w - borderSize; ++x) { @@ -110,9 +104,9 @@ namespace DotRecast.Recast ref RcCompactSpan s = ref chf.spans[i]; if (chf.areas[i] == RC_NULL_AREA) continue; - - int sid = 0xFF; - + + byte sid = 0xFF; + // -x if (GetCon(ref s, 0) != RC_NOT_CONNECTED) { @@ -136,7 +130,7 @@ namespace DotRecast.Recast int ax = x + GetDirOffsetX(3); int ay = y + GetDirOffsetY(3); int ai = chf.cells[ax + ay * w].index + GetCon(ref s, 3); - int nr = srcReg[ai]; + byte nr = srcReg[ai]; if (nr != 0xff) { // Set neighbour when first valid neighbour is encoutered. @@ -266,9 +260,11 @@ namespace DotRecast.Recast } // Create 2D layers from regions. - int layerId = 0; + byte layerId = 0; - List stack = new List(); + const int MAX_STACK = 64; + Span stack = stackalloc byte[MAX_STACK]; + int nstack = 0; for (int i = 0; i < nregs; ++i) { @@ -281,14 +277,16 @@ namespace DotRecast.Recast root.layerId = layerId; root.@base = true; - stack.Add(i); + nstack = 0; + stack[nstack++] = ((byte)i); - while (stack.Count != 0) + while (0 != nstack) { // Pop front - int pop = stack[0]; // TODO : 여기에 stack 처럼 작동하게 했는데, 스택인지는 모르겠음 - stack.RemoveAt(0); - RcLayerRegion reg = regs[pop]; + RcLayerRegion reg = regs[stack[0]]; + nstack--; + for (int j = 0; j < nstack; ++j) + stack[j] = stack[j + 1]; foreach (int nei in reg.neis) { @@ -307,19 +305,22 @@ namespace DotRecast.Recast if ((ymax - ymin) >= 255) continue; - // Deepen - stack.Add(nei); - - // Mark layer id - regn.layerId = layerId; - // Merge current layers to root. - foreach (int layer in regn.layers) + if (nstack < MAX_STACK) { - AddUnique(root.layers, layer); - } + // Deepen + stack[nstack++] = (byte)nei; - root.ymin = Math.Min(root.ymin, regn.ymin); - root.ymax = Math.Max(root.ymax, regn.ymax); + // Mark layer id + regn.layerId = layerId; + // Merge current layers to root. + foreach (int layer in regn.layers) + { + AddUnique(root.layers, layer); + } + + root.ymin = Math.Min(root.ymin, regn.ymin); + root.ymax = Math.Max(root.ymax, regn.ymax); + } } } @@ -335,7 +336,7 @@ namespace DotRecast.Recast if (!ri.@base) continue; - int newId = ri.layerId; + byte newId = ri.layerId; for (;;) { @@ -411,7 +412,7 @@ namespace DotRecast.Recast } // Compact layerIds - int[] remap = new int[256]; + Span remap = stackalloc byte[256]; // Find number of unique layers. layerId = 0; @@ -450,7 +451,7 @@ namespace DotRecast.Recast bmin.Z += borderSize * chf.cs; bmax.X -= borderSize * chf.cs; bmax.Z -= borderSize * chf.cs; - + lset = new RcHeightfieldLayerSet(); lset.layers = new RcHeightfieldLayer[layerId]; for (int i = 0; i < lset.layers.Length; i++) diff --git a/src/DotRecast.Recast/RcLevelStackEntry.cs b/src/DotRecast.Recast/RcLevelStackEntry.cs index 6648f20..8feda76 100644 --- a/src/DotRecast.Recast/RcLevelStackEntry.cs +++ b/src/DotRecast.Recast/RcLevelStackEntry.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public readonly struct RcLevelStackEntry { diff --git a/src/DotRecast.Recast/RcMeshDetails.cs b/src/DotRecast.Recast/RcMeshDetails.cs index f3fbcca..aabb3d0 100644 --- a/src/DotRecast.Recast/RcMeshDetails.cs +++ b/src/DotRecast.Recast/RcMeshDetails.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -22,178 +22,57 @@ using System; using System.Collections.Generic; using DotRecast.Core; using DotRecast.Core.Numerics; -using static DotRecast.Recast.RcConstants; + namespace DotRecast.Recast { - using static RcCommons; - + using static RcRecast; + using static RcVec; + using static EdgeValues; public static class RcMeshDetails { - public const int MAX_VERTS = 127; - public const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). - public const int MAX_VERTS_PER_EDGE = 32; - - public const int RC_UNSET_HEIGHT = RcConstants.RC_SPAN_MAX_HEIGHT; - public const int EV_UNDEF = -1; - public const int EV_HULL = -2; + public const int RC_UNSET_HEIGHT = RC_SPAN_MAX_HEIGHT; - private static float Vdot2(float[] a, float[] b) - { - return a[0] * b[0] + a[2] * b[2]; - } - - private static float Vdot2(RcVec3f a, RcVec3f b) - { - return a.X * b.X + a.Z * b.Z; - } - - - private static float VdistSq2(float[] verts, int p, int q) - { - float dx = verts[q + 0] - verts[p + 0]; - float dy = verts[q + 2] - verts[p + 2]; - return dx * dx + dy * dy; - } - - private static float Vdist2(float[] verts, int p, int q) - { - return MathF.Sqrt(VdistSq2(verts, p, q)); - } - - private static float VdistSq2(float[] p, float[] q) - { - float dx = q[0] - p[0]; - float dy = q[2] - p[2]; - return dx * dx + dy * dy; - } - - private static float VdistSq2(float[] p, RcVec3f q) - { - float dx = q.X - p[0]; - float dy = q.Z - p[2]; - return dx * dx + dy * dy; - } - - - private static float VdistSq2(RcVec3f p, RcVec3f q) - { - float dx = q.X - p.X; - float dy = q.Z - p.Z; - return dx * dx + dy * dy; - } - - - private static float Vdist2(float[] p, float[] q) - { - return MathF.Sqrt(VdistSq2(p, q)); - } - - private static float Vdist2(RcVec3f p, RcVec3f q) - { - return MathF.Sqrt(VdistSq2(p, q)); - } - - private static float Vdist2(float[] p, RcVec3f q) - { - return MathF.Sqrt(VdistSq2(p, q)); - } - - - private static float VdistSq2(float[] p, float[] verts, int q) - { - float dx = verts[q + 0] - p[0]; - float dy = verts[q + 2] - p[2]; - return dx * dx + dy * dy; - } - - private static float VdistSq2(RcVec3f p, float[] verts, int q) - { - float dx = verts[q + 0] - p.X; - float dy = verts[q + 2] - p.Z; - return dx * dx + dy * dy; - } - - - private static float Vdist2(float[] p, float[] verts, int q) - { - return MathF.Sqrt(VdistSq2(p, verts, q)); - } - - private static float Vdist2(RcVec3f p, float[] verts, int q) - { - return MathF.Sqrt(VdistSq2(p, verts, q)); - } - - - private static float Vcross2(float[] verts, int p1, int p2, int p3) - { - float u1 = verts[p2 + 0] - verts[p1 + 0]; - float v1 = verts[p2 + 2] - verts[p1 + 2]; - float u2 = verts[p3 + 0] - verts[p1 + 0]; - float v2 = verts[p3 + 2] - verts[p1 + 2]; - return u1 * v2 - v1 * u2; - } - - private static float Vcross2(float[] p1, float[] p2, float[] p3) - { - float u1 = p2[0] - p1[0]; - float v1 = p2[2] - p1[2]; - float u2 = p3[0] - p1[0]; - float v2 = p3[2] - p1[2]; - return u1 * v2 - v1 * u2; - } - - private static float Vcross2(RcVec3f p1, RcVec3f p2, RcVec3f p3) - { - float u1 = p2.X - p1.X; - float v1 = p2.Z - p1.Z; - float u2 = p3.X - p1.X; - float v2 = p3.Z - p1.Z; - return u1 * v2 - v1 * u2; - } - - - private static bool CircumCircle(float[] verts, int p1, int p2, int p3, ref RcVec3f c, RcAtomicFloat r) + public static bool CircumCircle(RcVec3f p1, RcVec3f p2, RcVec3f p3, ref RcVec3f c, out float r) { const float EPS = 1e-6f; // Calculate the circle relative to p1, to avoid some precision issues. var v1 = new RcVec3f(); - var v2 = RcVecUtils.Subtract(verts, p2, p1); - var v3 = RcVecUtils.Subtract(verts, p3, p1); + var v2 = p2 - p1; + var v3 = p3 - p1; - float cp = Vcross2(v1, v2, v3); + float cp = Cross2(v1, v2, v3); if (MathF.Abs(cp) > EPS) { - float v1Sq = Vdot2(v1, v1); - float v2Sq = Vdot2(v2, v2); - float v3Sq = Vdot2(v3, v3); + float v1Sq = Dot2(v1, v1); + float v2Sq = Dot2(v2, v2); + float v3Sq = Dot2(v3, v3); c.X = (v1Sq * (v2.Z - v3.Z) + v2Sq * (v3.Z - v1.Z) + v3Sq * (v1.Z - v2.Z)) / (2 * cp); c.Y = 0; c.Z = (v1Sq * (v3.X - v2.X) + v2Sq * (v1.X - v3.X) + v3Sq * (v2.X - v1.X)) / (2 * cp); - r.Exchange(Vdist2(c, v1)); - c = RcVecUtils.Add(c, verts, p1); + r = Dist2(c, v1); + c = c + p1; return true; } - c = RcVecUtils.Create(verts, p1); - r.Exchange(0f); + c = p1; + r = 0f; return false; } - private static float DistPtTri(RcVec3f p, float[] verts, int a, int b, int c) + public static float DistPtTri(RcVec3f p, RcVec3f a, RcVec3f b, RcVec3f c) { - var v0 = RcVecUtils.Subtract(verts, c, a); - var v1 = RcVecUtils.Subtract(verts, b, a); - var v2 = RcVecUtils.Subtract(p, verts, a); + var v0 = c - a; + var v1 = b - a; + var v2 = p - a; - float dot00 = Vdot2(v0, v0); - float dot01 = Vdot2(v0, v1); - float dot02 = Vdot2(v0, v2); - float dot11 = Vdot2(v1, v1); - float dot12 = Vdot2(v1, v2); + float dot00 = Dot2(v0, v0); + float dot01 = Dot2(v0, v1); + float dot02 = Dot2(v0, v2); + float dot11 = Dot2(v1, v1); + float dot12 = Dot2(v1, v2); // Compute barycentric coordinates float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); @@ -204,14 +83,14 @@ namespace DotRecast.Recast const float EPS = 1e-4f; if (u >= -EPS && v >= -EPS && (u + v) <= 1 + EPS) { - float y = verts[a + 1] + v0.Y * u + v1.Y * v; + float y = a.Y + v0.Y * u + v1.Y * v; return MathF.Abs(y - p.Y); } return float.MaxValue; } - private static float DistancePtSeg(float[] verts, int pt, int p, int q) + public static float DistancePtSeg(float[] verts, int pt, int p, int q) { float pqx = verts[q + 0] - verts[p + 0]; float pqy = verts[q + 1] - verts[p + 1]; @@ -242,7 +121,7 @@ namespace DotRecast.Recast return dx * dx + dy * dy + dz * dz; } - private static float DistancePtSeg2d(RcVec3f verts, float[] poly, int p, int q) + public static float DistancePtSeg2d(RcVec3f verts, float[] poly, int p, int q) { float pqx = poly[q + 0] - poly[p + 0]; float pqz = poly[q + 2] - poly[p + 2]; @@ -270,7 +149,7 @@ namespace DotRecast.Recast return dx * dx + dz * dz; } - private static float DistancePtSeg2d(float[] verts, int pt, float[] poly, int p, int q) + public static float DistancePtSeg2d(float[] verts, int pt, float[] poly, int p, int q) { float pqx = poly[q + 0] - poly[p + 0]; float pqz = poly[q + 2] - poly[p + 2]; @@ -298,15 +177,15 @@ namespace DotRecast.Recast return dx * dx + dz * dz; } - private static float DistToTriMesh(RcVec3f p, float[] verts, int nverts, List tris, int ntris) + public static float DistToTriMesh(RcVec3f p, float[] verts, int nverts, List tris, int ntris) { float dmin = float.MaxValue; for (int i = 0; i < ntris; ++i) { - int va = tris[i * 4 + 0] * 3; - int vb = tris[i * 4 + 1] * 3; - int vc = tris[i * 4 + 2] * 3; - float d = DistPtTri(p, verts, va, vb, vc); + RcVec3f va = RcVec.Create(verts, tris[i * 4 + 0] * 3); + RcVec3f vb = RcVec.Create(verts, tris[i * 4 + 1] * 3); + RcVec3f vc = RcVec.Create(verts, tris[i * 4 + 2] * 3); + float d = DistPtTri(p, va, vb, vc); if (d < dmin) { dmin = d; @@ -321,7 +200,7 @@ namespace DotRecast.Recast return dmin; } - private static float DistToPoly(int nvert, float[] verts, RcVec3f p) + public static float DistToPoly(int nvert, float[] verts, RcVec3f p) { float dmin = float.MaxValue; int i, j; @@ -342,7 +221,7 @@ namespace DotRecast.Recast return c ? -dmin : dmin; } - private static int GetHeight(float fx, float fy, float fz, float cs, float ics, float ch, int radius, + public static int GetHeight(float fx, float fy, float fz, float cs, float ics, float ch, int radius, RcHeightPatch hp) { int ix = (int)MathF.Floor(fx * ics + 0.01f); @@ -425,7 +304,7 @@ namespace DotRecast.Recast return h; } - private static int FindEdge(List edges, int s, int t) + public static int FindEdge(List edges, int s, int t) { for (int i = 0; i < edges.Count / 4; i++) { @@ -439,7 +318,7 @@ namespace DotRecast.Recast return EV_UNDEF; } - private static void AddEdge(RcContext ctx, List edges, int maxEdges, int s, int t, int l, int r) + public static void AddEdge(RcContext ctx, List edges, int maxEdges, int s, int t, int l, int r) { if (edges.Count / 4 >= maxEdges) { @@ -457,7 +336,7 @@ namespace DotRecast.Recast } } - private static void UpdateLeftFace(List edges, int e, int s, int t, int f) + public static void UpdateLeftFace(List edges, int e, int s, int t, int f) { if (edges[e + 0] == s && edges[e + 1] == t && edges[e + 2] == EV_UNDEF) { @@ -469,13 +348,13 @@ namespace DotRecast.Recast } } - private static bool OverlapSegSeg2d(float[] verts, int a, int b, int c, int d) + public static bool OverlapSegSeg2d(float[] verts, int a, int b, int c, int d) { - float a1 = Vcross2(verts, a, b, d); - float a2 = Vcross2(verts, a, b, c); + float a1 = Cross2(verts, a, b, d); + float a2 = Cross2(verts, a, b, c); if (a1 * a2 < 0.0f) { - float a3 = Vcross2(verts, c, d, a); + float a3 = Cross2(verts, c, d, a); float a4 = a3 + a2 - a1; if (a3 * a4 < 0.0f) { @@ -486,7 +365,7 @@ namespace DotRecast.Recast return false; } - private static bool OverlapEdges(float[] pts, List edges, int s1, int t1) + public static bool OverlapEdges(float[] pts, List edges, int s1, int t1) { for (int i = 0; i < edges.Count / 4; ++i) { @@ -507,7 +386,7 @@ namespace DotRecast.Recast return false; } - static int CompleteFacet(RcContext ctx, float[] pts, int npts, List edges, int maxEdges, int nfaces, int e) + public static int CompleteFacet(RcContext ctx, float[] pts, int npts, List edges, int maxEdges, int nfaces, int e) { const float EPS = 1e-5f; @@ -534,7 +413,7 @@ namespace DotRecast.Recast // Find best point on left of edge. int pt = npts; RcVec3f c = new RcVec3f(); - RcAtomicFloat r = new RcAtomicFloat(-1f); + float r = -1f; for (int u = 0; u < npts; ++u) { if (u == s || u == t) @@ -542,28 +421,32 @@ namespace DotRecast.Recast continue; } - if (Vcross2(pts, s * 3, t * 3, u * 3) > EPS) + RcVec3f vs = RcVec.Create(pts, s * 3); + RcVec3f vt = RcVec.Create(pts, t * 3); + RcVec3f vu = RcVec.Create(pts, u * 3); + + if (Cross2(vs, vt, vu) > EPS) { - if (r.Get() < 0) + if (r < 0) { // The circle is not updated yet, do it now. pt = u; - CircumCircle(pts, s * 3, t * 3, u * 3, ref c, r); + CircumCircle(vs, vt, vu, ref c, out r); continue; } - float d = Vdist2(c, pts, u * 3); + float d = Dist2(c, vu); float tol = 0.001f; - if (d > r.Get() * (1 + tol)) + if (d > r * (1 + tol)) { // Outside current circumcircle, skip. continue; } - else if (d < r.Get() * (1 - tol)) + else if (d < r * (1 - tol)) { // Inside safe circumcircle, update circle. pt = u; - CircumCircle(pts, s * 3, t * 3, u * 3, ref c, r); + CircumCircle(vs, vt, vu, ref c, out r); } else { @@ -581,7 +464,7 @@ namespace DotRecast.Recast // Edge is valid. pt = u; - CircumCircle(pts, s * 3, t * 3, u * 3, ref c, r); + CircumCircle(vs, vt, vu, ref c, out r); } } } @@ -624,7 +507,7 @@ namespace DotRecast.Recast return nfaces; } - private static void DelaunayHull(RcContext ctx, int npts, float[] pts, int nhull, int[] hull, List tris) + public static void DelaunayHull(RcContext ctx, int npts, float[] pts, int nhull, int[] hull, List tris) { int nfaces = 0; int maxEdges = npts * 10; @@ -720,7 +603,7 @@ namespace DotRecast.Recast } // Calculate minimum extend of the polygon. - private static float PolyMinExtent(float[] verts, int nverts) + public static float PolyMinExtent(float[] verts, int nverts) { float minDist = float.MaxValue; for (int i = 0; i < nverts; i++) @@ -746,7 +629,7 @@ namespace DotRecast.Recast return MathF.Sqrt(minDist); } - private static void TriangulateHull(int nverts, float[] verts, int nhull, int[] hull, int nin, List tris) + public static void TriangulateHull(int nverts, float[] verts, int nhull, int[] hull, int nin, List tris) { int start = 0, left = 1, right = nhull - 1; @@ -766,7 +649,7 @@ namespace DotRecast.Recast int pv = hull[pi] * 3; int cv = hull[i] * 3; int nv = hull[ni] * 3; - float d = Vdist2(verts, pv, cv) + Vdist2(verts, cv, nv) + Vdist2(verts, nv, pv); + float d = Dist2(verts, pv, cv) + Dist2(verts, cv, nv) + Dist2(verts, nv, pv); if (d < dmin) { start = i; @@ -796,8 +679,8 @@ namespace DotRecast.Recast int nvleft = hull[nleft] * 3; int cvright = hull[right] * 3; int nvright = hull[nright] * 3; - float dleft = Vdist2(verts, cvleft, nvleft) + Vdist2(verts, nvleft, cvright); - float dright = Vdist2(verts, cvright, nvright) + Vdist2(verts, cvleft, nvright); + float dleft = Dist2(verts, cvleft, nvleft) + Dist2(verts, nvleft, cvright); + float dright = Dist2(verts, cvright, nvright) + Dist2(verts, cvleft, nvright); if (dleft < dright) { @@ -818,33 +701,37 @@ namespace DotRecast.Recast } } - private static float GetJitterX(int i) + public static float GetJitterX(int i) { return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f; } - private static float GetJitterY(int i) + public static float GetJitterY(int i) { return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f; } - static int BuildPolyDetail(RcContext ctx, float[] @in, int nin, float sampleDist, float sampleMaxError, - int heightSearchRadius, RcCompactHeightfield chf, RcHeightPatch hp, float[] verts, List tris) + public static int BuildPolyDetail(RcContext ctx, float[] @in, int nin, + float sampleDist, float sampleMaxError, + int heightSearchRadius, RcCompactHeightfield chf, + RcHeightPatch hp, float[] verts, + ref List tris, ref List edges, ref List samples) { - List samples = new List(512); - - int nverts = 0; + const int MAX_VERTS = 127; + const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). + const int MAX_VERTS_PER_EDGE = 32; float[] edge = new float[(MAX_VERTS_PER_EDGE + 1) * 3]; int[] hull = new int[MAX_VERTS]; int nhull = 0; - nverts = nin; + int nverts = nin; for (int i = 0; i < nin; ++i) { - RcVecUtils.Copy(verts, i * 3, @in, i * 3); + RcVec.Copy(verts, i * 3, @in, i * 3); } + edges.Clear(); tris.Clear(); float cs = chf.cs; @@ -957,7 +844,7 @@ namespace DotRecast.Recast { for (int k = nidx - 2; k > 0; --k) { - RcVecUtils.Copy(verts, nverts * 3, edge, idx[k] * 3); + RcVec.Copy(verts, nverts * 3, edge, idx[k] * 3); hull[nhull++] = nverts; nverts++; } @@ -966,7 +853,7 @@ namespace DotRecast.Recast { for (int k = 1; k < nidx - 1; ++k) { - RcVecUtils.Copy(verts, nverts * 3, edge, idx[k] * 3); + RcVec.Copy(verts, nverts * 3, edge, idx[k] * 3); hull[nhull++] = nverts; nverts++; } @@ -997,12 +884,12 @@ namespace DotRecast.Recast if (sampleDist > 0) { // Create sample locations in a grid. - RcVec3f bmin = RcVecUtils.Create(@in); - RcVec3f bmax = RcVecUtils.Create(@in); + RcVec3f bmin = new RcVec3f(@in); + RcVec3f bmax = new RcVec3f(@in); for (int i = 1; i < nin; ++i) { - bmin = RcVecUtils.Min(bmin, @in, i * 3); - bmax = RcVecUtils.Max(bmax, @in, i * 3); + bmin = RcVec3f.Min(bmin, RcVec.Create(@in, i * 3)); + bmax = RcVec3f.Max(bmax, RcVec.Create(@in, i * 3)); } int x0 = (int)MathF.Floor(bmin.X / sampleDist); @@ -1105,7 +992,7 @@ namespace DotRecast.Recast return nverts; } - static bool OnHull(int a, int b, int nhull, int[] hull) + public static bool OnHull(int a, int b, int nhull, int[] hull) { // All internal sampled points come after the hull so we can early out for those. if (a >= nhull || b >= nhull) @@ -1121,7 +1008,7 @@ namespace DotRecast.Recast } // Find edges that lie on hull and mark them as such. - static void SetTriFlags(List tris, int nhull, int[] hull) + public static void SetTriFlags(List tris, int nhull, int[] hull) { // Matches DT_DETAIL_EDGE_BOUNDARY const int DETAIL_EDGE_BOUNDARY = 0x1; @@ -1140,7 +1027,7 @@ namespace DotRecast.Recast } - static void SeedArrayWithPolyCenter(RcContext ctx, RcCompactHeightfield chf, int[] meshpoly, int poly, int npoly, + public static void SeedArrayWithPolyCenter(RcContext ctx, RcCompactHeightfield chf, int[] meshpoly, int poly, int npoly, int[] verts, int bs, RcHeightPatch hp, List array) { // Note: Reads to the compact heightfield are offset by border size (bs) @@ -1289,22 +1176,25 @@ namespace DotRecast.Recast hp.data[cx - hp.xmin + (cy - hp.ymin) * hp.width] = cs2.y; } - const int RETRACT_SIZE = 256; - static void Push3(List queue, int v1, int v2, int v3) + public static void Push3(List queue, int v1, int v2, int v3) { queue.Add(v1); queue.Add(v2); queue.Add(v3); } - static void GetHeightData(RcContext ctx, RcCompactHeightfield chf, int[] meshpolys, int poly, int npoly, int[] verts, - int bs, RcHeightPatch hp, int region) + public static void GetHeightData(RcContext ctx, RcCompactHeightfield chf, + int[] meshpolys, int poly, int npoly, + int[] verts, int bs, + ref RcHeightPatch hp, ref List queue, + int region) { // Note: Reads to the compact heightfield are offset by border size (bs) // since border size offset is already removed from the polymesh vertices. - List queue = new List(512); + queue.Clear(); + // Set all heights to RC_UNSET_HEIGHT. Array.Fill(hp.data, RC_UNSET_HEIGHT, 0, (hp.width * hp.height) - (0)); bool empty = true; @@ -1370,6 +1260,7 @@ namespace DotRecast.Recast SeedArrayWithPolyCenter(ctx, chf, meshpolys, poly, npoly, verts, bs, hp, queue); } + const int RETRACT_SIZE = 256; int head = 0; // We assume the seed is centered in the polygon, so a BFS to collect @@ -1441,7 +1332,10 @@ namespace DotRecast.Recast int borderSize = mesh.borderSize; int heightSearchRadius = (int)Math.Max(1, MathF.Ceiling(mesh.maxEdgeError)); + List edges = new List(64); List tris = new List(512); + List arr = new List(512); + List samples = new List(512); float[] verts = new float[256 * 3]; RcHeightPatch hp = new RcHeightPatch(); int nPolyVerts = 0; @@ -1526,18 +1420,20 @@ namespace DotRecast.Recast hp.ymin = bounds[i * 4 + 2]; hp.width = bounds[i * 4 + 1] - bounds[i * 4 + 0]; hp.height = bounds[i * 4 + 3] - bounds[i * 4 + 2]; - GetHeightData(ctx, chf, mesh.polys, p, npoly, mesh.verts, borderSize, hp, mesh.regs[i]); + GetHeightData(ctx, chf, mesh.polys, p, npoly, mesh.verts, borderSize, ref hp, ref arr, mesh.regs[i]); // Build detail mesh. - int nverts = BuildPolyDetail(ctx, poly, npoly, sampleDist, sampleMaxError, heightSearchRadius, chf, hp, - verts, tris); + int nverts = BuildPolyDetail(ctx, poly, npoly, + sampleDist, sampleMaxError, + heightSearchRadius, chf, hp, + verts, ref tris, + ref edges, ref samples); // Move detail verts to world space. for (int j = 0; j < nverts; ++j) { verts[j * 3 + 0] += orig.X; verts[j * 3 + 1] += orig.Y + chf.ch; // Is this offset necessary? See - // https://groups.google.com/d/msg/recastnavigation/UQFN6BGCcV0/-1Ny4koOBpkJ verts[j * 3 + 2] += orig.Z; } @@ -1614,7 +1510,7 @@ namespace DotRecast.Recast } /// @see rcAllocPolyMeshDetail, rcPolyMeshDetail - private static RcPolyMeshDetail MergePolyMeshDetails(RcContext ctx, RcPolyMeshDetail[] meshes, int nmeshes) + public static RcPolyMeshDetail MergePolyMeshDetails(RcContext ctx, RcPolyMeshDetail[] meshes, int nmeshes) { using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL); @@ -1665,7 +1561,7 @@ namespace DotRecast.Recast for (int k = 0; k < dm.nverts; ++k) { - RcVecUtils.Copy(mesh.verts, mesh.nverts * 3, dm.verts, k * 3); + RcVec.Copy(mesh.verts, mesh.nverts * 3, dm.verts, k * 3); mesh.nverts++; } diff --git a/src/DotRecast.Recast/RcMeshs.cs b/src/DotRecast.Recast/RcMeshs.cs index 269be05..5a2ddbd 100644 --- a/src/DotRecast.Recast/RcMeshs.cs +++ b/src/DotRecast.Recast/RcMeshs.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,7 +24,7 @@ using DotRecast.Core.Numerics; namespace DotRecast.Recast { - using static RcConstants; + using static RcRecast; public static class RcMeshs { @@ -715,8 +715,13 @@ namespace DotRecast.Recast int nv = CountPolyVerts(mesh.polys, p, nvp); bool hasRem = false; for (int j = 0; j < nv; ++j) + { if (mesh.polys[p + j] == rem) + { hasRem = true; + } + } + if (hasRem) { // Collect edges which does not touch the removed vertex. @@ -956,7 +961,7 @@ namespace DotRecast.Recast mesh.npolys++; if (mesh.npolys > maxTris) { - throw new Exception("removeVertex: Too many polygons " + mesh.npolys + " (max:" + maxTris + "."); + throw new Exception($"removeVertex: Too many polygons {mesh.npolys} max:({maxTris})."); } } } @@ -1033,11 +1038,23 @@ namespace DotRecast.Recast // Triangulate contour for (int j = 0; j < cont.nverts; ++j) indices[j] = j; + int ntris = Triangulate(cont.nverts, cont.verts, indices, tris); if (ntris <= 0) { // Bad triangulation, should not happen. - ctx.Warn("buildPolyMesh: Bad triangulation Contour " + i + "."); + /* + printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]); + printf("\tconst float cs = %ff;\n", cset.cs); + printf("\tconst float ch = %ff;\n", cset.ch); + printf("\tconst int verts[] = {\n"); + for (int k = 0; k < cont.nverts; ++k) + { + const int* v = &cont.verts[k*4]; + printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); + } + printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/ + ctx.Warn($"BuildPolyMesh: Bad triangulation Contour {i}."); ntris = -ntris; } @@ -1198,14 +1215,12 @@ namespace DotRecast.Recast if (mesh.nverts > MAX_MESH_VERTS_POLY) { - throw new Exception("rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts - + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted."); + throw new Exception($"BuildPolyMesh: The resulting mesh has too many vertices {mesh.nverts} (max {MAX_MESH_VERTS_POLY}). Data can be corrupted."); } if (mesh.npolys > MAX_MESH_VERTS_POLY) { - throw new Exception("rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys - + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted."); + throw new Exception($"BuildPolyMesh: The resulting mesh has too many polygons {mesh.npolys} (max {MAX_MESH_VERTS_POLY}). Data can be corrupted."); } return mesh; diff --git a/src/DotRecast.Recast/RcPartition.cs b/src/DotRecast.Recast/RcPartition.cs index 266bfd8..322d2f1 100644 --- a/src/DotRecast.Recast/RcPartition.cs +++ b/src/DotRecast.Recast/RcPartition.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public enum RcPartition { diff --git a/src/DotRecast.Recast/RcPartitionType.cs b/src/DotRecast.Recast/RcPartitionType.cs index cb85ac7..ef97c9b 100644 --- a/src/DotRecast.Recast/RcPartitionType.cs +++ b/src/DotRecast.Recast/RcPartitionType.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcPolyMesh.cs b/src/DotRecast.Recast/RcPolyMesh.cs index 48a547f..5014849 100644 --- a/src/DotRecast.Recast/RcPolyMesh.cs +++ b/src/DotRecast.Recast/RcPolyMesh.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Recast4J Copyright (c) 2015 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcPolyMeshDetail.cs b/src/DotRecast.Recast/RcPolyMeshDetail.cs index e9e1b21..78a90d2 100644 --- a/src/DotRecast.Recast/RcPolyMeshDetail.cs +++ b/src/DotRecast.Recast/RcPolyMeshDetail.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/src/DotRecast.Recast/RcPolyMeshRaycast.cs b/src/DotRecast.Recast/RcPolyMeshRaycast.cs index 845b6ec..f872a92 100644 --- a/src/DotRecast.Recast/RcPolyMeshRaycast.cs +++ b/src/DotRecast.Recast/RcPolyMeshRaycast.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -17,6 +17,7 @@ freely, subject to the following restrictions: 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.Numerics; @@ -45,6 +46,7 @@ namespace DotRecast.Recast private static bool Raycast(RcPolyMesh poly, RcPolyMeshDetail meshDetail, RcVec3f sp, RcVec3f sq, out float hitTime) { hitTime = 0; + Span tempVs = stackalloc RcVec3f[3]; if (meshDetail != null) { for (int i = 0; i < meshDetail.nmeshes; ++i) @@ -57,7 +59,7 @@ namespace DotRecast.Recast int tris = btris * 4; for (int j = 0; j < ntris; ++j) { - RcVec3f[] vs = new RcVec3f[3]; + Span vs = tempVs; for (int k = 0; k < 3; ++k) { vs[k].X = meshDetail.verts[verts + meshDetail.tris[tris + j * 4 + k] * 3]; diff --git a/src/DotRecast.Recast/RcPotentialDiagonalComparer.cs b/src/DotRecast.Recast/RcPotentialDiagonalComparer.cs index 66dd128..257dd16 100644 --- a/src/DotRecast.Recast/RcPotentialDiagonalComparer.cs +++ b/src/DotRecast.Recast/RcPotentialDiagonalComparer.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcRasterizations.cs b/src/DotRecast.Recast/RcRasterizations.cs index d1cf465..74ed12a 100644 --- a/src/DotRecast.Recast/RcRasterizations.cs +++ b/src/DotRecast.Recast/RcRasterizations.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -21,10 +21,12 @@ freely, subject to the following restrictions: using System; using DotRecast.Core; using DotRecast.Core.Numerics; -using static DotRecast.Recast.RcConstants; + namespace DotRecast.Recast { + using static RcRecast; + public static class RcRasterizations { /// Check whether two bounding boxes overlap @@ -41,7 +43,7 @@ namespace DotRecast.Recast aMin.Y <= bMax.Y && aMax.Y >= bMin.Y && aMin.Z <= bMax.Z && aMax.Z >= bMin.Z; } - + /// Allocates a new span in the heightfield. /// Use a memory pool and free list to minimize actual allocations. /// @@ -207,7 +209,7 @@ namespace DotRecast.Recast /// @param[out] outVerts2Count The number of resulting polygon 2 vertices /// @param[in] axisOffset THe offset along the specified axis /// @param[in] axis The separating axis - private static void DividePoly(float[] inVerts, int inVertsOffset, int inVertsCount, + private static void DividePoly(Span inVerts, int inVertsOffset, int inVertsCount, int outVerts1, out int outVerts1Count, int outVerts2, out int outVerts2Count, float axisOffset, int axis) @@ -231,7 +233,7 @@ namespace DotRecast.Recast inVerts[outVerts1 + poly1Vert * 3 + 0] = inVerts[inVertsOffset + inVertB * 3 + 0] + (inVerts[inVertsOffset + inVertA * 3 + 0] - inVerts[inVertsOffset + inVertB * 3 + 0]) * s; inVerts[outVerts1 + poly1Vert * 3 + 1] = inVerts[inVertsOffset + inVertB * 3 + 1] + (inVerts[inVertsOffset + inVertA * 3 + 1] - inVerts[inVertsOffset + inVertB * 3 + 1]) * s; inVerts[outVerts1 + poly1Vert * 3 + 2] = inVerts[inVertsOffset + inVertB * 3 + 2] + (inVerts[inVertsOffset + inVertA * 3 + 2] - inVerts[inVertsOffset + inVertB * 3 + 2]) * s; - RcVecUtils.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, outVerts1 + poly1Vert * 3); + RcVec.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, outVerts1 + poly1Vert * 3); poly1Vert++; poly2Vert++; @@ -239,12 +241,12 @@ namespace DotRecast.Recast // since these were already added above if (inVertAxisDelta[inVertA] > 0) { - RcVecUtils.Copy(inVerts, outVerts1 + poly1Vert * 3, inVerts, inVertsOffset + inVertA * 3); + RcVec.Copy(inVerts, outVerts1 + poly1Vert * 3, inVerts, inVertsOffset + inVertA * 3); poly1Vert++; } else if (inVertAxisDelta[inVertA] < 0) { - RcVecUtils.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, inVertsOffset + inVertA * 3); + RcVec.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, inVertsOffset + inVertA * 3); poly2Vert++; } } @@ -253,7 +255,7 @@ namespace DotRecast.Recast // add the i'th point to the right polygon. Addition is done even for points on the dividing line if (inVertAxisDelta[inVertA] >= 0) { - RcVecUtils.Copy(inVerts, outVerts1 + poly1Vert * 3, inVerts, inVertsOffset + inVertA * 3); + RcVec.Copy(inVerts, outVerts1 + poly1Vert * 3, inVerts, inVertsOffset + inVertA * 3); poly1Vert++; if (inVertAxisDelta[inVertA] != 0) { @@ -261,7 +263,7 @@ namespace DotRecast.Recast } } - RcVecUtils.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, inVertsOffset + inVertA * 3); + RcVec.Copy(inVerts, outVerts2 + poly2Vert * 3, inVerts, inVertsOffset + inVertA * 3); poly2Vert++; } } @@ -293,13 +295,13 @@ namespace DotRecast.Recast int flagMergeThreshold) { // Calculate the bounding box of the triangle. - RcVec3f triBBMin = RcVecUtils.Create(verts, v0 * 3); - triBBMin = RcVecUtils.Min(triBBMin, verts, v1 * 3); - triBBMin = RcVecUtils.Min(triBBMin, verts, v2 * 3); + RcVec3f triBBMin = RcVec.Create(verts, v0 * 3); + triBBMin = RcVec3f.Min(triBBMin, RcVec.Create(verts, v1 * 3)); + triBBMin = RcVec3f.Min(triBBMin, RcVec.Create(verts, v2 * 3)); - RcVec3f triBBMax = RcVecUtils.Create(verts, v0 * 3); - triBBMax = RcVecUtils.Max(triBBMax, verts, v1 * 3); - triBBMax = RcVecUtils.Max(triBBMax, verts, v2 * 3); + RcVec3f triBBMax = RcVec.Create(verts, v0 * 3); + triBBMax = RcVec3f.Max(triBBMax, RcVec.Create(verts, v1 * 3)); + triBBMax = RcVec3f.Max(triBBMax, RcVec.Create(verts, v2 * 3)); // If the triangle does not touch the bounding box of the heightfield, skip the triangle. if (!OverlapBounds(triBBMin, triBBMax, heightfieldBBMin, heightfieldBBMax)) @@ -320,15 +322,15 @@ namespace DotRecast.Recast z1 = Math.Clamp(z1, 0, h - 1); // Clip the triangle into all grid cells it touches. - float[] buf = new float[7 * 3 * 4]; + Span buf = stackalloc float[7 * 3 * 4]; int @in = 0; int inRow = 7 * 3; int p1 = inRow + 7 * 3; int p2 = p1 + 7 * 3; - RcVecUtils.Copy(buf, 0, verts, v0 * 3); - RcVecUtils.Copy(buf, 3, verts, v1 * 3); - RcVecUtils.Copy(buf, 6, verts, v2 * 3); + RcVec.Copy(buf, 0, verts, v0 * 3); + RcVec.Copy(buf, 3, verts, v1 * 3); + RcVec.Copy(buf, 6, verts, v2 * 3); int nvRow; int nvIn = 3; diff --git a/src/DotRecast.Recast/RcCommons.cs b/src/DotRecast.Recast/RcRecast.cs similarity index 60% rename from src/DotRecast.Recast/RcCommons.cs rename to src/DotRecast.Recast/RcRecast.cs index cbebebf..146d91f 100644 --- a/src/DotRecast.Recast/RcCommons.cs +++ b/src/DotRecast.Recast/RcRecast.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -24,10 +24,81 @@ using DotRecast.Core.Numerics; namespace DotRecast.Recast { - using static RcConstants; - - public static class RcCommons + public static class RcRecast { + /// Represents the null area. + /// When a data element is given this value it is considered to no longer be + /// assigned to a usable area. (E.g. It is un-walkable.) + public const int RC_NULL_AREA = 0; + + /// The default area id used to indicate a walkable polygon. + /// This is also the maximum allowed area id, and the only non-null area id + /// recognized by some steps in the build process. + public const int RC_WALKABLE_AREA = 63; + + /// The value returned by #rcGetCon if the specified direction is not connected + /// to another span. (Has no neighbor.) + public const int RC_NOT_CONNECTED = 0x3f; + + /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. + public const int RC_SPAN_HEIGHT_BITS = 20; + + /// Defines the maximum value for rcSpan::smin and rcSpan::smax. + public const int RC_SPAN_MAX_HEIGHT = (1 << RC_SPAN_HEIGHT_BITS) - 1; + + /// The number of spans allocated per span spool. + /// @see rcSpanPool + public const int RC_SPANS_PER_POOL = 2048; + + // Must be 255 or smaller (not 256) because layer IDs are stored as + // a byte where 255 is a special value. + public const int RC_MAX_LAYERS = RC_NOT_CONNECTED; + public const int RC_MAX_NEIS = 16; + + /// Heighfield border flag. + /// If a heightfield region ID has this bit set, then the region is a border + /// region and its spans are considered unwalkable. + /// (Used during the region and contour build process.) + /// @see rcCompactSpan::reg + public const int RC_BORDER_REG = 0x8000; + + /// Polygon touches multiple regions. + /// If a polygon has this region ID it was merged with or created + /// from polygons of different regions during the polymesh + /// build step that removes redundant border vertices. + /// (Used during the polymesh and detail polymesh build processes) + /// @see rcPolyMesh::regs + public const int RC_MULTIPLE_REGS = 0; + + // Border vertex flag. + /// If a region ID has this bit set, then the associated element lies on + /// a tile border. If a contour vertex's region ID has this bit set, the + /// vertex will later be removed in order to match the segments and vertices + /// at tile boundaries. + /// (Used during the build process.) + /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts + public const int RC_BORDER_VERTEX = 0x10000; + + /// Area border flag. + /// If a region ID has this bit set, then the associated element lies on + /// the border of an area. + /// (Used during the region and contour build process.) + /// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts + public const int RC_AREA_BORDER = 0x20000; + + /// Applied to the region id field of contour vertices in order to extract the region id. + /// The region id field of a vertex may have several flags applied to it. So the + /// fields value can't be used directly. + /// @see rcContour::verts, rcContour::rverts + public const int RC_CONTOUR_REG_MASK = 0xffff; + + /// A value which indicates an invalid index within a mesh. + /// @note This does not necessarily indicate an error. + /// @see rcPolyMesh::polys + public const int RC_MESH_NULL_IDX = 0xffff; + + public const int RC_LOG_WARNING = 1; + private static readonly int[] DirOffsetX = { -1, 0, 1, 0, }; private static readonly int[] DirOffsetY = { 0, 1, 0, -1 }; private static readonly int[] DirForOffset = { 3, 0, -1, 2, 1 }; @@ -127,7 +198,10 @@ namespace DotRecast.Recast for (int i = 0; i < nt; ++i) { int tri = i * 3; - CalcTriNormal(verts, tris[tri], tris[tri + 1], tris[tri + 2], ref norm); + RcVec3f v0 = RcVec.Create(verts, tris[tri + 0] * 3); + RcVec3f v1 = RcVec.Create(verts, tris[tri + 1] * 3); + RcVec3f v2 = RcVec.Create(verts, tris[tri + 2] * 3); + CalcTriNormal(v0, v1, v2, ref norm); // Check if the face is walkable. if (norm.Y > walkableThr) areas[i] = areaMod.Apply(areas[i]); @@ -136,10 +210,10 @@ namespace DotRecast.Recast return areas; } - public static void CalcTriNormal(float[] verts, int v0, int v1, int v2, ref RcVec3f norm) + public static void CalcTriNormal(RcVec3f v0, RcVec3f v1, RcVec3f v2, ref RcVec3f norm) { - var e0 = RcVecUtils.Subtract(verts, v1 * 3, v0 * 3); - var e1 = RcVecUtils.Subtract(verts, v2 * 3, v0 * 3); + var e0 = v1 - v0; + var e1 = v2 - v0; norm = RcVec3f.Cross(e0, e1); norm = RcVec3f.Normalize(norm); } @@ -162,7 +236,10 @@ namespace DotRecast.Recast for (int i = 0; i < nt; ++i) { int tri = i * 3; - CalcTriNormal(verts, tris[tri], tris[tri + 1], tris[tri + 2], ref norm); + RcVec3f v0 = RcVec.Create(verts, tris[tri + 0] * 3); + RcVec3f v1 = RcVec.Create(verts, tris[tri + 1] * 3); + RcVec3f v2 = RcVec.Create(verts, tris[tri + 2] * 3); + CalcTriNormal(v0, v1, v2, ref norm); // Check if the face is walkable. if (norm.Y <= walkableThr) areas[i] = RC_NULL_AREA; diff --git a/src/DotRecast.Recast/RcRegion.cs b/src/DotRecast.Recast/RcRegion.cs index 9be4c60..4dcb170 100644 --- a/src/DotRecast.Recast/RcRegion.cs +++ b/src/DotRecast.Recast/RcRegion.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace DotRecast.Recast { diff --git a/src/DotRecast.Recast/RcRegions.cs b/src/DotRecast.Recast/RcRegions.cs index bcde43c..607259e 100644 --- a/src/DotRecast.Recast/RcRegions.cs +++ b/src/DotRecast.Recast/RcRegions.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,8 +25,8 @@ using DotRecast.Core; namespace DotRecast.Recast { - using static RcConstants; - using static RcCommons; + + using static RcRecast; public static class RcRegions { @@ -1665,8 +1665,8 @@ namespace DotRecast.Recast ctx.StartTimer(RcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED); - int LOG_NB_STACKS = 3; - int NB_STACKS = 1 << LOG_NB_STACKS; + const int LOG_NB_STACKS = 3; + const int NB_STACKS = 1 << LOG_NB_STACKS; List> lvlStacks = new List>(); for (int i = 0; i < NB_STACKS; ++i) { @@ -1756,7 +1756,7 @@ namespace DotRecast.Recast ExpandRegions(expandIters * 8, 0, chf, srcReg, srcDist, stack, true); ctx.StopTimer(RcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED); - + ctx.StartTimer(RcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER); // Merge regions and filter out small regions. diff --git a/src/DotRecast.Recast/RcSpan.cs b/src/DotRecast.Recast/RcSpan.cs index 068c073..9748cd4 100644 --- a/src/DotRecast.Recast/RcSpan.cs +++ b/src/DotRecast.Recast/RcSpan.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -20,19 +20,13 @@ freely, subject to the following restrictions: namespace DotRecast.Recast { - /** Represents a span in a heightfield. */ + /// Represents a span in a heightfield. + /// @see rcHeightfield public class RcSpan { - /** The lower limit of the span. [Limit: < smax] */ - public int smin; - - /** The upper limit of the span. [Limit: <= SPAN_MAX_HEIGHT] */ - public int smax; - - /** The area id assigned to the span. */ - public int area; - - /** The next span higher up in column. */ - public RcSpan next; + public int smin; //< The lower limit of the span. [Limit: < #smax] + public int smax; //< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] + public int area; //< The area id assigned to the span. + public RcSpan next; //< The next span higher up in column. } } \ No newline at end of file diff --git a/src/DotRecast.Recast/RcSpanPool.cs b/src/DotRecast.Recast/RcSpanPool.cs index 393ceea..b31181d 100644 --- a/src/DotRecast.Recast/RcSpanPool.cs +++ b/src/DotRecast.Recast/RcSpanPool.cs @@ -1,6 +1,6 @@ -/* +/* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -28,7 +28,7 @@ namespace DotRecast.Recast public RcSpanPool() { - items = new RcSpan[RcConstants.RC_SPANS_PER_POOL]; + items = new RcSpan[RcRecast.RC_SPANS_PER_POOL]; for (int i = 0; i < items.Length; ++i) { items[i] = new RcSpan(); diff --git a/src/DotRecast.Recast/RcSweepSpan.cs b/src/DotRecast.Recast/RcSweepSpan.cs index 4dcb80b..f07375f 100644 --- a/src/DotRecast.Recast/RcSweepSpan.cs +++ b/src/DotRecast.Recast/RcSweepSpan.cs @@ -1,4 +1,4 @@ -namespace DotRecast.Recast +namespace DotRecast.Recast { public class RcSweepSpan { diff --git a/src/DotRecast.Recast/RcVoxelizations.cs b/src/DotRecast.Recast/RcVoxelizations.cs index 3cc9b90..0687824 100644 --- a/src/DotRecast.Recast/RcVoxelizations.cs +++ b/src/DotRecast.Recast/RcVoxelizations.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -19,6 +19,7 @@ freely, subject to the following restrictions: using System.Collections.Generic; using DotRecast.Core; +using DotRecast.Core.Numerics; using DotRecast.Recast.Geom; namespace DotRecast.Recast @@ -44,18 +45,18 @@ namespace DotRecast.Recast float[] verts = geom.GetVerts(); if (cfg.UseTiles) { - float[] tbmin = new float[2]; - float[] tbmax = new float[2]; - tbmin[0] = builderCfg.bmin.X; - tbmin[1] = builderCfg.bmin.Z; - tbmax[0] = builderCfg.bmax.X; - tbmax[1] = builderCfg.bmax.Z; + RcVec2f tbmin; + RcVec2f tbmax; + tbmin.X = builderCfg.bmin.X; + tbmin.Y = builderCfg.bmin.Z; + tbmax.X = builderCfg.bmax.X; + tbmax.Y = builderCfg.bmax.Z; List nodes = geom.GetChunksOverlappingRect(tbmin, tbmax); foreach (RcChunkyTriMeshNode node in nodes) { int[] tris = node.tris; int ntris = tris.Length / 3; - int[] m_triareas = RcCommons.MarkWalkableTriangles(ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); + int[] m_triareas = RcRecast.MarkWalkableTriangles(ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); RcRasterizations.RasterizeTriangles(ctx, verts, tris, m_triareas, ntris, solid, cfg.WalkableClimb); } } @@ -63,7 +64,7 @@ namespace DotRecast.Recast { int[] tris = geom.GetTris(); int ntris = tris.Length / 3; - int[] m_triareas = RcCommons.MarkWalkableTriangles(ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); + int[] m_triareas = RcRecast.MarkWalkableTriangles(ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); RcRasterizations.RasterizeTriangles(ctx, verts, tris, m_triareas, ntris, solid, cfg.WalkableClimb); } } diff --git a/test/DotRecast.Benchmark/BenchmarkProgram.cs b/test/DotRecast.Benchmark/BenchmarkProgram.cs new file mode 100644 index 0000000..8f4c057 --- /dev/null +++ b/test/DotRecast.Benchmark/BenchmarkProgram.cs @@ -0,0 +1,22 @@ +using System.Collections.Immutable; +using System.Linq; +using BenchmarkDotNet.Running; +using DotRecast.Benchmark.Benchmarks; + +namespace DotRecast.Benchmark; + +public static class BenchmarkProgram +{ + public static int Main(string[] args) + { + var runs = ImmutableArray.Create( + BenchmarkConverter.TypeToBenchmarks(typeof(VectorBenchmarks)), + BenchmarkConverter.TypeToBenchmarks(typeof(PriorityQueueBenchmarks)), + BenchmarkConverter.TypeToBenchmarks(typeof(StackallocBenchmarks)) + ); + + var summary = BenchmarkRunner.Run(runs.ToArray()); + + return 0; + } +} \ No newline at end of file diff --git a/test/DotRecast.Benchmark/Benchmarks/PriorityQueueBenchmarks.cs b/test/DotRecast.Benchmark/Benchmarks/PriorityQueueBenchmarks.cs new file mode 100644 index 0000000..b929454 --- /dev/null +++ b/test/DotRecast.Benchmark/Benchmarks/PriorityQueueBenchmarks.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using DotRecast.Core.Collections; + +namespace DotRecast.Benchmark.Benchmarks; + +/* + +// * Summary * +BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3958/23H2/2023Update/SunValley3) +AMD Ryzen 5 3600, 1 CPU, 12 logical and 6 physical cores +.NET SDK 8.0.101 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + +| Method | Count | Mean | Error | StdDev | +|------------------------------- |------ |--------------------:|-----------------:|-----------------:| +| Enqueue_RcSortedQueue | 10 | 87.49 ns | 0.774 ns | 0.724 ns | +| Enqueue_RcBinaryMinHeap | 10 | 185.23 ns | 1.730 ns | 1.533 ns | +| Enqueue_PriorityQueue | 10 | 202.95 ns | 1.611 ns | 1.428 ns | +| DequeueAll_RcSortedQueue | 10 | 460.97 ns | 2.169 ns | 2.029 ns | +| DequeueAll_RcBinaryMinHeap | 10 | 573.17 ns | 2.542 ns | 2.378 ns | +| DequeueAll_PriorityQueue | 10 | 500.68 ns | 2.364 ns | 2.212 ns | +| EnqueueDequeue_RcSortedQueue | 10 | 525.43 ns | 1.842 ns | 1.632 ns | +| EnqueueDequeue_RcBinaryMinHeap | 10 | 455.65 ns | 2.410 ns | 2.254 ns | +| EnqueueDequeue_PriorityQueue | 10 | 381.82 ns | 6.036 ns | 5.646 ns | +| Enqueue_RcSortedQueue | 100 | 730.57 ns | 5.229 ns | 4.635 ns | +| Enqueue_RcBinaryMinHeap | 100 | 3,012.15 ns | 10.875 ns | 9.640 ns | +| Enqueue_PriorityQueue | 100 | 2,306.80 ns | 26.694 ns | 23.663 ns | +| DequeueAll_RcSortedQueue | 100 | 6,241.67 ns | 31.856 ns | 29.798 ns | +| DequeueAll_RcBinaryMinHeap | 100 | 13,692.29 ns | 38.829 ns | 34.421 ns | +| DequeueAll_PriorityQueue | 100 | 12,482.93 ns | 93.955 ns | 87.886 ns | +| EnqueueDequeue_RcSortedQueue | 100 | 64,002.79 ns | 316.081 ns | 280.197 ns | +| EnqueueDequeue_RcBinaryMinHeap | 100 | 8,655.79 ns | 23.703 ns | 22.172 ns | +| EnqueueDequeue_PriorityQueue | 100 | 7,806.20 ns | 105.801 ns | 98.967 ns | +| Enqueue_RcSortedQueue | 1000 | 7,566.23 ns | 149.010 ns | 218.418 ns | +| Enqueue_RcBinaryMinHeap | 1000 | 36,277.43 ns | 96.710 ns | 90.462 ns | +| Enqueue_PriorityQueue | 1000 | 28,564.19 ns | 186.866 ns | 174.795 ns | +| DequeueAll_RcSortedQueue | 1000 | 108,574.26 ns | 745.459 ns | 697.303 ns | +| DequeueAll_RcBinaryMinHeap | 1000 | 210,346.25 ns | 332.478 ns | 311.000 ns | +| DequeueAll_PriorityQueue | 1000 | 189,536.32 ns | 1,180.045 ns | 1,046.079 ns | +| EnqueueDequeue_RcSortedQueue | 1000 | 8,957,965.42 ns | 45,715.567 ns | 42,762.370 ns | +| EnqueueDequeue_RcBinaryMinHeap | 1000 | 131,615.02 ns | 394.216 ns | 368.750 ns | +| EnqueueDequeue_PriorityQueue | 1000 | 114,799.89 ns | 1,269.621 ns | 1,060.191 ns | +| Enqueue_RcSortedQueue | 10000 | 77,143.76 ns | 996.372 ns | 932.007 ns | +| Enqueue_RcBinaryMinHeap | 10000 | 417,620.57 ns | 853.343 ns | 756.466 ns | +| Enqueue_PriorityQueue | 10000 | 278,791.68 ns | 1,566.093 ns | 1,464.924 ns | +| DequeueAll_RcSortedQueue | 10000 | 1,435,539.99 ns | 9,329.910 ns | 8,727.204 ns | +| DequeueAll_RcBinaryMinHeap | 10000 | 2,956,366.90 ns | 6,344.030 ns | 5,934.210 ns | +| DequeueAll_PriorityQueue | 10000 | 2,642,186.54 ns | 9,482.374 ns | 8,869.819 ns | +| EnqueueDequeue_RcSortedQueue | 10000 | 1,318,277,320.00 ns | 6,725,701.525 ns | 6,291,225.379 ns | +| EnqueueDequeue_RcBinaryMinHeap | 10000 | 1,712,170.68 ns | 5,674.513 ns | 5,307.943 ns | +| EnqueueDequeue_PriorityQueue | 10000 | 1,466,910.77 ns | 4,394.686 ns | 4,110.792 ns | + +*/ + +public class PriorityQueueBenchmarks +{ + [Params(10, 100, 1000, 10000)] public int Count; + + private RcSortedQueue _sq; + private RcBinaryMinHeap _bmHeap; + private PriorityQueue _pq; + + private float[] _priority; + + class Node + { + public int id; + public float total; + } + + [GlobalSetup] + public void Setup() + { + static int Comp(Node x, Node y) + { + var v = x.total.CompareTo(y.total); + if (v != 0) + return v; + return x.id.CompareTo(y.id); + } + + _sq = new(Comp); + _bmHeap = new(Count, Comp); + _pq = new(Count, Comparer.Create(Comp)); + + _priority = new float[Count]; + for (int i = 0; i < Count; i++) + { + _priority[i] = (float)Random.Shared.NextDouble() * 100f; + } + } + + [Benchmark] + public void Enqueue_RcSortedQueue() + { + _sq.Clear(); + for (int i = 0; i < Count; i++) + { + _sq.Enqueue(new Node + { + id = i, + total = _priority[i], + }); + } + } + + [Benchmark] + public void Enqueue_RcBinaryMinHeap() + { + _bmHeap.Clear(); + for (int i = 0; i < Count; i++) + { + _bmHeap.Push(new Node + { + id = i, + total = _priority[i], + }); + } + } + + [Benchmark] + public void Enqueue_PriorityQueue() + { + _pq.Clear(); + for (int i = 0; i < Count; i++) + { + var node = new Node + { + id = i, + total = _priority[i], + }; + _pq.Enqueue(node, node); + } + } + + [Benchmark] + public void DequeueAll_RcSortedQueue() + { + _sq.Clear(); + for (int i = 0; i < Count; i++) + { + _sq.Enqueue(new Node + { + id = i, + total = _priority[i], + }); + } + + while (_sq.Count() > 0) + { + _sq.Dequeue(); + } + } + + [Benchmark] + public void DequeueAll_RcBinaryMinHeap() + { + _bmHeap.Clear(); + for (int i = 0; i < Count; i++) + { + _bmHeap.Push(new Node + { + id = i, + total = _priority[i], + }); + } + + while (_bmHeap.Count > 0) + { + _bmHeap.Pop(); + } + } + + [Benchmark] + public void DequeueAll_PriorityQueue() + { + _pq.Clear(); + for (int i = 0; i < Count; i++) + { + var node = new Node + { + id = i, + total = _priority[i], + }; + _pq.Enqueue(node, node); + } + + while (_pq.Count > 0) + { + _pq.Dequeue(); + } + } + + + [Benchmark] + public void EnqueueDequeue_RcSortedQueue() + { + _sq.Clear(); + int half = Count / 2; + for (int i = 0; i < half; i++) + { + _sq.Enqueue(new Node + { + id = i, + total = _priority[i], + }); + } + + for (int i = half; i < Count; i++) + { + _sq.Enqueue(new Node + { + id = i, + total = _priority[i], + }); + + _sq.Dequeue(); + } + + } + + [Benchmark] + public void EnqueueDequeue_RcBinaryMinHeap() + { + _bmHeap.Clear(); + int half = Count / 2; + for (int i = 0; i < half; i++) + { + _bmHeap.Push(new Node + { + id = i, + total = _priority[i], + }); + } + + for (int i = half; i < Count; i++) + { + _bmHeap.Push(new Node + { + id = i, + total = _priority[i], + }); + + _bmHeap.Pop(); + } + + } + + [Benchmark] + public void EnqueueDequeue_PriorityQueue() + { + _pq.Clear(); + int half = Count / 2; + for (int i = 0; i < half; i++) + { + var node = new Node + { + id = i, + total = _priority[i], + }; + _pq.Enqueue(node, node); + } + + for (int i = half; i < Count; i++) + { + var node = new Node + { + id = i, + total = _priority[i], + }; + _pq.Enqueue(node, node); + _pq.Dequeue(); + } + } +} \ No newline at end of file diff --git a/test/DotRecast.Benchmark/Benchmarks/StackallocBenchmarks.cs b/test/DotRecast.Benchmark/Benchmarks/StackallocBenchmarks.cs new file mode 100644 index 0000000..bc542e4 --- /dev/null +++ b/test/DotRecast.Benchmark/Benchmarks/StackallocBenchmarks.cs @@ -0,0 +1,71 @@ +using System; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; + +namespace DotRecast.Benchmark.Benchmarks; + +/* + +| Method | TestArraySize | Mean | Error | StdDev | Median | +|------------------------------- |-------------- |-------------:|-----------:|------------:|-------------:| +| Stackalloc_Long | 16 | 3.016 ns | 0.0179 ns | 0.0149 ns | 3.017 ns | +| Stackalloc_Long_SkipLocalsInit | 16 | 2.265 ns | 0.0197 ns | 0.0184 ns | 2.258 ns | +| New_Long | 16 | 5.917 ns | 0.1964 ns | 0.5634 ns | 5.761 ns | +| New_Long_SkipLocalsInit | 16 | 5.703 ns | 0.1371 ns | 0.3935 ns | 5.661 ns | +| Stackalloc_Long | 256 | 39.418 ns | 0.2737 ns | 0.2285 ns | 39.410 ns | +| Stackalloc_Long_SkipLocalsInit | 256 | 2.274 ns | 0.0147 ns | 0.0131 ns | 2.274 ns | +| New_Long | 256 | 53.901 ns | 2.9999 ns | 8.4614 ns | 51.449 ns | +| New_Long_SkipLocalsInit | 256 | 53.480 ns | 1.8716 ns | 5.4298 ns | 51.858 ns | +| Stackalloc_Long | 1024 | 137.037 ns | 0.3652 ns | 0.3416 ns | 137.031 ns | +| Stackalloc_Long_SkipLocalsInit | 1024 | 3.669 ns | 0.0254 ns | 0.0226 ns | 3.668 ns | +| New_Long | 1024 | 197.324 ns | 9.2795 ns | 27.0687 ns | 186.588 ns | +| New_Long_SkipLocalsInit | 1024 | 210.996 ns | 10.0255 ns | 27.9471 ns | 206.110 ns | +| Stackalloc_Long | 8192 | 1,897.989 ns | 7.1814 ns | 5.9968 ns | 1,897.814 ns | +| Stackalloc_Long_SkipLocalsInit | 8192 | 20.598 ns | 0.2645 ns | 0.2344 ns | 20.572 ns | +| New_Long | 8192 | 1,324.061 ns | 39.8447 ns | 116.2288 ns | 1,298.794 ns | +| New_Long_SkipLocalsInit | 8192 | 1,305.211 ns | 35.1855 ns | 102.0796 ns | 1,295.539 ns | +*/ + +public class StackallocBenchmarks +{ + private readonly Consumer _consumer = new(); + + [Params(1 << 4, 1 << 8, 1 << 10, 1 << 13)] + public int HashTableSize; + + [Benchmark] + public void Stackalloc_Long() + { + Span hashTable = stackalloc long[HashTableSize]; + + _consumer.Consume(hashTable[0]); + } + + [Benchmark] + [SkipLocalsInit] + public void Stackalloc_Long_SkipLocalsInit() + { + Span hashTable = stackalloc long[HashTableSize]; + + _consumer.Consume(hashTable[0]); + } + + [Benchmark] + public void New_Long() + { + Span hashTable = new long[HashTableSize]; + + _consumer.Consume(hashTable[0]); + } + + + [Benchmark] + [SkipLocalsInit] + public void New_Long_SkipLocalsInit() + { + Span hashTable = new long[HashTableSize]; + + _consumer.Consume(hashTable[0]); + } +} \ No newline at end of file diff --git a/test/DotRecast.Benchmark/Benchmarks/VectorBenchmarks.cs b/test/DotRecast.Benchmark/Benchmarks/VectorBenchmarks.cs new file mode 100644 index 0000000..0cb3faa --- /dev/null +++ b/test/DotRecast.Benchmark/Benchmarks/VectorBenchmarks.cs @@ -0,0 +1,73 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Engines; +using DotRecast.Core.Numerics; + +namespace DotRecast.Benchmark.Benchmarks; + +/* +| Method | Mean | Error | StdDev | +|------------------ |----------:|----------:|----------:| +| Dot_Vector3 | 0.6395 ns | 0.0125 ns | 0.0104 ns | +| Dot_RcVec3f | 0.2275 ns | 0.0281 ns | 0.0375 ns | +| Cross_Vector3 | 1.1652 ns | 0.0102 ns | 0.0085 ns | +| Cross_RcVec3f | 1.1687 ns | 0.0140 ns | 0.0124 ns | +| Normalize_Vector3 | 1.7964 ns | 0.0173 ns | 0.0162 ns | +| Normalize_RcVec3f | 1.2806 ns | 0.0088 ns | 0.0078 ns | +*/ + +public class VectorBenchmarks +{ + private readonly Consumer _consumer = new(); + + [Benchmark] + public void Dot_Vector3() + { + var v1 = new System.Numerics.Vector3(1, 2, 3); + var v2 = new System.Numerics.Vector3(1, 2, 3); + var v = System.Numerics.Vector3.Dot(v1, v2); + _consumer.Consume(v); + } + + [Benchmark] + public void Dot_RcVec3f() + { + var v1 = new RcVec3f(1, 2, 3); + var v2 = new RcVec3f(1, 2, 3); + var v = RcVec3f.Dot(v1, v2); + _consumer.Consume(v); + } + + [Benchmark] + public void Cross_Vector3() + { + var v1 = new System.Numerics.Vector3(1, 2, 3); + var v2 = new System.Numerics.Vector3(1, 2, 3); + var v = System.Numerics.Vector3.Cross(v1, v2); + _consumer.Consume(v); + } + + [Benchmark] + public void Cross_RcVec3f() + { + var v1 = new RcVec3f(1, 2, 3); + var v2 = new RcVec3f(1, 2, 3); + var v = RcVec3f.Cross(v1, v2); + _consumer.Consume(v); + } + + [Benchmark] + public void Normalize_Vector3() + { + var v1 = new System.Numerics.Vector3(1, 2, 3); + var v = System.Numerics.Vector3.Normalize(v1); + _consumer.Consume(v); + } + + [Benchmark] + public void Normalize_RcVec3f() + { + var v1 = new RcVec3f(1, 2, 3); + var v = RcVec3f.Normalize(v1); + _consumer.Consume(v); + } +} \ No newline at end of file diff --git a/test/DotRecast.Benchmark/DotRecast.Benchmark.csproj b/test/DotRecast.Benchmark/DotRecast.Benchmark.csproj new file mode 100644 index 0000000..f5d699c --- /dev/null +++ b/test/DotRecast.Benchmark/DotRecast.Benchmark.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0;net7.0;net8.0 + true + false + + + + + + + + + + + diff --git a/test/DotRecast.Core.Test/DotRecast.Core.Test.csproj b/test/DotRecast.Core.Test/DotRecast.Core.Test.csproj index 343e1ce..bc18d06 100644 --- a/test/DotRecast.Core.Test/DotRecast.Core.Test.csproj +++ b/test/DotRecast.Core.Test/DotRecast.Core.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Core.Test/RcArrayBenchmarkTests.cs b/test/DotRecast.Core.Test/RcArrayBenchmarkTests.cs index 3117e7d..38badae 100644 --- a/test/DotRecast.Core.Test/RcArrayBenchmarkTests.cs +++ b/test/DotRecast.Core.Test/RcArrayBenchmarkTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.Collections.Generic; using System.Net.Sockets; diff --git a/test/DotRecast.Core.Test/RcBinaryMinHeapTest.cs b/test/DotRecast.Core.Test/RcBinaryMinHeapTest.cs new file mode 100644 index 0000000..cc788cd --- /dev/null +++ b/test/DotRecast.Core.Test/RcBinaryMinHeapTest.cs @@ -0,0 +1,147 @@ +using DotRecast.Core.Collections; +using NUnit.Framework; + +namespace DotRecast.Core.Test; + +public class RcBinaryMinHeapTest +{ + private static readonly RcAtomicLong Gen = new(); + + private class Node + { + public readonly long Id; + public long Value; + + public Node(int value) + { + Id = Gen.IncrementAndGet(); + Value = value; + } + } + + [Test] + public void TestPush() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + minHeap.Push(new Node(5)); + minHeap.Push(new Node(3)); + minHeap.Push(new Node(7)); + minHeap.Push(new Node(2)); + minHeap.Push(new Node(4)); + + // Push 후 힙의 속성을 검증 + AssertHeapProperty(minHeap.ToArray()); + } + + [Test] + public void TestPop() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + minHeap.Push(new Node(5)); + minHeap.Push(new Node(3)); + minHeap.Push(new Node(7)); + minHeap.Push(new Node(2)); + minHeap.Push(new Node(4)); + + // Pop을 통해 최소 값부터 순서대로 제거하면서 검증 + Assert.That(minHeap.Pop().Value, Is.EqualTo(2)); + Assert.That(minHeap.Pop().Value, Is.EqualTo(3)); + Assert.That(minHeap.Pop().Value, Is.EqualTo(4)); + Assert.That(minHeap.Pop().Value, Is.EqualTo(5)); + Assert.That(minHeap.Pop().Value, Is.EqualTo(7)); + + // 모든 요소를 Pop한 후에는 비어있어야 함 + Assert.That(minHeap.IsEmpty(), Is.True); + } + + + [Test] + public void TestTop() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + minHeap.Push(new Node(5)); + minHeap.Push(new Node(3)); + minHeap.Push(new Node(7)); + + Assert.That(minHeap.Top().Value, Is.EqualTo(3)); + AssertHeapProperty(minHeap.ToArray()); + } + + [Test] + public void TestModify() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + var node7 = new Node(7); + minHeap.Push(new Node(5)); + minHeap.Push(new Node(3)); + minHeap.Push(node7); + minHeap.Push(new Node(2)); + minHeap.Push(new Node(4)); + + node7.Value = 1; + var result = minHeap.Modify(node7); // Modify value 7 to 1 + var result2 = minHeap.Modify(new Node(4)); + + Assert.That(result, Is.EqualTo(true)); + Assert.That(result2, Is.EqualTo(false)); + Assert.That(minHeap.Top().Value, Is.EqualTo(1)); + AssertHeapProperty(minHeap.ToArray()); + } + + [Test] + public void TestCount() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + minHeap.Push(new Node(5)); + minHeap.Push(new Node(3)); + minHeap.Push(new Node(7)); + + Assert.That(minHeap.Count, Is.EqualTo(3)); + + minHeap.Pop(); + + Assert.That(minHeap.Count, Is.EqualTo(2)); + + minHeap.Clear(); + + Assert.That(minHeap.Count, Is.EqualTo(0)); + } + + [Test] + public void TestIsEmpty() + { + var minHeap = new RcBinaryMinHeap((x, y) => x.Value.CompareTo(y.Value)); + + Assert.That(minHeap.IsEmpty(), Is.True); + + minHeap.Push(new Node(5)); + + Assert.That(minHeap.IsEmpty(), Is.False); + + minHeap.Pop(); + + Assert.That(minHeap.IsEmpty(), Is.True); + } + + private void AssertHeapProperty(Node[] array) + { + for (int i = 0; i < array.Length / 2; i++) + { + int leftChildIndex = 2 * i + 1; + int rightChildIndex = 2 * i + 2; + + // 왼쪽 자식 노드가 있는지 확인하고 비교 + if (leftChildIndex < array.Length) + Assert.That(array[i].Value, Is.LessThanOrEqualTo(array[leftChildIndex].Value)); + + // 오른쪽 자식 노드가 있는지 확인하고 비교 + if (rightChildIndex < array.Length) + Assert.That(array[i].Value, Is.LessThanOrEqualTo(array[rightChildIndex].Value)); + } + } +} \ No newline at end of file diff --git a/test/DotRecast.Core.Test/RcCyclicBufferTest.cs b/test/DotRecast.Core.Test/RcCyclicBufferTest.cs index df11f1d..a2a0834 100644 --- a/test/DotRecast.Core.Test/RcCyclicBufferTest.cs +++ b/test/DotRecast.Core.Test/RcCyclicBufferTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using DotRecast.Core.Buffers; diff --git a/test/DotRecast.Core.Test/RcHashCodesTest.cs b/test/DotRecast.Core.Test/RcHashCodesTest.cs index 0d500c8..a3883d5 100644 --- a/test/DotRecast.Core.Test/RcHashCodesTest.cs +++ b/test/DotRecast.Core.Test/RcHashCodesTest.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; namespace DotRecast.Core.Test; diff --git a/test/DotRecast.Core.Test/RcIoTests.cs b/test/DotRecast.Core.Test/RcIoTests.cs new file mode 100644 index 0000000..1686296 --- /dev/null +++ b/test/DotRecast.Core.Test/RcIoTests.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using NUnit.Framework; + +namespace DotRecast.Core.Test; + +public class RcIoTests +{ + [Test] + public void Test() + { + const long tileRef = 281474976710656L; + const int dataSize = 344; + + byte[] actual; + + { + using MemoryStream ms = new MemoryStream(); + using BinaryWriter bw = new BinaryWriter(ms); + + RcIO.Write(bw, tileRef, RcByteOrder.LITTLE_ENDIAN); + RcIO.Write(bw, dataSize, RcByteOrder.LITTLE_ENDIAN); + + bw.Flush(); + actual= ms.ToArray(); + } + + { + using MemoryStream ms = new MemoryStream(actual); + using BinaryReader br = new BinaryReader(ms); + var byteBuffer = RcIO.ToByteBuffer(br); + byteBuffer.Order(RcByteOrder.LITTLE_ENDIAN); + + Assert.That(byteBuffer.GetLong(), Is.EqualTo(tileRef)); + Assert.That(byteBuffer.GetInt(), Is.EqualTo(dataSize)); + } + } +} \ No newline at end of file diff --git a/test/DotRecast.Core.Test/RcMathTest.cs b/test/DotRecast.Core.Test/RcMathTest.cs new file mode 100644 index 0000000..81b84b9 --- /dev/null +++ b/test/DotRecast.Core.Test/RcMathTest.cs @@ -0,0 +1,38 @@ +using System; +using NUnit.Framework; + +namespace DotRecast.Core.Test; + +public class RcMathTest +{ + [Test] + public void TestSqr() + { + Assert.That(RcMath.Sqr(0), Is.EqualTo(0)); + Assert.That(RcMath.Sqr(5), Is.EqualTo(25)); + Assert.That(RcMath.Sqr(-5), Is.EqualTo(25)); + Assert.That(RcMath.Sqr(float.PositiveInfinity), Is.EqualTo(float.PositiveInfinity)); + Assert.That(RcMath.Sqr(float.NegativeInfinity), Is.EqualTo(float.PositiveInfinity)); + Assert.That(RcMath.Sqr(float.NaN), Is.EqualTo(float.NaN)); + } + + [Test] + public void TestLerp() + { + // + Assert.That(RcMath.Lerp(-10, 10, 2f), Is.EqualTo(30)); + Assert.That(RcMath.Lerp(-10, 10, 1f), Is.EqualTo(10)); + Assert.That(RcMath.Lerp(-10, 10, 0.5f), Is.EqualTo(0)); + Assert.That(RcMath.Lerp(-10, 10, 0.25f), Is.EqualTo(-5)); + Assert.That(RcMath.Lerp(-10, 10, 0), Is.EqualTo(-10)); + Assert.That(RcMath.Lerp(-10, 10, -0.5f), Is.EqualTo(-20)); + Assert.That(RcMath.Lerp(-10, 10, -1f), Is.EqualTo(-30)); + + // + Assert.That(RcMath.Lerp(10, 10, 0.5f), Is.EqualTo(10)); + Assert.That(RcMath.Lerp(10, 10, 0.8f), Is.EqualTo(10)); + + // + Assert.That(RcMath.Lerp(10, -10, 0.75f), Is.EqualTo(-5)); + } +} \ No newline at end of file diff --git a/test/DotRecast.Core.Test/RcRentedArrayTest.cs b/test/DotRecast.Core.Test/RcRentedArrayTest.cs index 50007f2..6ba6feb 100644 --- a/test/DotRecast.Core.Test/RcRentedArrayTest.cs +++ b/test/DotRecast.Core.Test/RcRentedArrayTest.cs @@ -1,9 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using System.Xml; using DotRecast.Core.Buffers; -using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Serialization; using NUnit.Framework; namespace DotRecast.Core.Test; diff --git a/test/DotRecast.Core.Test/RcSortedQueueTest.cs b/test/DotRecast.Core.Test/RcSortedQueueTest.cs index 537d711..b6532bb 100644 --- a/test/DotRecast.Core.Test/RcSortedQueueTest.cs +++ b/test/DotRecast.Core.Test/RcSortedQueueTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using DotRecast.Core.Collections; diff --git a/test/DotRecast.Core.Test/RcSpanTest.cs b/test/DotRecast.Core.Test/RcSpanTest.cs new file mode 100644 index 0000000..c5f2242 --- /dev/null +++ b/test/DotRecast.Core.Test/RcSpanTest.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace DotRecast.Core.Test; + +public class RcSpanTest +{ + [Test] + public void TestCopy() + { + // Test for copying all elements to the destination span. + { + Span src = stackalloc long[] { 1, 2, 3 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + RcSpans.Copy(src, dst); + Assert.That(src.ToArray(), Is.EqualTo(dst.ToArray())); + } + + // Test for successful copying when the destination Span has a larger size. + { + Span src = stackalloc long[] { 1, 2 }; + Span dst = stackalloc long[] { 0, 0, 0, 0 }; + + RcSpans.Copy(src, dst); + Assert.That(src.ToArray(), Is.EqualTo(dst.Slice(0, src.Length).ToArray())); + } + + // Test for an error when copying to a Span with a smaller size. + { + Assert.Throws(() => + { + Span src = stackalloc long[] { 1, 2, 3, 4, 5 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + RcSpans.Copy(src, dst); + }); + } + + // Test for copying a specific range of elements from the source span to a specific range in the destination span. + { + Span src = stackalloc long[] { 1, 2, 3, 4, 5 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + RcSpans.Copy(src, 2, dst, 0, 2); + Assert.That(src.Slice(2, 2).ToArray(), Is.EqualTo(dst.Slice(0, 2).ToArray())); + } + + // Test for copying a specific range of elements from the source span to a specific range in the destination span. + { + Span src = stackalloc long[] { 5, 4, 3, 2, 1 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + RcSpans.Copy(src, 2, dst, 0, 3); + Assert.That(src.Slice(2, 3).ToArray(), Is.EqualTo(dst.ToArray())); + } + + // Test for src (index + length) over + Assert.Throws(() => + { + Span src = stackalloc long[] { 5, 4, 3, 2, 1 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + // + RcSpans.Copy(src, 3, dst, 0, 3); + }); + + // Test for src (index + length) over + Assert.Throws(() => + { + Span src = stackalloc long[] { 5, 4, 3, 2, 1 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + // + RcSpans.Copy(src, 5, dst, 0, 1); + }); + + + // Test for dst (idx + length) over + Assert.Throws(() => + { + Span src = stackalloc long[] { 5, 4, 3, 2, 1 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + // + RcSpans.Copy(src, 0, dst, 1, 3); + }); + + // Test for dst (idx + length) over + Assert.Throws(() => + { + Span src = stackalloc long[] { 5, 4, 3, 2, 1 }; + Span dst = stackalloc long[] { 0, 0, 0 }; + + // + RcSpans.Copy(src, 0, dst, 0, 4); + }); + } + + [Test] + public void TestMove() + { + // [3, 2, 1] -> [3, 1, 1] + { + var expected = new List() { 3, 1, 1 }; + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 2, 1, 1); + Assert.That(src.ToArray(), Is.EqualTo(expected)); + } + + // [3, 2, 1] -> [2, 1, 1] + { + var expected = new List() { 2, 1, 1 }; + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 1, 0, 2); + Assert.That(src.ToArray(), Is.EqualTo(expected)); + } + + // [3, 2, 1] -> [3, 2, 1] + { + var expected = new List() { 3, 2, 1 }; + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 0, 0, 3); + Assert.That(src.ToArray(), Is.EqualTo(expected)); + } + + // length over + Assert.Throws(() => + { + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 0, 0, 4); + }); + + // source index over + Assert.Throws(() => + { + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 3, 0, 1); + }); + + // destination index over + Assert.Throws(() => + { + Span src = stackalloc long[] { 3, 2, 1 }; + RcSpans.Move(src, 0, 3, 1); + }); + } +} \ No newline at end of file diff --git a/test/DotRecast.Core.Test/RcStackArrayTest.cs b/test/DotRecast.Core.Test/RcStackArrayTest.cs index 2678839..d799e93 100644 --- a/test/DotRecast.Core.Test/RcStackArrayTest.cs +++ b/test/DotRecast.Core.Test/RcStackArrayTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using DotRecast.Core.Collections; using NUnit.Framework; diff --git a/test/DotRecast.Core.Test/Vector3Test.cs b/test/DotRecast.Core.Test/Vector3Test.cs index f845040..6537261 100644 --- a/test/DotRecast.Core.Test/Vector3Test.cs +++ b/test/DotRecast.Core.Test/Vector3Test.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; using DotRecast.Core.Numerics; using NUnit.Framework; diff --git a/test/DotRecast.Detour.Crowd.Test/AbstractCrowdTest.cs b/test/DotRecast.Detour.Crowd.Test/AbstractCrowdTest.cs index 0df0e02..fbd19f1 100644 --- a/test/DotRecast.Detour.Crowd.Test/AbstractCrowdTest.cs +++ b/test/DotRecast.Detour.Crowd.Test/AbstractCrowdTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -25,7 +25,6 @@ using NUnit.Framework; namespace DotRecast.Detour.Crowd.Test; - public class AbstractCrowdTest { protected readonly long[] startRefs = @@ -63,8 +62,9 @@ public class AbstractCrowdTest [SetUp] public void SetUp() { - nmd = new RecastTestMeshBuilder().GetMeshData(); - navmesh = new DtNavMesh(nmd, 6, 0); + nmd = TestMeshDataFactory.Create(); + navmesh = new DtNavMesh(); + navmesh.Init(nmd, 6, 0); query = new DtNavMeshQuery(navmesh); DtCrowdConfig config = new DtCrowdConfig(0.6f); crowd = new DtCrowd(config, navmesh); @@ -153,7 +153,7 @@ public class AbstractCrowdTest RcVec3f vel = RcVec3f.Subtract(tgt, pos); vel.Y = 0.0f; vel = RcVec3f.Normalize(vel); - vel = vel.Scale(speed); + vel = vel * speed; return vel; } diff --git a/test/DotRecast.Detour.Crowd.Test/Crowd1Test.cs b/test/DotRecast.Detour.Crowd.Test/Crowd1Test.cs index 23b60ac..e178b70 100644 --- a/test/DotRecast.Detour.Crowd.Test/Crowd1Test.cs +++ b/test/DotRecast.Detour.Crowd.Test/Crowd1Test.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Crowd.Test/Crowd4Test.cs b/test/DotRecast.Detour.Crowd.Test/Crowd4Test.cs index 30613fb..a0b2af8 100644 --- a/test/DotRecast.Detour.Crowd.Test/Crowd4Test.cs +++ b/test/DotRecast.Detour.Crowd.Test/Crowd4Test.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Crowd.Test/Crowd4VelocityTest.cs b/test/DotRecast.Detour.Crowd.Test/Crowd4VelocityTest.cs index b619d06..14a0fbf 100644 --- a/test/DotRecast.Detour.Crowd.Test/Crowd4VelocityTest.cs +++ b/test/DotRecast.Detour.Crowd.Test/Crowd4VelocityTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Crowd.Test/DotRecast.Detour.Crowd.Test.csproj b/test/DotRecast.Detour.Crowd.Test/DotRecast.Detour.Crowd.Test.csproj index 55953c2..fc62d5e 100644 --- a/test/DotRecast.Detour.Crowd.Test/DotRecast.Detour.Crowd.Test.csproj +++ b/test/DotRecast.Detour.Crowd.Test/DotRecast.Detour.Crowd.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Detour.Crowd.Test/DtPathCorridorTest.cs b/test/DotRecast.Detour.Crowd.Test/DtPathCorridorTest.cs new file mode 100644 index 0000000..2c0bd48 --- /dev/null +++ b/test/DotRecast.Detour.Crowd.Test/DtPathCorridorTest.cs @@ -0,0 +1,73 @@ +/* +recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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.Numerics; +using NUnit.Framework; + +namespace DotRecast.Detour.Crowd.Test; + +public class DtPathCorridorTest +{ + private readonly DtPathCorridor corridor = new DtPathCorridor(); + private readonly IDtQueryFilter filter = new DtQueryDefaultFilter(); + + [SetUp] + public void SetUp() + { + corridor.Init(256); + corridor.Reset(0, new RcVec3f(10, 20, 30)); + } + + [Test] + public void ShouldKeepOriginalPathInFindCornersWhenNothingCanBePruned() + { + var straightPath = new DtStraightPath[4]; + straightPath[0] = new DtStraightPath(new RcVec3f(11, 20, 30.00001f), 0, 0); + straightPath[1] = new DtStraightPath(new RcVec3f(12, 20, 30.00002f), 0, 0); + straightPath[2] = new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0); + straightPath[3] = new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0); + var query = new DtNavMeshQueryMock(straightPath, DtStatus.DT_SUCCESS); + + Span path = stackalloc DtStraightPath[8]; + var npath = corridor.FindCorners(path, 8, query, filter); + Assert.That(npath, Is.EqualTo(4)); + Assert.That(path.Slice(0, npath).ToArray(), Is.EqualTo(straightPath)); + } + + + [Test] + public void ShouldPrunePathInFindCorners() + { + DtStraightPath[] straightPath = new DtStraightPath[5]; + straightPath[0] = (new DtStraightPath(new RcVec3f(10, 20, 30.00001f), 0, 0)); // too close + straightPath[1] = (new DtStraightPath(new RcVec3f(10, 20, 30.00002f), 0, 0)); // too close + straightPath[2] = (new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); + straightPath[3] = (new DtStraightPath(new RcVec3f(12f, 22, 33f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh + straightPath[4] = (new DtStraightPath(new RcVec3f(11f, 21, 32f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh + + var query = new DtNavMeshQueryMock(straightPath, DtStatus.DT_SUCCESS); + + Span path = stackalloc DtStraightPath[8]; + int npath = corridor.FindCorners(path, 8, query, filter); + Assert.That(npath, Is.EqualTo(2)); + Assert.That(path.Slice(0, npath).ToArray(), Is.EqualTo(new DtStraightPath[] { straightPath[2], straightPath[3] })); + } +} \ No newline at end of file diff --git a/test/DotRecast.Detour.Crowd.Test/PathCorridorTest.cs b/test/DotRecast.Detour.Crowd.Test/PathCorridorTest.cs deleted file mode 100644 index fdc10c1..0000000 --- a/test/DotRecast.Detour.Crowd.Test/PathCorridorTest.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* -recast4j copyright (c) 2021 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.Collections.Generic; -using DotRecast.Core.Numerics; - -using Moq; -using NUnit.Framework; - -namespace DotRecast.Detour.Crowd.Test; - - -public class PathCorridorTest -{ - private readonly DtPathCorridor corridor = new DtPathCorridor(); - private readonly IDtQueryFilter filter = new DtQueryDefaultFilter(); - - [SetUp] - public void SetUp() - { - corridor.Init(256); - corridor.Reset(0, new RcVec3f(10, 20, 30)); - } - - [Test] - public void ShouldKeepOriginalPathInFindCornersWhenNothingCanBePruned() - { - List straightPath = new(); - straightPath.Add(new DtStraightPath(new RcVec3f(11, 20, 30.00001f), 0, 0)); - straightPath.Add(new DtStraightPath(new RcVec3f(12, 20, 30.00002f), 0, 0)); - straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); - straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); - var mockQuery = new Mock(It.IsAny()); - mockQuery.Setup(q => q.FindStraightPath( - It.IsAny(), - It.IsAny(), - It.IsAny>(), - ref It.Ref>.IsAny, - It.IsAny(), - It.IsAny()) - ) - .Callback((RcVec3f startPos, RcVec3f endPos, List path, - ref List refStraightPath, int maxStraightPath, int options) => - { - refStraightPath = straightPath; - }) - .Returns(() => DtStatus.DT_SUCCESS); - - var path = new List(); - corridor.FindCorners(ref path, int.MaxValue, mockQuery.Object, filter); - Assert.That(path.Count, Is.EqualTo(4)); - Assert.That(path, Is.EqualTo(straightPath)); - } - - [Test] - public void ShouldPrunePathInFindCorners() - { - List straightPath = new(); - straightPath.Add(new DtStraightPath(new RcVec3f(10, 20, 30.00001f), 0, 0)); // too close - straightPath.Add(new DtStraightPath(new RcVec3f(10, 20, 30.00002f), 0, 0)); // too close - straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), 0, 0)); - straightPath.Add(new DtStraightPath(new RcVec3f(12f, 22, 33f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh - straightPath.Add(new DtStraightPath(new RcVec3f(11f, 21, 32f), DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION, 0)); // offmesh - - var mockQuery = new Mock(It.IsAny()); - mockQuery.Setup(q => q.FindStraightPath( - It.IsAny(), - It.IsAny(), - It.IsAny>(), - ref It.Ref>.IsAny, - It.IsAny(), - It.IsAny()) - ).Callback((RcVec3f startPos, RcVec3f endPos, List path, - ref List refStraightPath, int maxStraightPath, int options) => - { - refStraightPath = straightPath; - }) - .Returns(() => DtStatus.DT_SUCCESS); - - var path = new List(); - corridor.FindCorners(ref path, int.MaxValue, mockQuery.Object, filter); - Assert.That(path.Count, Is.EqualTo(2)); - Assert.That(path, Is.EqualTo(new List { straightPath[2], straightPath[3] })); - } -} \ No newline at end of file diff --git a/test/DotRecast.Detour.Crowd.Test/SampleAreaModifications.cs b/test/DotRecast.Detour.Crowd.Test/SampleAreaModifications.cs index 742e079..aa2d399 100644 --- a/test/DotRecast.Detour.Crowd.Test/SampleAreaModifications.cs +++ b/test/DotRecast.Detour.Crowd.Test/SampleAreaModifications.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Crowd.Test/RecastTestMeshBuilder.cs b/test/DotRecast.Detour.Crowd.Test/TestMeshDataFactory.cs similarity index 53% rename from test/DotRecast.Detour.Crowd.Test/RecastTestMeshBuilder.cs rename to test/DotRecast.Detour.Crowd.Test/TestMeshDataFactory.cs index bacecb9..d186f77 100644 --- a/test/DotRecast.Detour.Crowd.Test/RecastTestMeshBuilder.cs +++ b/test/DotRecast.Detour.Crowd.Test/TestMeshDataFactory.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -22,46 +23,42 @@ using DotRecast.Recast.Geom; namespace DotRecast.Detour.Crowd.Test; -public class RecastTestMeshBuilder +public class TestMeshDataFactory { - private readonly DtMeshData meshData; - public const float m_cellSize = 0.3f; - public const float m_cellHeight = 0.2f; - public const float m_agentHeight = 2.0f; - public const float m_agentRadius = 0.6f; - public const float m_agentMaxClimb = 0.9f; - public const float m_agentMaxSlope = 45.0f; - public const int m_regionMinSize = 8; - public const int m_regionMergeSize = 20; - public const float m_edgeMaxLen = 12.0f; - public const float m_edgeMaxError = 1.3f; - public const int m_vertsPerPoly = 6; - public const float m_detailSampleDist = 6.0f; - public const float m_detailSampleMaxError = 1.0f; + private const float m_cellSize = 0.3f; + private const float m_cellHeight = 0.2f; + private const float m_agentHeight = 2.0f; + private const float m_agentRadius = 0.6f; + private const float m_agentMaxClimb = 0.9f; + private const float m_agentMaxSlope = 45.0f; + private const int m_regionMinSize = 8; + private const int m_regionMergeSize = 20; + private const float m_edgeMaxLen = 12.0f; + private const float m_edgeMaxError = 1.3f; + private const int m_vertsPerPoly = 6; + private const float m_detailSampleDist = 6.0f; + private const float m_detailSampleMaxError = 1.0f; - public RecastTestMeshBuilder() - : this(SimpleInputGeomProvider.LoadFile("dungeon.obj"), - RcPartition.WATERSHED, - m_cellSize, m_cellHeight, - m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb, - m_regionMinSize, m_regionMergeSize, - m_edgeMaxLen, m_edgeMaxError, - m_vertsPerPoly, - m_detailSampleDist, m_detailSampleMaxError) + public static DtMeshData Create() { - } + IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj"); + RcPartition partition = RcPartition.WATERSHED; + float cellSize = m_cellSize; + float cellHeight = m_cellHeight; + float agentMaxSlope = m_agentMaxSlope; + float agentHeight = m_agentHeight; + float agentRadius = m_agentRadius; + float agentMaxClimb = m_agentMaxClimb; + int regionMinSize = m_regionMinSize; + int regionMergeSize = m_regionMergeSize; + float edgeMaxLen = m_edgeMaxLen; + float edgeMaxError = m_edgeMaxError; + int vertsPerPoly = m_vertsPerPoly; + float detailSampleDist = m_detailSampleDist; + float detailSampleMaxError = m_detailSampleMaxError; - public RecastTestMeshBuilder(IInputGeomProvider geom, - RcPartition partitionType, - float cellSize, float cellHeight, - float agentMaxSlope, float agentHeight, float agentRadius, float agentMaxClimb, - int regionMinSize, int regionMergeSize, - float edgeMaxLen, float edgeMaxError, - int vertsPerPoly, - float detailSampleDist, float detailSampleMaxError) - { RcConfig cfg = new RcConfig( - partitionType, + partition, cellSize, cellHeight, agentMaxSlope, agentHeight, agentRadius, agentMaxClimb, regionMinSize, regionMergeSize, @@ -73,31 +70,31 @@ public class RecastTestMeshBuilder RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilder rcBuilder = new RcBuilder(); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false); - RcPolyMesh m_pmesh = rcResult.Mesh; - for (int i = 0; i < m_pmesh.npolys; ++i) + RcPolyMesh pmesh = rcResult.Mesh; + for (int i = 0; i < pmesh.npolys; ++i) { - m_pmesh.flags[i] = 1; + pmesh.flags[i] = 1; } - RcPolyMeshDetail m_dmesh = rcResult.MeshDetail; + RcPolyMeshDetail dmesh = rcResult.MeshDetail; DtNavMeshCreateParams option = new DtNavMeshCreateParams(); - option.verts = m_pmesh.verts; - option.vertCount = m_pmesh.nverts; - option.polys = m_pmesh.polys; - option.polyAreas = m_pmesh.areas; - option.polyFlags = m_pmesh.flags; - option.polyCount = m_pmesh.npolys; - option.nvp = m_pmesh.nvp; - option.detailMeshes = m_dmesh.meshes; - option.detailVerts = m_dmesh.verts; - option.detailVertsCount = m_dmesh.nverts; - option.detailTris = m_dmesh.tris; - option.detailTriCount = m_dmesh.ntris; + 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; + 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 = m_pmesh.bmin; - option.bmax = m_pmesh.bmax; + option.bmin = pmesh.bmin; + option.bmax = pmesh.bmax; option.cs = cellSize; option.ch = cellHeight; option.buildBvTree = true; @@ -120,11 +117,8 @@ public class RecastTestMeshBuilder option.offMeshConUserID = new int[1]; option.offMeshConUserID[0] = 0x4567; option.offMeshConCount = 1; - meshData = DtNavMeshBuilder.CreateNavMeshData(option); - } + var meshData = DtNavMeshBuilder.CreateNavMeshData(option); - public DtMeshData GetMeshData() - { return meshData; } } \ No newline at end of file diff --git a/test/DotRecast.Detour.Dynamic.Test/DotRecast.Detour.Dynamic.Test.csproj b/test/DotRecast.Detour.Dynamic.Test/DotRecast.Detour.Dynamic.Test.csproj index 57e7160..fa65c3a 100644 --- a/test/DotRecast.Detour.Dynamic.Test/DotRecast.Detour.Dynamic.Test.csproj +++ b/test/DotRecast.Detour.Dynamic.Test/DotRecast.Detour.Dynamic.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Detour.Dynamic.Test/DynamicNavMeshTest.cs b/test/DotRecast.Detour.Dynamic.Test/DynamicNavMeshTest.cs index cbcaa04..deb60c3 100644 --- a/test/DotRecast.Detour.Dynamic.Test/DynamicNavMeshTest.cs +++ b/test/DotRecast.Detour.Dynamic.Test/DynamicNavMeshTest.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -6,6 +7,7 @@ using DotRecast.Core.Numerics; using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Detour.Dynamic.Io; using DotRecast.Detour.Dynamic.Test.Io; +using DotRecast.Detour.Io; using NUnit.Framework; namespace DotRecast.Detour.Dynamic.Test; @@ -21,7 +23,7 @@ public class DynamicNavMeshTest [Test] public void E2eTest() { - byte[] bytes = RcResources.Load("test_tiles.voxels"); + byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); @@ -78,4 +80,101 @@ public class DynamicNavMeshTest // path length should be back to the initial value Assert.That(path.Count, Is.EqualTo(16)); } + + + [Test] + public void ShouldSaveAndLoadDynamicNavMesh() + { + using var writerMs = new MemoryStream(); + using var bw = new BinaryWriter(writerMs); + + + int maxVertsPerPoly = 6; + // load voxels from file + + { + byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); + using var readMs = new MemoryStream(bytes); + using var br = new BinaryReader(readMs); + + DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared); + DtVoxelFile f = reader.Read(br); + + // create dynamic navmesh + DtDynamicNavMesh mesh = new DtDynamicNavMesh(f); + + // build navmesh asynchronously using multiple threads + mesh.Build(Task.Factory); + + // Save the resulting nav mesh and re-use it + new DtMeshSetWriter().Write(bw, mesh.NavMesh(), RcByteOrder.LITTLE_ENDIAN, true); + maxVertsPerPoly = mesh.NavMesh().GetMaxVertsPerPoly(); + } + + { + byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); + using var readMs = new MemoryStream(bytes); + using var br = new BinaryReader(readMs); + + // load voxels from file + DtVoxelFileReader reader = new DtVoxelFileReader(DtVoxelTileLZ4ForTestCompressor.Shared); + DtVoxelFile f = reader.Read(br); + + // create dynamic navmesh + DtDynamicNavMesh mesh = new DtDynamicNavMesh(f); + // use the saved nav mesh instead of building from scratch + DtNavMesh navMesh = new DtMeshSetReader().Read(new RcByteBuffer(writerMs.ToArray()), maxVertsPerPoly); + mesh.NavMesh(navMesh); + + DtNavMeshQuery query = new DtNavMeshQuery(mesh.NavMesh()); + IDtQueryFilter filter = new DtQueryDefaultFilter(); + + // find path + _ = query.FindNearestPoly(START_POS, EXTENT, filter, out var startNearestRef, out var startNearestPos, out var _); + _ = query.FindNearestPoly(END_POS, EXTENT, filter, out var endNearestRef, out var endNearestPos, out var _); + + List path = new List(); + query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle); + + // check path length without any obstacles + Assert.That(path.Count, Is.EqualTo(16)); + + // place obstacle + DtCollider colldier = new DtSphereCollider(SPHERE_POS, 20, SampleAreaModifications.SAMPLE_POLYAREA_TYPE_GROUND, 0.1f); + long colliderId = mesh.AddCollider(colldier); + + // update navmesh asynchronously + mesh.Update(Task.Factory); + + // create new query + query = new DtNavMeshQuery(mesh.NavMesh()); + + // find path again + _ = query.FindNearestPoly(START_POS, EXTENT, filter, out startNearestRef, out startNearestPos, out var _); + _ = query.FindNearestPoly(END_POS, EXTENT, filter, out endNearestRef, out endNearestPos, out var _); + + path = new List(); + query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle); + + // check path length with obstacles + Assert.That(path.Count, Is.EqualTo(19)); + + // remove obstacle + mesh.RemoveCollider(colliderId); + // update navmesh asynchronously + mesh.Update(Task.Factory); + + // create new query + query = new DtNavMeshQuery(mesh.NavMesh()); + // find path one more time + _ = query.FindNearestPoly(START_POS, EXTENT, filter, out startNearestRef, out startNearestPos, out var _); + _ = query.FindNearestPoly(END_POS, EXTENT, filter, out endNearestRef, out endNearestPos, out var _); + + path = new List(); + query.FindPath(startNearestRef, endNearestRef, startNearestPos, endNearestPos, filter, ref path, DtFindPathOption.AnyAngle); + + // path length should be back to the initial value + Assert.That(path.Count, Is.EqualTo(16)); + } + } } \ No newline at end of file diff --git a/test/DotRecast.Detour.Dynamic.Test/Io/DtVoxelTileLZ4ForTestCompressor.cs b/test/DotRecast.Detour.Dynamic.Test/Io/DtVoxelTileLZ4ForTestCompressor.cs index a5fab28..958a4b1 100644 --- a/test/DotRecast.Detour.Dynamic.Test/Io/DtVoxelTileLZ4ForTestCompressor.cs +++ b/test/DotRecast.Detour.Dynamic.Test/Io/DtVoxelTileLZ4ForTestCompressor.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderTest.cs b/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderTest.cs index 275330f..cb67afd 100644 --- a/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderTest.cs +++ b/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderTest.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -31,7 +31,7 @@ public class VoxelFileReaderTest [Test] public void ShouldReadSingleTileFile() { - byte[] bytes = RcResources.Load("test.voxels"); + byte[] bytes = RcIO.ReadFileIfFound("test.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); @@ -57,7 +57,7 @@ public class VoxelFileReaderTest [Test] public void ShouldReadMultiTileFile() { - byte[] bytes = RcResources.Load("test_tiles.voxels"); + byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); diff --git a/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderWriterTest.cs b/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderWriterTest.cs index 7ebf01a..75eb73b 100644 --- a/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderWriterTest.cs +++ b/test/DotRecast.Detour.Dynamic.Test/Io/VoxelFileReaderWriterTest.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -32,7 +32,7 @@ public class VoxelFileReaderWriterTest [TestCase(true)] public void ShouldReadSingleTileFile(bool compression) { - byte[] bytes = RcResources.Load("test.voxels"); + byte[] bytes = RcIO.ReadFileIfFound("test.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); @@ -60,7 +60,7 @@ public class VoxelFileReaderWriterTest [TestCase(true)] public void ShouldReadMultiTileFile(bool compression) { - byte[] bytes = RcResources.Load("test_tiles.voxels"); + byte[] bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); diff --git a/test/DotRecast.Detour.Dynamic.Test/SampleAreaModifications.cs b/test/DotRecast.Detour.Dynamic.Test/SampleAreaModifications.cs index f2ea293..6f933b0 100644 --- a/test/DotRecast.Detour.Dynamic.Test/SampleAreaModifications.cs +++ b/test/DotRecast.Detour.Dynamic.Test/SampleAreaModifications.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Dynamic.Test/VoxelQueryTest.cs b/test/DotRecast.Detour.Dynamic.Test/VoxelQueryTest.cs index 451de65..6024013 100644 --- a/test/DotRecast.Detour.Dynamic.Test/VoxelQueryTest.cs +++ b/test/DotRecast.Detour.Dynamic.Test/VoxelQueryTest.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 @@ -93,7 +93,7 @@ public class VoxelQueryTest private DtDynamicNavMesh CreateDynaMesh() { - var bytes = RcResources.Load("test_tiles.voxels"); + var bytes = RcIO.ReadFileIfFound("test_tiles.voxels"); using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); diff --git a/test/DotRecast.Detour.Extras.Test/DotRecast.Detour.Extras.Test.csproj b/test/DotRecast.Detour.Extras.Test/DotRecast.Detour.Extras.Test.csproj index a11f9e8..a8b27dc 100644 --- a/test/DotRecast.Detour.Extras.Test/DotRecast.Detour.Extras.Test.csproj +++ b/test/DotRecast.Detour.Extras.Test/DotRecast.Detour.Extras.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Detour.Extras.Test/Unity/Astar/UnityAStarPathfindingImporterTest.cs b/test/DotRecast.Detour.Extras.Test/Unity/Astar/UnityAStarPathfindingImporterTest.cs index c903ce7..5912061 100644 --- a/test/DotRecast.Detour.Extras.Test/Unity/Astar/UnityAStarPathfindingImporterTest.cs +++ b/test/DotRecast.Detour.Extras.Test/Unity/Astar/UnityAStarPathfindingImporterTest.cs @@ -1,5 +1,6 @@ /* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/AbstractDetourTest.cs b/test/DotRecast.Detour.Test/AbstractDetourTest.cs index 2e086b7..41eb9ac 100644 --- a/test/DotRecast.Detour.Test/AbstractDetourTest.cs +++ b/test/DotRecast.Detour.Test/AbstractDetourTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -21,7 +22,6 @@ using NUnit.Framework; namespace DotRecast.Detour.Test; - public abstract class AbstractDetourTest { protected static readonly long[] startRefs = @@ -64,6 +64,8 @@ public abstract class AbstractDetourTest protected DtNavMesh CreateNavMesh() { - return new DtNavMesh(new RecastTestMeshBuilder().GetMeshData(), 6, 0); + var mesh = new DtNavMesh(); + mesh.Init(TestMeshDataFactory.Create(), 6, 0); + return mesh; } } \ No newline at end of file diff --git a/test/DotRecast.Detour.Test/ConvexConvexIntersectionTest.cs b/test/DotRecast.Detour.Test/ConvexConvexIntersectionTest.cs index 012ecf9..f78f331 100644 --- a/test/DotRecast.Detour.Test/ConvexConvexIntersectionTest.cs +++ b/test/DotRecast.Detour.Test/ConvexConvexIntersectionTest.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/DotRecast.Detour.Test.csproj b/test/DotRecast.Detour.Test/DotRecast.Detour.Test.csproj index c4e8dd5..c2afb29 100644 --- a/test/DotRecast.Detour.Test/DotRecast.Detour.Test.csproj +++ b/test/DotRecast.Detour.Test/DotRecast.Detour.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Detour.Test/DtNodePoolTest.cs b/test/DotRecast.Detour.Test/DtNodePoolTest.cs index 7ad3d68..4abecba 100644 --- a/test/DotRecast.Detour.Test/DtNodePoolTest.cs +++ b/test/DotRecast.Detour.Test/DtNodePoolTest.cs @@ -1,4 +1,4 @@ -using System.Collections.Immutable; +using System.Collections.Immutable; using System.Linq; using NUnit.Framework; diff --git a/test/DotRecast.Detour.Test/DtNodeQueueTest.cs b/test/DotRecast.Detour.Test/DtNodeQueueTest.cs index 5e6c7ad..7b9a89f 100644 --- a/test/DotRecast.Detour.Test/DtNodeQueueTest.cs +++ b/test/DotRecast.Detour.Test/DtNodeQueueTest.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using DotRecast.Core; using DotRecast.Core.Collections; using NUnit.Framework; diff --git a/test/DotRecast.Detour.Test/FindCollectPolyTest.cs b/test/DotRecast.Detour.Test/FindCollectPolyTest.cs new file mode 100644 index 0000000..a213fdf --- /dev/null +++ b/test/DotRecast.Detour.Test/FindCollectPolyTest.cs @@ -0,0 +1,68 @@ +using System; +using System.Linq; +using DotRecast.Core.Numerics; +using NUnit.Framework; + +namespace DotRecast.Detour.Test; + +public class FindCollectPolyTest : AbstractDetourTest +{ + private static readonly long[][] POLY_REFS = + { + new long[] + { + 281474976710697L, + 281474976710695L, + 281474976710696L, + 281474976710691L, + }, + new long[] + { + 281474976710769L, + 281474976710773L, + }, + new long[] + { + 281474976710676L, + 281474976710678L, + 281474976710679L, + 281474976710674L, + 281474976710677L, + 281474976710683L, + 281474976710680L, + 281474976710684L, + }, + + new long[] + { + 281474976710748L, + 281474976710753L, + 281474976710752L, + 281474976710750L, + }, + + new long[] + { + 281474976710736L, + 281474976710733L, + 281474976710735L, + } + }; + + [Test] + public void TestFindNearestPoly() + { + IDtQueryFilter filter = new DtQueryDefaultFilter(); + RcVec3f extents = new RcVec3f(2, 4, 2); + var polys = new long[32]; + for (int i = 0; i < startRefs.Length; i++) + { + Array.Fill(polys, 0); + RcVec3f startPos = startPoss[i]; + var status = query.QueryPolygons(startPos, extents, filter, polys, out var polyCount, 32); + Assert.That(status.Succeeded(), Is.True, $"index({i})"); + Assert.That(polyCount, Is.EqualTo(POLY_REFS[i].Length), $"index({i})"); + Assert.That(polys.AsSpan(0, polyCount).ToArray(), Is.EqualTo(POLY_REFS[i]), $"index({i})"); + } + } +} \ No newline at end of file diff --git a/test/DotRecast.Detour.Test/FindDistanceToWallTest.cs b/test/DotRecast.Detour.Test/FindDistanceToWallTest.cs index 4fe79a1..f3ad19c 100644 --- a/test/DotRecast.Detour.Test/FindDistanceToWallTest.cs +++ b/test/DotRecast.Detour.Test/FindDistanceToWallTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/FindLocalNeighbourhoodTest.cs b/test/DotRecast.Detour.Test/FindLocalNeighbourhoodTest.cs index 662d9d0..300c9c6 100644 --- a/test/DotRecast.Detour.Test/FindLocalNeighbourhoodTest.cs +++ b/test/DotRecast.Detour.Test/FindLocalNeighbourhoodTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/FindNearestPolyTest.cs b/test/DotRecast.Detour.Test/FindNearestPolyTest.cs index bf47916..894fd50 100644 --- a/test/DotRecast.Detour.Test/FindNearestPolyTest.cs +++ b/test/DotRecast.Detour.Test/FindNearestPolyTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -21,10 +22,12 @@ using NUnit.Framework; namespace DotRecast.Detour.Test; - public class FindNearestPolyTest : AbstractDetourTest { - private static readonly long[] POLY_REFS = { 281474976710696L, 281474976710773L, 281474976710680L, 281474976710753L, 281474976710733L }; + private static readonly long[] POLY_REFS = + { + 281474976710696L, 281474976710773L, 281474976710680L, 281474976710753L, 281474976710733L + }; private static readonly RcVec3f[] POLY_POS = { @@ -44,11 +47,11 @@ public class FindNearestPolyTest : AbstractDetourTest { RcVec3f startPos = startPoss[i]; var status = query.FindNearestPoly(startPos, extents, filter, out var nearestRef, out var nearestPt, out var _); - Assert.That(status.Succeeded(), Is.True); - Assert.That(nearestRef, Is.EqualTo(POLY_REFS[i])); - Assert.That(nearestPt.X, Is.EqualTo(POLY_POS[i].X).Within(0.001f)); - Assert.That(nearestPt.Y, Is.EqualTo(POLY_POS[i].Y).Within(0.001f)); - Assert.That(nearestPt.Z, Is.EqualTo(POLY_POS[i].Z).Within(0.001f)); + Assert.That(status.Succeeded(), Is.True, $"index({i})"); + Assert.That(nearestRef, Is.EqualTo(POLY_REFS[i]), $"index({i})"); + Assert.That(nearestPt.X, Is.EqualTo(POLY_POS[i].X).Within(0.001f), $"index({i})"); + Assert.That(nearestPt.Y, Is.EqualTo(POLY_POS[i].Y).Within(0.001f), $"index({i})"); + Assert.That(nearestPt.Z, Is.EqualTo(POLY_POS[i].Z).Within(0.001f), $"index({i})"); } } diff --git a/test/DotRecast.Detour.Test/FindPathTest.cs b/test/DotRecast.Detour.Test/FindPathTest.cs index 4ce825a..e00dc86 100644 --- a/test/DotRecast.Detour.Test/FindPathTest.cs +++ b/test/DotRecast.Detour.Test/FindPathTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -16,13 +17,13 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; using System.Collections.Generic; using DotRecast.Core.Numerics; using NUnit.Framework; namespace DotRecast.Detour.Test; - public class FindPathTest : AbstractDetourTest { private static readonly DtStatus[] STATUSES = @@ -184,6 +185,7 @@ public class FindPathTest : AbstractDetourTest { IDtQueryFilter filter = new DtQueryDefaultFilter(); var path = new List(); + Span straightPath = stackalloc DtStraightPath[256]; for (int i = 0; i < STRAIGHT_PATHS.Length; i++) { // startRefs.Length; i++) { @@ -192,9 +194,8 @@ public class FindPathTest : AbstractDetourTest var startPos = startPoss[i]; var endPos = endPoss[i]; var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); - var straightPath = new List(); - query.FindStraightPath(startPos, endPos, path, ref straightPath, int.MaxValue, 0); - Assert.That(straightPath.Count, Is.EqualTo(STRAIGHT_PATHS[i].Length)); + query.FindStraightPath(startPos, endPos, path, path.Count, straightPath, out var nstraightPath, 256, 0); + Assert.That(nstraightPath, Is.EqualTo(STRAIGHT_PATHS[i].Length)); for (int j = 0; j < STRAIGHT_PATHS[i].Length; j++) { Assert.That(straightPath[j].refs, Is.EqualTo(STRAIGHT_PATHS[i][j].refs)); diff --git a/test/DotRecast.Detour.Test/FindPolysAroundCircleTest.cs b/test/DotRecast.Detour.Test/FindPolysAroundCircleTest.cs index 5c0be03..f71979e 100644 --- a/test/DotRecast.Detour.Test/FindPolysAroundCircleTest.cs +++ b/test/DotRecast.Detour.Test/FindPolysAroundCircleTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/FindPolysAroundShapeTest.cs b/test/DotRecast.Detour.Test/FindPolysAroundShapeTest.cs index 6bae858..653ca66 100644 --- a/test/DotRecast.Detour.Test/FindPolysAroundShapeTest.cs +++ b/test/DotRecast.Detour.Test/FindPolysAroundShapeTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/GetPolyWallSegmentsTest.cs b/test/DotRecast.Detour.Test/GetPolyWallSegmentsTest.cs index b05f52d..d5b3873 100644 --- a/test/DotRecast.Detour.Test/GetPolyWallSegmentsTest.cs +++ b/test/DotRecast.Detour.Test/GetPolyWallSegmentsTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -16,13 +17,13 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; using System.Collections.Generic; using DotRecast.Core; using NUnit.Framework; namespace DotRecast.Detour.Test; - public class GetPolyWallSegmentsTest : AbstractDetourTest { private static readonly RcSegmentVert[][] VERTICES = @@ -82,28 +83,30 @@ public class GetPolyWallSegmentsTest : AbstractDetourTest [Test] public void TestFindDistanceToWall() { - var segmentVerts = new List(); - var segmentRefs = new List(); + const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4; + Span segs = stackalloc RcSegmentVert[MAX_SEGS]; + Span refs = stackalloc long[MAX_SEGS]; + int nsegs = 0; IDtQueryFilter filter = new DtQueryDefaultFilter(); for (int i = 0; i < startRefs.Length; i++) { - var result = query.GetPolyWallSegments(startRefs[i], true, filter, ref segmentVerts, ref segmentRefs); - Assert.That(segmentVerts.Count, Is.EqualTo(VERTICES[i].Length)); - Assert.That(segmentRefs.Count, Is.EqualTo(REFS[i].Length)); + var result = query.GetPolyWallSegments(startRefs[i], filter, segs, refs, ref nsegs, MAX_SEGS); + Assert.That(nsegs, Is.EqualTo(VERTICES[i].Length)); + Assert.That(nsegs, Is.EqualTo(REFS[i].Length)); for (int v = 0; v < VERTICES[i].Length / 6; v++) { - Assert.That(segmentVerts[v].vmin.X, Is.EqualTo(VERTICES[i][v].vmin.X).Within(0.001f)); - Assert.That(segmentVerts[v].vmin.Y, Is.EqualTo(VERTICES[i][v].vmin.Y).Within(0.001f)); - Assert.That(segmentVerts[v].vmin.Z, Is.EqualTo(VERTICES[i][v].vmin.Z).Within(0.001f)); - Assert.That(segmentVerts[v].vmax.X, Is.EqualTo(VERTICES[i][v].vmax.X).Within(0.001f)); - Assert.That(segmentVerts[v].vmax.Y, Is.EqualTo(VERTICES[i][v].vmax.Y).Within(0.001f)); - Assert.That(segmentVerts[v].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f)); + Assert.That(segs[v].vmin.X, Is.EqualTo(VERTICES[i][v].vmin.X).Within(0.001f)); + Assert.That(segs[v].vmin.Y, Is.EqualTo(VERTICES[i][v].vmin.Y).Within(0.001f)); + Assert.That(segs[v].vmin.Z, Is.EqualTo(VERTICES[i][v].vmin.Z).Within(0.001f)); + Assert.That(segs[v].vmax.X, Is.EqualTo(VERTICES[i][v].vmax.X).Within(0.001f)); + Assert.That(segs[v].vmax.Y, Is.EqualTo(VERTICES[i][v].vmax.Y).Within(0.001f)); + Assert.That(segs[v].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f)); } for (int v = 0; v < REFS[i].Length; v++) { - Assert.That(segmentRefs[v], Is.EqualTo(REFS[i][v])); + Assert.That(refs[v], Is.EqualTo(REFS[i][v])); } } } diff --git a/test/DotRecast.Detour.Test/Io/MeshDataReaderWriterTest.cs b/test/DotRecast.Detour.Test/Io/MeshDataReaderWriterTest.cs index acb8ad8..2cdd4ed 100644 --- a/test/DotRecast.Detour.Test/Io/MeshDataReaderWriterTest.cs +++ b/test/DotRecast.Detour.Test/Io/MeshDataReaderWriterTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -32,8 +33,7 @@ public class MeshDataReaderWriterTest [SetUp] public void SetUp() { - RecastTestMeshBuilder rcBuilder = new RecastTestMeshBuilder(); - meshData = rcBuilder.GetMeshData(); + meshData = TestMeshDataFactory.Create(); } [Test] @@ -117,11 +117,8 @@ public class MeshDataReaderWriterTest for (int i = 0; i < meshData.header.bvNodeCount; i++) { Assert.That(readData.bvTree[i].i, Is.EqualTo(meshData.bvTree[i].i)); - for (int j = 0; j < 3; j++) - { - Assert.That(readData.bvTree[i].bmin[j], Is.EqualTo(meshData.bvTree[i].bmin[j])); - Assert.That(readData.bvTree[i].bmax[j], Is.EqualTo(meshData.bvTree[i].bmax[j])); - } + Assert.That(readData.bvTree[i].bmin, Is.EqualTo(meshData.bvTree[i].bmin)); + Assert.That(readData.bvTree[i].bmax, Is.EqualTo(meshData.bvTree[i].bmax)); } for (int i = 0; i < meshData.header.offMeshConCount; i++) diff --git a/test/DotRecast.Detour.Test/Io/MeshSetReaderTest.cs b/test/DotRecast.Detour.Test/Io/MeshSetReaderTest.cs index e2bf949..e23f621 100644 --- a/test/DotRecast.Detour.Test/Io/MeshSetReaderTest.cs +++ b/test/DotRecast.Detour.Test/Io/MeshSetReaderTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -24,7 +25,6 @@ using NUnit.Framework; namespace DotRecast.Detour.Test.Io; - public class MeshSetReaderTest { private readonly DtMeshSetReader reader = new DtMeshSetReader(); @@ -32,27 +32,35 @@ public class MeshSetReaderTest [Test] public void TestNavmesh() { - byte[] @is = RcResources.Load("all_tiles_navmesh.bin"); + byte[] @is = RcIO.ReadFileIfFound("all_tiles_navmesh.bin"); using var ms = new MemoryStream(@is); using var br = new BinaryReader(ms); DtNavMesh mesh = reader.Read(br, 6); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); - List tiles = mesh.GetTilesAt(4, 7); - Assert.That(tiles.Count, Is.EqualTo(1)); + + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = mesh.GetTilesAt(4, 7, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(22 * 3)); - tiles = mesh.GetTilesAt(1, 6); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(1, 6, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(7)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(26 * 3)); - tiles = mesh.GetTilesAt(6, 2); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(6, 2, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(1)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(4 * 3)); - tiles = mesh.GetTilesAt(7, 6); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(7, 6, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(8)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(24 * 3)); } @@ -60,7 +68,7 @@ public class MeshSetReaderTest [Test] public void TestDungeon() { - byte[] @is = RcResources.Load("dungeon_all_tiles_navmesh.bin"); + byte[] @is = RcIO.ReadFileIfFound("dungeon_all_tiles_navmesh.bin"); using var ms = new MemoryStream(@is); using var br = new BinaryReader(ms); @@ -68,20 +76,28 @@ public class MeshSetReaderTest Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); - List tiles = mesh.GetTilesAt(6, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); - tiles = mesh.GetTilesAt(2, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); - tiles = mesh.GetTilesAt(4, 3); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); - tiles = mesh.GetTilesAt(2, 8); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); } @@ -89,7 +105,7 @@ public class MeshSetReaderTest [Test] public void TestDungeon32Bit() { - byte[] @is = RcResources.Load("dungeon_all_tiles_navmesh_32bit.bin"); + byte[] @is = RcIO.ReadFileIfFound("dungeon_all_tiles_navmesh_32bit.bin"); using var ms = new MemoryStream(@is); using var br = new BinaryReader(ms); @@ -97,21 +113,29 @@ public class MeshSetReaderTest Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); - List tiles = mesh.GetTilesAt(6, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); - tiles = mesh.GetTilesAt(2, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); - tiles = mesh.GetTilesAt(4, 3); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); - tiles = mesh.GetTilesAt(2, 8); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); } -} +} \ No newline at end of file diff --git a/test/DotRecast.Detour.Test/Io/MeshSetReaderWriterTest.cs b/test/DotRecast.Detour.Test/Io/MeshSetReaderWriterTest.cs index c01ea73..58e37a1 100644 --- a/test/DotRecast.Detour.Test/Io/MeshSetReaderWriterTest.cs +++ b/test/DotRecast.Detour.Test/Io/MeshSetReaderWriterTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -28,7 +29,6 @@ using NUnit.Framework; namespace DotRecast.Detour.Test.Io; - public class MeshSetReaderWriterTest { private readonly DtMeshSetWriter writer = new DtMeshSetWriter(); @@ -66,11 +66,12 @@ public class MeshSetReaderWriterTest header.option.maxTiles = m_maxTiles; header.option.maxPolys = m_maxPolysPerTile; header.numTiles = 0; - DtNavMesh mesh = new DtNavMesh(header.option, 6); + DtNavMesh mesh = new DtNavMesh(); + mesh.Init(header.option, 6); RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmax = geom.GetMeshBoundsMax(); - RcCommons.CalcTileCount(bmin, bmax, m_cellSize, m_tileSize, m_tileSize, out var tw, out var th); + RcRecast.CalcTileCount(bmin, bmax, m_cellSize, m_tileSize, m_tileSize, out var tw, out var th); for (int y = 0; y < th; ++y) { for (int x = 0; x < tw; ++x) @@ -92,7 +93,7 @@ public class MeshSetReaderWriterTest if (data != null) { mesh.RemoveTile(mesh.GetTileRefAt(x, y, 0)); - mesh.AddTile(data, 0, 0); + mesh.AddTile(data, 0, 0, out _); } } } @@ -107,20 +108,28 @@ public class MeshSetReaderWriterTest Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); - List tiles = mesh.GetTilesAt(6, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = mesh.GetTilesAt(6, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); - tiles = mesh.GetTilesAt(2, 9); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 9, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(2)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); - tiles = mesh.GetTilesAt(4, 3); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(4, 3, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 3)); - tiles = mesh.GetTilesAt(2, 8); - Assert.That(tiles.Count, Is.EqualTo(1)); + + nneis = mesh.GetTilesAt(2, 8, tiles, MAX_NEIS); + Assert.That(nneis, Is.EqualTo(1)); Assert.That(tiles[0].data.polys.Length, Is.EqualTo(5)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); } diff --git a/test/DotRecast.Detour.Test/MoveAlongSurfaceTest.cs b/test/DotRecast.Detour.Test/MoveAlongSurfaceTest.cs index 4a84028..22ae8e1 100644 --- a/test/DotRecast.Detour.Test/MoveAlongSurfaceTest.cs +++ b/test/DotRecast.Detour.Test/MoveAlongSurfaceTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -16,13 +17,13 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; using System.Collections.Generic; using DotRecast.Core.Numerics; using NUnit.Framework; namespace DotRecast.Detour.Test; - public class MoveAlongSurfaceTest : AbstractDetourTest { private static readonly long[][] VISITED = @@ -69,20 +70,21 @@ public class MoveAlongSurfaceTest : AbstractDetourTest public void TestMoveAlongSurface() { IDtQueryFilter filter = new DtQueryDefaultFilter(); - var visited = new List(); + const int MAX_VISITED = 32; + Span visited = stackalloc long[MAX_VISITED]; for (int i = 0; i < startRefs.Length; i++) { long startRef = startRefs[i]; RcVec3f startPos = startPoss[i]; RcVec3f endPos = endPoss[i]; - var status = query.MoveAlongSurface(startRef, startPos, endPos, filter, out var result, ref visited); + var status = query.MoveAlongSurface(startRef, startPos, endPos, filter, out var result, visited, out var nvisited, MAX_VISITED); Assert.That(status.Succeeded(), Is.True); Assert.That(result.X, Is.EqualTo(POSITION[i].X).Within(0.01f)); Assert.That(result.Y, Is.EqualTo(POSITION[i].Y).Within(0.01f)); Assert.That(result.Z, Is.EqualTo(POSITION[i].Z).Within(0.01f)); - Assert.That(visited.Count, Is.EqualTo(VISITED[i].Length)); + Assert.That(nvisited, Is.EqualTo(VISITED[i].Length)); for (int j = 0; j < 3; j++) { Assert.That(visited[j], Is.EqualTo(VISITED[i][j])); diff --git a/test/DotRecast.Detour.Test/NavMeshBuilderTest.cs b/test/DotRecast.Detour.Test/NavMeshBuilderTest.cs index 2a3abbb..dc441ec 100644 --- a/test/DotRecast.Detour.Test/NavMeshBuilderTest.cs +++ b/test/DotRecast.Detour.Test/NavMeshBuilderTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -21,6 +22,7 @@ using NUnit.Framework; namespace DotRecast.Detour.Test; +using static DtDetour; public class NavMeshBuilderTest { @@ -29,7 +31,7 @@ public class NavMeshBuilderTest [SetUp] public void SetUp() { - nmd = new RecastTestMeshBuilder().GetMeshData(); + nmd = TestMeshDataFactory.Create(); } [Test] @@ -52,12 +54,12 @@ public class NavMeshBuilderTest for (int i = 0; i < 2; i++) { - Assert.That(RcVecUtils.Create(nmd.verts, 223 * 3 + (i * 3)), Is.EqualTo(nmd.offMeshCons[0].pos[i])); + Assert.That(RcVec.Create(nmd.verts, 223 * 3 + (i * 3)), Is.EqualTo(nmd.offMeshCons[0].pos[i])); } Assert.That(nmd.offMeshCons[0].rad, Is.EqualTo(0.1f)); Assert.That(nmd.offMeshCons[0].poly, Is.EqualTo(118)); - Assert.That(nmd.offMeshCons[0].flags, Is.EqualTo(DtNavMesh.DT_OFFMESH_CON_BIDIR)); + Assert.That(nmd.offMeshCons[0].flags, Is.EqualTo(DT_OFFMESH_CON_BIDIR)); Assert.That(nmd.offMeshCons[0].side, Is.EqualTo(0xFF)); Assert.That(nmd.offMeshCons[0].userId, Is.EqualTo(0x4567)); Assert.That(nmd.polys[118].vertCount, Is.EqualTo(2)); diff --git a/test/DotRecast.Detour.Test/PolygonByCircleConstraintTest.cs b/test/DotRecast.Detour.Test/PolygonByCircleConstraintTest.cs index 070848a..7e7c231 100644 --- a/test/DotRecast.Detour.Test/PolygonByCircleConstraintTest.cs +++ b/test/DotRecast.Detour.Test/PolygonByCircleConstraintTest.cs @@ -1,6 +1,6 @@ /* recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/RandomPointTest.cs b/test/DotRecast.Detour.Test/RandomPointTest.cs index c98079f..9495c5f 100644 --- a/test/DotRecast.Detour.Test/RandomPointTest.cs +++ b/test/DotRecast.Detour.Test/RandomPointTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2021 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -19,21 +20,22 @@ freely, subject to the following restrictions: using System; using DotRecast.Core; using DotRecast.Core.Numerics; - using NUnit.Framework; namespace DotRecast.Detour.Test; - public class RandomPointTest : AbstractDetourTest { [Test] + [Repeat(10)] public void TestRandom() { RcRand f = new RcRand(1); IDtQueryFilter filter = new DtQueryDefaultFilter(); - for (int i = 0; i < 1000; i++) + + var begin = RcFrequency.Ticks; + for (int i = 0; i < 10000; i++) { var status = query.FindRandomPoint(filter, f, out var randomRef, out var randomPt); Assert.That(status.Succeeded(), Is.True); @@ -55,6 +57,9 @@ public class RandomPointTest : AbstractDetourTest Assert.That(randomPt.Z >= bmin[1], Is.True); Assert.That(randomPt.Z <= bmax[1], Is.True); } + + var ticks = RcFrequency.Ticks - begin; + Console.WriteLine($"RandomPointTest::TestRandom() - {(double)ticks / TimeSpan.TicksPerMillisecond} ms"); } [Test] @@ -72,7 +77,7 @@ public class RandomPointTest : AbstractDetourTest randomPt = nextRandomPt; status = navmesh.GetTileAndPolyByRef(randomRef, out var tile, out var poly); - + float[] bmin = new float[2]; float[] bmax = new float[2]; for (int j = 0; j < poly.vertCount; j++) @@ -103,7 +108,7 @@ public class RandomPointTest : AbstractDetourTest var status = query.FindRandomPointWithinCircle(randomRef, randomPt, radius, filter, f, out var nextRandomRef, out var nextRandomPt); Assert.That(status.Failed(), Is.False); - float distance = RcVecUtils.Dist2D(randomPt, nextRandomPt); + float distance = RcVec.Dist2D(randomPt, nextRandomPt); Assert.That(distance <= radius, Is.True); randomRef = nextRandomRef; diff --git a/test/DotRecast.Detour.Test/SampleAreaModifications.cs b/test/DotRecast.Detour.Test/SampleAreaModifications.cs index 73ce304..eff5001 100644 --- a/test/DotRecast.Detour.Test/SampleAreaModifications.cs +++ b/test/DotRecast.Detour.Test/SampleAreaModifications.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/TestDetourBuilder.cs b/test/DotRecast.Detour.Test/TestDetourBuilder.cs index 4df7c6d..83f9a9f 100644 --- a/test/DotRecast.Detour.Test/TestDetourBuilder.cs +++ b/test/DotRecast.Detour.Test/TestDetourBuilder.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.Test/RecastTestMeshBuilder.cs b/test/DotRecast.Detour.Test/TestMeshDataFactory.cs similarity index 64% rename from test/DotRecast.Detour.Test/RecastTestMeshBuilder.cs rename to test/DotRecast.Detour.Test/TestMeshDataFactory.cs index 12c618e..fec3aab 100644 --- a/test/DotRecast.Detour.Test/RecastTestMeshBuilder.cs +++ b/test/DotRecast.Detour.Test/TestMeshDataFactory.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -16,15 +17,13 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using DotRecast.Core.Numerics; using DotRecast.Recast; using DotRecast.Recast.Geom; namespace DotRecast.Detour.Test; -public class RecastTestMeshBuilder +public static class TestMeshDataFactory { - private readonly DtMeshData meshData; private const float m_cellSize = 0.3f; private const float m_cellHeight = 0.2f; private const float m_agentHeight = 2.0f; @@ -39,27 +38,24 @@ public class RecastTestMeshBuilder private const float m_detailSampleDist = 6.0f; private const float m_detailSampleMaxError = 1.0f; - public RecastTestMeshBuilder() - : this(SimpleInputGeomProvider.LoadFile("dungeon.obj"), - RcPartition.WATERSHED, - m_cellSize, m_cellHeight, - m_agentMaxSlope, m_agentHeight, m_agentRadius, m_agentMaxClimb, - m_regionMinSize, m_regionMergeSize, - m_edgeMaxLen, m_edgeMaxError, - m_vertsPerPoly, - m_detailSampleDist, m_detailSampleMaxError) + public static DtMeshData Create() { - } + IInputGeomProvider geom = SimpleInputGeomProvider.LoadFile("dungeon.obj"); + RcPartition partition = RcPartition.WATERSHED; + float cellSize = m_cellSize; + float cellHeight = m_cellHeight; + float agentMaxSlope = m_agentMaxSlope; + float agentHeight = m_agentHeight; + float agentRadius = m_agentRadius; + float agentMaxClimb = m_agentMaxClimb; + int regionMinSize = m_regionMinSize; + int regionMergeSize = m_regionMergeSize; + float edgeMaxLen = m_edgeMaxLen; + float edgeMaxError = m_edgeMaxError; + int vertsPerPoly = m_vertsPerPoly; + float detailSampleDist = m_detailSampleDist; + float detailSampleMaxError = m_detailSampleMaxError; - public RecastTestMeshBuilder(IInputGeomProvider geom, - RcPartition partition, - float cellSize, float cellHeight, - float agentMaxSlope, float agentHeight, float agentRadius, float agentMaxClimb, - int regionMinSize, int regionMergeSize, - float edgeMaxLen, float edgeMaxError, - int vertsPerPoly, - float detailSampleDist, float detailSampleMaxError) - { RcConfig cfg = new RcConfig( partition, cellSize, cellHeight, @@ -73,31 +69,31 @@ public class RecastTestMeshBuilder RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilder rcBuilder = new RcBuilder(); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false); - RcPolyMesh m_pmesh = rcResult.Mesh; - for (int i = 0; i < m_pmesh.npolys; ++i) + RcPolyMesh pmesh = rcResult.Mesh; + for (int i = 0; i < pmesh.npolys; ++i) { - m_pmesh.flags[i] = 1; + pmesh.flags[i] = 1; } - RcPolyMeshDetail m_dmesh = rcResult.MeshDetail; + RcPolyMeshDetail dmesh = rcResult.MeshDetail; DtNavMeshCreateParams option = new DtNavMeshCreateParams(); - option.verts = m_pmesh.verts; - option.vertCount = m_pmesh.nverts; - option.polys = m_pmesh.polys; - option.polyAreas = m_pmesh.areas; - option.polyFlags = m_pmesh.flags; - option.polyCount = m_pmesh.npolys; - option.nvp = m_pmesh.nvp; - option.detailMeshes = m_dmesh.meshes; - option.detailVerts = m_dmesh.verts; - option.detailVertsCount = m_dmesh.nverts; - option.detailTris = m_dmesh.tris; - option.detailTriCount = m_dmesh.ntris; + 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; + 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 = m_pmesh.bmin; - option.bmax = m_pmesh.bmax; + option.bmin = pmesh.bmin; + option.bmax = pmesh.bmax; option.cs = cellSize; option.ch = cellHeight; option.buildBvTree = true; @@ -120,11 +116,8 @@ public class RecastTestMeshBuilder option.offMeshConUserID = new int[1]; option.offMeshConUserID[0] = 0x4567; option.offMeshConCount = 1; - meshData = DtNavMeshBuilder.CreateNavMeshData(option); - } + var meshData = DtNavMeshBuilder.CreateNavMeshData(option); - public DtMeshData GetMeshData() - { return meshData; } } \ No newline at end of file diff --git a/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs b/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs index c03623e..5773ca5 100644 --- a/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs +++ b/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -17,7 +18,6 @@ freely, subject to the following restrictions: */ using System.Collections.Generic; -using DotRecast.Core.Numerics; using DotRecast.Recast; using DotRecast.Recast.Geom; @@ -64,7 +64,8 @@ public class TestTiledNavMeshBuilder navMeshParams.tileHeight = tileSize * cellSize; navMeshParams.maxTiles = 128; navMeshParams.maxPolys = 32768; - navMesh = new DtNavMesh(navMeshParams, 6); + navMesh = new DtNavMesh(); + navMesh.Init(navMeshParams, 6); // Build all tiles RcConfig cfg = new RcConfig(true, tileSize, tileSize, RcConfig.CalcBorder(agentRadius, cellSize), @@ -119,7 +120,7 @@ public class TestTiledNavMeshBuilder option.tileX = result.TileX; option.tileZ = result.TileZ; option.buildBvTree = true; - navMesh.AddTile(DtNavMeshBuilder.CreateNavMeshData(option), 0, 0); + navMesh.AddTile(Detour.DtNavMeshBuilder.CreateNavMeshData(option), 0, 0, out _); } } diff --git a/test/DotRecast.Detour.Test/TiledFindPathTest.cs b/test/DotRecast.Detour.Test/TiledFindPathTest.cs index 09d712c..c57f2e8 100644 --- a/test/DotRecast.Detour.Test/TiledFindPathTest.cs +++ b/test/DotRecast.Detour.Test/TiledFindPathTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/AbstractTileCacheTest.cs b/test/DotRecast.Detour.TileCache.Test/AbstractTileCacheTest.cs index fded142..1bf5e71 100644 --- a/test/DotRecast.Detour.TileCache.Test/AbstractTileCacheTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/AbstractTileCacheTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -44,7 +44,7 @@ public class AbstractTileCacheTest public DtTileCache GetTileCache(IInputGeomProvider geom, RcByteOrder order, bool cCompatibility) { DtTileCacheParams option = new DtTileCacheParams(); - RcCommons.CalcTileCount(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), m_cellSize, m_tileSize, m_tileSize, out var tw, out var th); + RcRecast.CalcTileCount(geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax(), m_cellSize, m_tileSize, m_tileSize, out var tw, out var th); option.ch = m_cellHeight; option.cs = m_cellSize; option.orig = geom.GetMeshBoundsMin(); @@ -64,7 +64,8 @@ public class AbstractTileCacheTest navMeshParams.maxTiles = 256; navMeshParams.maxPolys = 16384; - var navMesh = new DtNavMesh(navMeshParams, 6); + var navMesh = new DtNavMesh(); + navMesh.Init(navMeshParams, 6); var comp = DtTileCacheCompressorFactory.Shared.Create(cCompatibility ? 0 : 1); var storageParams = new DtTileCacheStorageParams(order, cCompatibility); var process = new TestTileCacheMeshProcess(); diff --git a/test/DotRecast.Detour.TileCache.Test/DotRecast.Detour.TileCache.Test.csproj b/test/DotRecast.Detour.TileCache.Test/DotRecast.Detour.TileCache.Test.csproj index 4278add..506423f 100644 --- a/test/DotRecast.Detour.TileCache.Test/DotRecast.Detour.TileCache.Test.csproj +++ b/test/DotRecast.Detour.TileCache.Test/DotRecast.Detour.TileCache.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Detour.TileCache.Test/Io/DtTileCacheLZ4ForTestCompressor.cs b/test/DotRecast.Detour.TileCache.Test/Io/DtTileCacheLZ4ForTestCompressor.cs index 0a22d94..e08f3e6 100644 --- a/test/DotRecast.Detour.TileCache.Test/Io/DtTileCacheLZ4ForTestCompressor.cs +++ b/test/DotRecast.Detour.TileCache.Test/Io/DtTileCacheLZ4ForTestCompressor.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderTest.cs b/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderTest.cs index ba67a7f..dbb5e7a 100644 --- a/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -35,7 +35,7 @@ public class TileCacheReaderTest [Test] public void TestNavmesh() { - using var ms = new MemoryStream(RcResources.Load("all_tiles_tilecache.bin")); + using var ms = new MemoryStream(RcIO.ReadFileIfFound("all_tiles_tilecache.bin")); using var br = new BinaryReader(ms); DtTileCache tc = reader.Read(br, 6, null); Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256)); @@ -133,7 +133,7 @@ public class TileCacheReaderTest [Test] public void TestDungeon() { - using var ms = new MemoryStream(RcResources.Load("dungeon_all_tiles_tilecache.bin")); + using var ms = new MemoryStream(RcIO.ReadFileIfFound("dungeon_all_tiles_tilecache.bin")); using var br = new BinaryReader(ms); DtTileCache tc = reader.Read(br, 6, null); Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256)); diff --git a/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderWriterTest.cs b/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderWriterTest.cs index f4430c5..a1e1833 100644 --- a/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderWriterTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/Io/TileCacheReaderWriterTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/SampleAreaModifications.cs b/test/DotRecast.Detour.TileCache.Test/SampleAreaModifications.cs index ecdf2fe..11ca622 100644 --- a/test/DotRecast.Detour.TileCache.Test/SampleAreaModifications.cs +++ b/test/DotRecast.Detour.TileCache.Test/SampleAreaModifications.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs b/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs index a894e41..0286580 100644 --- a/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TempObstaclesTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -26,7 +26,6 @@ using NUnit.Framework; namespace DotRecast.Detour.TileCache.Test; - public class TempObstaclesTest : AbstractTileCacheTest { [Test] @@ -43,21 +42,29 @@ public class TempObstaclesTest : AbstractTileCacheTest tc.BuildNavMeshTile(refs); } - List tiles = tc.GetNavMesh().GetTilesAt(1, 4); + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); DtMeshTile tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); + long o = tc.AddObstacle(new RcVec3f(-1.815208f, 9.998184f, -20.307983f), 1f, 2f); bool upToDate = tc.Update(); Assert.That(upToDate, Is.True); - tiles = tc.GetNavMesh().GetTilesAt(1, 4); + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); + tc.RemoveObstacle(o); upToDate = tc.Update(); Assert.That(upToDate, Is.True); - tiles = tc.GetNavMesh().GetTilesAt(1, 4); + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); @@ -77,24 +84,32 @@ public class TempObstaclesTest : AbstractTileCacheTest tc.BuildNavMeshTile(refs); } - List tiles = tc.GetNavMesh().GetTilesAt(1, 4); + const int MAX_NEIS = 32; + DtMeshTile[] tiles = new DtMeshTile[MAX_NEIS]; + int nneis = 0; + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); DtMeshTile tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); + long o = tc.AddBoxObstacle( new RcVec3f(-2.315208f, 9.998184f, -20.807983f), new RcVec3f(-1.315208f, 11.998184f, -19.807983f) ); bool upToDate = tc.Update(); Assert.That(upToDate, Is.True); - tiles = tc.GetNavMesh().GetTilesAt(1, 4); + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); + tc.RemoveObstacle(o); upToDate = tc.Update(); Assert.That(upToDate, Is.True); - tiles = tc.GetNavMesh().GetTilesAt(1, 4); + + nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS); tile = tiles[0]; Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); diff --git a/test/DotRecast.Detour.TileCache.Test/TestTileCacheMeshProcess.cs b/test/DotRecast.Detour.TileCache.Test/TestTileCacheMeshProcess.cs index affafd5..62bcaa0 100644 --- a/test/DotRecast.Detour.TileCache.Test/TestTileCacheMeshProcess.cs +++ b/test/DotRecast.Detour.TileCache.Test/TestTileCacheMeshProcess.cs @@ -1,4 +1,4 @@ -using DotRecast.Recast.Geom; +using DotRecast.Recast.Geom; namespace DotRecast.Detour.TileCache.Test; diff --git a/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs b/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs index 3864ac2..b3a2d99 100644 --- a/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs +++ b/test/DotRecast.Detour.TileCache.Test/TestTileLayerBuilder.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -73,7 +73,7 @@ public class TestTileLayerBuilder : DtTileCacheLayerBuilder RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmax = geom.GetMeshBoundsMax(); - RcCommons.CalcTileCount(bmin, bmax, CellSize, m_tileSize, m_tileSize, out tw, out th); + RcRecast.CalcTileCount(bmin, bmax, CellSize, m_tileSize, m_tileSize, out tw, out th); } public List Build(RcByteOrder order, bool cCompatibility, int threads) diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheFindPathTest.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheFindPathTest.cs index f1735bd..4f381e0 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheFindPathTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheFindPathTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 @@ -18,6 +18,7 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System; using System.Collections.Generic; using System.IO; using DotRecast.Core; @@ -29,7 +30,6 @@ using NUnit.Framework; namespace DotRecast.Detour.TileCache.Test; - public class TileCacheFindPathTest : AbstractTileCacheTest { private readonly RcVec3f start = new RcVec3f(39.44734f, 9.998177f, -0.784811f); @@ -39,7 +39,7 @@ public class TileCacheFindPathTest : AbstractTileCacheTest public TileCacheFindPathTest() { - using var msr = new MemoryStream(RcResources.Load("dungeon_all_tiles_tilecache.bin")); + using var msr = new MemoryStream(RcIO.ReadFileIfFound("dungeon_all_tiles_tilecache.bin")); using var br = new BinaryReader(msr); DtTileCache tcC = new DtTileCacheReader(DtTileCacheCompressorFactory.Shared).Read(br, 6, new TestTileCacheMeshProcess()); navmesh = tcC.GetNavMesh(); @@ -56,11 +56,11 @@ public class TileCacheFindPathTest : AbstractTileCacheTest var path = new List(); var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); - int maxStraightPath = 256; + const int maxStraightPath = 256; int options = 0; - var pathStr = new List(); - query.FindStraightPath(startPos, endPos, path, ref pathStr, maxStraightPath, options); - Assert.That(pathStr.Count, Is.EqualTo(8)); + Span pathStr = stackalloc DtStraightPath[maxStraightPath]; + query.FindStraightPath(startPos, endPos, path, path.Count, pathStr, out var npathStr, maxStraightPath, options); + Assert.That(npathStr, Is.EqualTo(8)); } } \ No newline at end of file diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs index 6ec738a..86b6247 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheNavigationTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs index f31569c..9026efe 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheTest.cs @@ -1,7 +1,7 @@ /* 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 +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Detour.TileCache.Test/TileCacheTestSetUpFixture.cs b/test/DotRecast.Detour.TileCache.Test/TileCacheTestSetUpFixture.cs index 8eb417b..1c109ca 100644 --- a/test/DotRecast.Detour.TileCache.Test/TileCacheTestSetUpFixture.cs +++ b/test/DotRecast.Detour.TileCache.Test/TileCacheTestSetUpFixture.cs @@ -1,4 +1,4 @@ -using DotRecast.Detour.TileCache.Io.Compress; +using DotRecast.Detour.TileCache.Io.Compress; using DotRecast.Detour.TileCache.Test.Io; using NUnit.Framework; diff --git a/test/DotRecast.Recast.Test/DotRecast.Recast.Test.csproj b/test/DotRecast.Recast.Test/DotRecast.Recast.Test.csproj index bd28373..d40500e 100644 --- a/test/DotRecast.Recast.Test/DotRecast.Recast.Test.csproj +++ b/test/DotRecast.Recast.Test/DotRecast.Recast.Test.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/DotRecast.Recast.Test/RecastLayersTest.cs b/test/DotRecast.Recast.Test/RecastLayersTest.cs index 1dbb009..f13689a 100644 --- a/test/DotRecast.Recast.Test/RecastLayersTest.cs +++ b/test/DotRecast.Recast.Test/RecastLayersTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -24,9 +25,6 @@ using NUnit.Framework; namespace DotRecast.Recast.Test; -using static RcConstants; - - public class RecastLayersTest { private const float m_cellSize = 0.3f; diff --git a/test/DotRecast.Recast.Test/RecastSoloMeshTest.cs b/test/DotRecast.Recast.Test/RecastSoloMeshTest.cs index 37c04c9..d187e10 100644 --- a/test/DotRecast.Recast.Test/RecastSoloMeshTest.cs +++ b/test/DotRecast.Recast.Test/RecastSoloMeshTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -25,10 +26,9 @@ using NUnit.Framework; namespace DotRecast.Recast.Test; -using static RcConstants; +using static RcRecast; using static RcAreas; - public class RecastSoloMeshTest { private const float m_cellSize = 0.3f; @@ -139,7 +139,7 @@ public class RecastSoloMeshTest // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the are type for each of the meshes and rasterize them. - int[] m_triareas = RcCommons.MarkWalkableTriangles(m_ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); + int[] m_triareas = RcRecast.MarkWalkableTriangles(m_ctx, cfg.WalkableSlopeAngle, verts, tris, ntris, cfg.WalkableAreaMod); RcRasterizations.RasterizeTriangles(m_ctx, verts, tris, m_triareas, ntris, m_solid, cfg.WalkableClimb); } diff --git a/test/DotRecast.Recast.Test/RecastTest.cs b/test/DotRecast.Recast.Test/RecastTest.cs index 3e08464..2b9fb6c 100644 --- a/test/DotRecast.Recast.Test/RecastTest.cs +++ b/test/DotRecast.Recast.Test/RecastTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 @@ -21,8 +22,7 @@ using DotRecast.Core; namespace DotRecast.Recast.Test; -using static RcConstants; - +using static RcRecast; public class RecastTest { @@ -39,19 +39,19 @@ public class RecastTest RcContext ctx = new RcContext(); { int[] areas = { 42 }; - RcCommons.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, unwalkable_tri, nt, areas); + RcRecast.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, unwalkable_tri, nt, areas); Assert.That(areas[0], Is.EqualTo(RC_NULL_AREA), "Sets area ID of unwalkable triangle to RC_NULL_AREA"); } { int[] areas = { 42 }; - RcCommons.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, walkable_tri, nt, areas); + RcRecast.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, walkable_tri, nt, areas); Assert.That(areas[0], Is.EqualTo(42), "Does not modify walkable triangle aread ID's"); } { int[] areas = { 42 }; walkableSlopeAngle = 0; - RcCommons.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, walkable_tri, nt, areas); + RcRecast.ClearUnwalkableTriangles(ctx, walkableSlopeAngle, verts, nv, walkable_tri, nt, areas); Assert.That(areas[0], Is.EqualTo(RC_NULL_AREA), "Slopes equal to the max slope are considered unwalkable."); } } -} +} \ No newline at end of file diff --git a/test/DotRecast.Recast.Test/RecastTileMeshTest.cs b/test/DotRecast.Recast.Test/RecastTileMeshTest.cs index 6fa4676..39f9d91 100644 --- a/test/DotRecast.Recast.Test/RecastTileMeshTest.cs +++ b/test/DotRecast.Recast.Test/RecastTileMeshTest.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/test/DotRecast.Recast.Test/SampleAreaModifications.cs b/test/DotRecast.Recast.Test/SampleAreaModifications.cs index 61c8ec3..1941dd0 100644 --- a/test/DotRecast.Recast.Test/SampleAreaModifications.cs +++ b/test/DotRecast.Recast.Test/SampleAreaModifications.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 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 diff --git a/tool/DotRecast.Tool.PublishToUniRecast/CsProj.cs b/tool/DotRecast.Tool.PublishToUniRecast/CsProj.cs new file mode 100644 index 0000000..bd545ce --- /dev/null +++ b/tool/DotRecast.Tool.PublishToUniRecast/CsProj.cs @@ -0,0 +1,15 @@ +namespace DotRecast.Tool.PublishToUniRecast; + +public class CsProj +{ + public readonly string RootPath; + public readonly string Name; + public readonly string TargetPath; + + public CsProj(string rootPath, string name, string targetPath) + { + RootPath = rootPath; + Name = name; + TargetPath = targetPath; + } +} \ No newline at end of file diff --git a/tool/DotRecast.Tool.PublishToUniRecast/DotRecast.Tool.PublishToUniRecast.csproj b/tool/DotRecast.Tool.PublishToUniRecast/DotRecast.Tool.PublishToUniRecast.csproj new file mode 100644 index 0000000..082a539 --- /dev/null +++ b/tool/DotRecast.Tool.PublishToUniRecast/DotRecast.Tool.PublishToUniRecast.csproj @@ -0,0 +1,9 @@ + + + + Exe + net6.0;net7.0;net8.0 + false + + + diff --git a/tool/DotRecast.Tool.PublishToUniRecast/Program.cs b/tool/DotRecast.Tool.PublishToUniRecast/Program.cs new file mode 100644 index 0000000..e5300e0 --- /dev/null +++ b/tool/DotRecast.Tool.PublishToUniRecast/Program.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; + +namespace DotRecast.Tool.PublishToUniRecast; + +public static class Program +{ + public static void Main(string[] args) + { + var source = SearchDirectory("DotRecast"); + var destination = SearchDirectory("UniRecast"); + + if (!Directory.Exists(source)) + { + throw new Exception("not found source directory"); + } + + if (!Directory.Exists(destination)) + { + throw new Exception("not found destination directory"); + } + + var ignorePaths = ImmutableArray.Create("bin", "obj"); + var projs = ImmutableArray.Create( + // src + new CsProj("src", "DotRecast.Core", "Runtime"), + new CsProj("src", "DotRecast.Recast", "Runtime"), + new CsProj("src", "DotRecast.Detour", "Runtime"), + new CsProj("src", "DotRecast.Detour.Crowd", "Runtime"), + new CsProj("src", "DotRecast.Detour.Dynamic", "Runtime"), + new CsProj("src", "DotRecast.Detour.Extras", "Runtime"), + new CsProj("src", "DotRecast.Detour.TileCache", "Runtime"), + new CsProj("src", "DotRecast.Recast.Toolset", "Runtime") + ); + + + foreach (var proj in projs) + { + var sourcePath = Path.Combine(source, proj.RootPath, $"{proj.Name}"); + var destPath = Path.Combine(destination, $"{proj.TargetPath}", $"{proj.Name}"); + + SyncFiles(sourcePath, destPath, ignorePaths, "*.cs"); + } + + // // 몇몇 필요한 리소스 복사 하기 + // string destResourcePath = destDotRecast + "/resources"; + // if (!Directory.Exists(destResourcePath)) + // { + // Directory.CreateDirectory(destResourcePath); + // } + + // string sourceResourcePath = Path.Combine(dotRecastPath, "resources/nav_test.obj"); + // File.Copy(sourceResourcePath, destResourcePath + "/nav_test.obj", true); + } + + public static string SearchPath(string searchPath, int depth, out bool isDir) + { + isDir = false; + + for (int i = 0; i < depth; ++i) + { + var relativePath = string.Join("", Enumerable.Range(0, i).Select(x => "../")); + var searchingPath = Path.Combine(relativePath, searchPath); + var fullSearchingPath = Path.GetFullPath(searchingPath); + + if (File.Exists(fullSearchingPath)) + { + return fullSearchingPath; + } + + if (Directory.Exists(fullSearchingPath)) + { + isDir = true; + return fullSearchingPath; + } + } + + return string.Empty; + } + + // only directory + public static string SearchDirectory(string dirname, int depth = 10) + { + var searchingPath = SearchPath(dirname, depth, out var isDir); + if (isDir) + { + return searchingPath; + } + + var path = Path.GetDirectoryName(searchingPath) ?? string.Empty; + return path; + } + + public static string SearchFile(string filename, int depth = 10) + { + var searchingPath = SearchPath(filename, depth, out var isDir); + if (!isDir) + { + return searchingPath; + } + + return string.Empty; + } + + private static void SyncFiles(string srcRootPath, string dstRootPath, IList ignoreFolders, string searchPattern = "*") + { + // 끝에서부터 이그노어 폴더일 경우 패스 + var destLastFolderName = Path.GetFileName(dstRootPath); + if (ignoreFolders.Any(x => x == destLastFolderName)) + return; + + if (!Directory.Exists(dstRootPath)) + Directory.CreateDirectory(dstRootPath); + + // 소스파일 추출 + var sourceFiles = Directory.GetFiles(srcRootPath, searchPattern).ToList(); + var sourceFolders = Directory.GetDirectories(srcRootPath) + .Select(x => new DirectoryInfo(x)) + .ToList(); + + // 대상 파일 추출 + var destinationFiles = Directory.GetFiles(dstRootPath, searchPattern).ToList(); + var destinationFolders = Directory.GetDirectories(dstRootPath) + .Select(x => new DirectoryInfo(x)) + .ToList(); + + // 대상에 파일이 있는데, 소스에 없을 경우, 대상 파일을 삭제 한다. + foreach (var destinationFile in destinationFiles) + { + var destName = Path.GetFileName(destinationFile); + var found = sourceFiles.Any(x => Path.GetFileName(x) == destName); + if (found) + continue; + + File.Delete(destinationFile); + Console.WriteLine($"delete file - {destinationFile}"); + } + + // 대상에 폴더가 있는데, 소스에 없을 경우, 대상 폴더를 삭제 한다. + foreach (var destinationFolder in destinationFolders) + { + var found = sourceFolders.Any(sourceFolder => sourceFolder.Name == destinationFolder.Name); + if (found) + continue; + + Directory.Delete(destinationFolder.FullName, true); + Console.WriteLine($"delete folder - {destinationFolder.FullName}"); + } + + // 소스 파일을 복사 한다. + foreach (var sourceFile in sourceFiles) + { + var name = Path.GetFileName(sourceFile); + var dest = Path.Combine(dstRootPath, name); + File.Copy(sourceFile, dest, true); + Console.WriteLine($"copy - {sourceFile} => {dest}"); + } + + // 대상 폴더를 복사 한다 + foreach (var sourceFolder in sourceFolders) + { + var dest = Path.Combine(dstRootPath, sourceFolder.Name); + SyncFiles(sourceFolder.FullName, dest, ignoreFolders, searchPattern); + } + } +} \ No newline at end of file