Compare commits

..

150 Commits

Author SHA1 Message Date
wrenge db0717a77e package version bump 2024-11-11 11:37:12 +03:00
wrenge 724be6f3b2 Backport 20a86b5bae 2024-11-11 11:34:47 +03:00
wrenge 0f28fa26ec Merge branch 'main'
# Conflicts:
#	src/DotRecast.Detour.Crowd/DtObstacleAvoidanceQuery.cs
#	src/DotRecast.Detour/DtBVNode.cs
#	src/DotRecast.Detour/DtNavMesh.cs
#	src/DotRecast.Detour/DtNavMeshQuery.cs
#	src/DotRecast.Detour/DtPoly.cs
#	src/DotRecast.Detour/DtPolyDetail.cs
#	src/DotRecast.Recast.Toolset/RcNavMeshBuildSettings.cs
#	test/DotRecast.Core.Test/RcRentedArrayTest.cs
2024-11-11 11:09:29 +03:00
ikpil fba594724c Silk.NET" 2.22.0 2024-11-07 00:50:47 +09:00
ikpil 193fe6dce5 fix publish error 2024-10-28 23:28:35 +09:00
ikpil 43446d4443 add publish to unirecast 2024-10-28 22:36:20 +09:00
ikpil 4ec36fb8b4 Serilog.Sinks.Async 2.1.0 2024-10-25 14:03:51 +09:00
ikpil 5de6e06a94 Serilog 4.1.0 2024-10-23 08:54:31 +09:00
ikpil 1bf2ff48f2 Changed bmin/bmax from float[] to RcVec2f for improved memory efficiency and readability 2024-10-13 16:41:25 +09:00
ikpil ea437ef020 Changed bmin/bmax from int[] to RcVec3i for improved memory efficiency 2024-10-13 16:30:52 +09:00
ikpil ecc02f12e4 install dotnet all version for unittest 2024-10-13 15:04:37 +09:00
ikpil a225e32a5a Serilog.Settings.Configuration 8.0.4 2024-10-10 13:39:58 +09:00
ikpil e9ca704ab7 update Serilog.Settings.Configuration 8.0.3 2024-10-08 13:44:36 +09:00
ikpil 62f9cfe034 Support for saving and loading dynamic nav meshes @ppiastucki
[Upstream] from recast4j
506b503 - chore: Support for saving and loading dynamic nav meshes (fixes #200) (#209)
2024-10-01 23:28:50 +09:00
ikpil 36795dc909 Serilog 4.0.2 2024-09-29 22:53:10 +09:00
ikpil 85e2484505 fix: Support non-tiled dynamic nav meshes (@ppiastucki)
[Upstream] from recast4j
- fe071aa - fix: Support non-tiled dynamic nav meshes (#205)
2024-09-21 03:13:51 +09:00
ikpil 2d0e2f8525 Fix dynamic mesh bounds calculation #77
recast4j
- https://github.com/recast4j/recast4j/issues/203
- https://github.com/recast4j/recast4j/pull/204
2024-09-21 02:54:01 +09:00
ikpil e9a05843da bugfix - checkbox flags 2024-09-12 01:35:21 +09:00
ikpil 5735c98c79 Moq 4.20.71 2024-09-07 23:22:41 +09:00
ikpil f18bedb02d force utf-8 2024-09-05 22:57:28 +09:00
ikpil 1179f87d02 Microsoft.NET.Test.Sdk 17.11.1 2024-09-05 22:37:33 +09:00
ikpil a9e4ca377d upgrade Moq 4.20.71 2024-09-05 22:34:22 +09:00
ikpil 1eb65ca856 NUnit 4.2.1 2024-09-01 22:22:34 +09:00
ikpil e73ec9acda runtime comment 2024-08-24 22:25:13 +09:00
ikpil 4a19d597c1 update Nunit 4.2.1 2024-08-24 20:32:38 +09:00
ikpil 201401b54f update Microsoft.NET.Test.Sdk 17.11.0 2024-08-21 13:58:04 +09:00
ikpil e6bea60db3 NUnit.Analyzers 4.3.0 2024-08-12 07:55:52 +09:00
ikpil 887294af1b upgrade BenchmarkDotNet 0.14.0 2024-08-08 22:54:03 +09:00
Sarofc 997d3f1a9b draw agent option 2024-08-08 22:53:27 +09:00
ikpil 13be6d5bd8 benchmark 2024-08-04 18:55:17 +09:00
ikpil cd7c668caf changed PriorityQueueBenchmarks 2024-08-04 18:35:25 +09:00
ikpil 153b523ea7 changelog 2024-08-04 18:15:16 +09:00
ikpil 89214accfb added RcBinaryMinHeapTest 2024-08-04 18:03:31 +09:00
ikpil 8a655528c3 benchmark 2024-08-03 14:36:46 +09:00
Sarofc c036501879 opt UpdateMoveRequest UpdateTopologyOptimization
# Conflicts:
#	src/DotRecast.Detour.Crowd/DtCrowd.cs

Benchmark

b3

b4
2024-08-03 13:24:42 +09:00
Sarofc 5aeb1e465c add benchmark 2024-08-03 12:13:45 +09:00
Sarofc ab04256cdf Update ModernOpenGLDraw.cs
(cherry picked from commit 6fb44cc3ce47be2c724d6093ef4b3168aacf9480)
2024-07-26 22:27:24 +09:00
ikpil 7ffda46c2f NUnit3TestAdapter 4.6.0 2024-07-26 22:10:17 +09:00
ikpil 8ba8c04895 update serilog 4.0.1 2024-07-25 23:16:19 +09:00
ikpil bc7818a1c5 [Upstream] fix: Fix raycast shortcuts (@Sarofc)
Raycast is performed in 2d and it might report reaching the given position even if the Y coordinate is different than the target. Therefore, it is necessary to check what poly is actually hit by raycast before taking a shortcut.

- https://github.com/recast4j/recast4j/issues/196
- https://github.com/recast4j/recast4j/pull/197
- https://github.com/ikpil/DotRecast/issues/72
2024-07-18 20:06:36 +09:00
ikpil a87f34e738 update comment 2024-07-17 23:48:11 +09:00
ikpil cf7aec90ee Changed DtNavMeshQuery.GetPolyWallSegments() to use Span<T> for enhanced performance, memory efficiency. 2024-07-16 22:19:41 +09:00
ikpil 84419b1d52 Changed memory handling to use stackalloc in DtNavMeshQuery.GetPolyWallSegments for reducing SOH
Refactored to use stack-allocated Span<DtSegInterval> instead of dynamically allocating List<DtSegInterval>. This reduces potential heap allocations and improves performance by efficiently managing memory within a fixed size context.
2024-07-14 23:51:04 +09:00
ikpil c562f8f6a1 Changed new RcVec3f[3] to stackalloc RcVec3f[3] in DtNavMesh.GetPolyHeight() to reduce heap allocation. 2024-07-14 00:09:08 +09:00
ikpil eccce01cff reorder variable declarations 2024-07-13 23:21:56 +09:00
ikpil 990dbcf97f Changed data structure of 'neis' from List<byte> to byte[] for optimized memory usage and improved access speed in `DtLayerMonotoneRegion` 2024-07-12 23:55:35 +09:00
ikpil 00950b1210 update Serilog.Settings.Configuration 8.0.2 2024-07-12 23:18:19 +09:00
ikpil c5820af20b update comment 2024-07-12 01:54:38 +09:00
ikpil afe93d084e update comment 2024-07-12 01:46:53 +09:00
ikpil c1e7b84efa update readme 2024-07-09 00:11:02 +09:00
ikpil fbce7f40f4 2024.3.1 2024-07-09 00:10:38 +09:00
ikpil b34b17e89c update comment in DtCrowdAgentParams 2024-07-08 14:01:28 +09:00
ikpil 31b7eaf9a3 update comment 2024-07-08 13:55:56 +09:00
ikpil 9ebaa3fc65 Changed to limit neighbor search to a maximum count and use array for memory efficiency in `DtCrowd.AddNeighbour()` 2024-07-07 14:58:38 +09:00
ikpil 76e5ade4d1 update comment in DtCrowdNeighbour 2024-07-07 14:04:37 +09:00
ikpil 1894a56889 Changed to efficiently stack nearby DtCrowdAgents in `DtCrowd.GetNeighbours()` 2024-07-07 12:55:51 +09:00
ikpil 828b9644cc Changed agents management from list to dictionary in `Crowd` 2024-07-07 12:38:09 +09:00
ikpil ab2c520076 Changed `new float[]` to `stackalloc float[]` in `DtConvexConvexIntersections.Intersect()` 2024-07-05 00:15:03 +09:00
ikpil 4743ba68f9 Changed static readonly to const 2024-07-04 23:34:25 +09:00
ikpil fa2ff6f133 comment 2024-07-04 01:14:31 +09:00
ikpil 355a08e7ef int to byte 2024-07-03 23:08:31 +09:00
ikpil a282a80356 Changed vertCount and triCount to byte in DtPolyDetail 2024-07-02 13:47:31 +09:00
ikpil ba6815769a Changed to use Span<byte> and stackalloc for improved performance and memory management in `RcLayers.BuildHeightfieldLayers()` 2024-06-27 14:04:30 +09:00
ikpil 27751522f9 comment 2024-06-26 23:48:24 +09:00
ikpil ee48892223 Optimize stack handling by replacing List with a fixed-size array and manual index management in RcLayers 2024-06-26 23:46:40 +09:00
ikpil d472d71795 Added RcSpans.Fill<T>() 2024-06-25 00:45:08 +09:00
ikpil bdb9463d88 Changed RcLayerRegion.layerId to byte 2024-06-25 00:30:51 +09:00
ikpil 35f5c63d77 DtStraightPathFlags int to byte 2024-06-25 00:17:07 +09:00
ikpil e5d5867c56 rename RcVecUtils to RcVec 2024-06-25 00:02:59 +09:00
ikpil 72b7cfd6db update Serilog.Sinks.File 6.0.0 2024-06-24 22:36:05 +09:00
ikpil 267e15fd4c refactor DtTileCacheObstacle class 2024-06-21 00:07:58 +09:00
ikpil 35ef64d9b4 reorder the member variables of DtTileCacheObstacle 2024-06-20 23:52:37 +09:00
ikpil 0092761742 Changed to consolidate vector-related functions into one place. 2024-06-19 13:46:04 +09:00
ikpil 5911510544 fix compile error 2024-06-17 23:15:43 +09:00
ikpil f54b586d75 Serilog.Enrichers.Thread 4.0.0 2024-06-17 14:09:46 +09:00
ikpil 5827a43dd8 Changed `RcChunkyTriMesh` to separate the function and variable. 2024-06-12 00:41:39 +09:00
ikpil 650849b11b Changed 'reg', 'area' arrays to byte arrays for uniformity and efficiency in DtTileCacheContour 2024-06-12 00:03:59 +09:00
ikpil b88b6096f6 Fixed where the dynamic voxel save file browser doesn't appear In Demo 2024-06-11 22:23:22 +09:00
ikpil 65c572a4c2 Changed 'heights', 'areas', 'cons', and 'regs' arrays to byte arrays for uniformity and efficiency in DtTileCacheLayer 2024-06-11 00:44:20 +09:00
ikpil 3417ecae36 Serilog.Sinks.Console 6.0.0 2024-06-10 14:09:06 +09:00
ikpil 320c7b4c65 Removed RcVecUtils.Dot2D(this RcVec3f @this, Span<float> v, int vi) 2024-06-08 20:56:55 +09:00
ikpil fd03f0f12f Removed RcVecUtils.Create(float[] values) 2024-06-08 14:24:47 +09:00
ikpil ed7173dd51 Removed RcVecUtils.Min(), RcVecUtils.Max() 2024-06-08 14:21:38 +09:00
ikpil face8eb48e Changed to reuse samples and edges list in BuildPolyDetail() 2024-06-08 12:08:31 +09:00
ikpil 759e335961 Removed RcVecUtils.Subtract(float[] verts, int i, int j) 2024-06-07 22:46:08 +09:00
ikpil 8ad34dc0d8 Removed RcMeshDetails.VdistSq2(float[], float[]) 2024-06-07 22:36:46 +09:00
ikpil ebab0a11f3 Removed RcVecUtils.Subtract(RcVec3f i, float[] verts, int j) 2024-06-07 21:44:59 +09:00
ikpil b18845a749 Removed RcVecUtils.Scale() 2024-06-07 21:31:11 +09:00
ikpil 0f20db44bb Serilog.Sinks.Async 1.5.0 -> 2.0.0 2024-06-07 21:24:11 +09:00
ikpil 099636cd7c Removed RcVecUtils.Dot() 2024-06-04 00:34:07 +09:00
ikpil f8ae77a192 Release 2024.2.3 2024-06-03 23:33:14 +09:00
ikpil 01b7b4add9 pgrade Serilog 4.0.0 for DotRecast.Recast.Demo 2024-06-01 23:27:22 +09:00
ikpil 2a45ec021c added DT_NODE_PARENT_BITS 2024-05-31 23:25:35 +09:00
ikpil d94408f7a1 Changed class `Trajectory` to interface `ITrajectory` 2024-05-31 22:36:57 +09:00
ikpil e9a5512bb2 public class EdgeSamplerFactory 2024-05-30 22:55:47 +09:00
ikpil 17ecdb1cc7 added RcMath.Lerp() 2024-05-30 22:53:01 +09:00
ikpil 2acbdf8c53 changed RecastTestMeshBuilder to TestMeshDataFactory 2024-05-28 00:06:21 +09:00
ikpil e9c8b3eddf added RcIO Tests 2024-05-27 00:01:14 +09:00
ikpil 104b5b02b2 cleanup TestMeshDataFactory 2024-05-25 23:01:16 +09:00
ikpil 34d2ef639a update `DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com` 2024-05-25 22:42:57 +09:00
ikpil e6f515f08a update comment 2024-05-25 21:37:04 +09:00
ikpil 7664ae9f3d code cleanup in RcResources 2024-05-25 09:36:00 +09:00
ikpil aeefed7fbb add DT_VERTS_PER_POLYGON 2024-05-25 00:34:24 +09:00
ikpil 1b4f5af155 update comment 2024-05-24 22:14:57 +09:00
ikpil 2f50768909 upgrade Microsoft.NET.Test.Sdk 17.10.0 2024-05-23 22:42:17 +09:00
ikpil 886be3712f Changed `DtWriter` to a static class and renamed it to `RcIO` 2024-05-23 22:39:03 +09:00
ikpil 99224251dc Changed `List<DtStraightPath>` to `Span<DtStraightPath>` for enhanced memory efficiency 2024-05-23 21:19:16 +09:00
ikpil 5b6905bd8f Changed `MAX_STEER_POINTS` from class constant to local. 2024-05-22 23:49:15 +09:00
ikpil c7f03d00ff Changed `Dictionary<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage 2024-05-22 01:34:11 +09:00
ikpil 9a03ade6c9 Changed `DtTileCacheBuilder` to a static class 2024-05-21 00:12:40 +09:00
ikpil 8f8db51542 Changed `DtTileCacheBuilder` to a static class 2024-05-21 00:10:24 +09:00
ikpil ec9ebe28b9 long[] -> Span<long> 2024-05-20 00:16:36 +09:00
ikpil c9a54d4b4e Added `DtCollectPolysQuery` and `FindCollectPolyTest` 2024-05-19 09:16:25 +09:00
ikpil 3808c13876 Changed `IDtPolyQuery` interface to make `Process()` more versatile 2024-05-18 14:50:22 +09:00
ikpil 0ce8ffba32 release 2024.2.2 2024-05-18 09:26:07 +09:00
ikpil 3b5e85eeb6 added Tile hash lookup size in DtNavMesh 2024-05-18 00:10:58 +09:00
ikpil 72d042b1ea update CHANGELOG.md 2024-05-17 23:46:48 +09:00
ikpil 7212afaac6 needed by Dotrecast 2024-05-17 23:46:48 +09:00
ikpil 886afd20cd reduced memory usage of DtLink. 2024-05-17 23:46:48 +09:00
ikpil c3208f7968 changed polyLinks to firstLinks 2024-05-17 23:46:48 +09:00
ikpil 4543df0b90 update comment 2024-05-15 23:06:52 +09:00
ikpil a649081a5b update comment DtMeshTile class 2024-05-14 23:46:54 +09:00
ikpil 4e7afa64c6 update comment in DtNavMeshParams 2024-05-14 23:17:39 +09:00
ikpil 47be4eca70 added DtNavMesh.Init() 2024-05-12 22:05:19 +09:00
ikpil 40306a5302 rename RcRecast, DtDetour 2024-05-12 01:11:03 +09:00
ikpil 3f750ba499 update comment, class member name 2024-05-12 00:36:18 +09:00
ikpil 61e7b7a443 update RandomPointInConvexPoly() 2024-05-12 00:25:53 +09:00
ikpil dfbd1b2cae update comment 2024-05-12 00:08:18 +09:00
ikpil 1bf0a88492 update comment in DtPoly class 2024-05-11 23:58:01 +09:00
ikpil e926c23195 SOH #41 2024-05-09 00:34:22 +09:00
ikpil 59849e1dac add `int nvisited` in DtPathUtils.functions 2024-05-09 00:00:53 +09:00
ikpil deb02778cc rename corridor test 2024-05-08 23:50:12 +09:00
ikpil cfdcc1336c preparatory work to resolve the SOH issue during path merging. 2024-05-08 00:25:32 +09:00
ikpil 19e358bdfc m_npath instead of m_path.Count 2024-05-07 23:56:15 +09:00
ikpil fc673b2c25 test2 2024-05-06 15:45:24 +09:00
ikpil 741200b559 added int npath param in FixupShortcuts() 2024-05-06 14:37:43 +09:00
ikpil b165a3afed SOH issue #41
- https://github.com/ikpil/DotRecast/issues/41
2024-05-06 14:34:46 +09:00
ikpil 884789a934 added RcSpan unit test 2024-05-05 13:11:12 +09:00
ikpil 5b9adf73e2 release 2024.2.1 2024-05-04 10:02:16 +09:00
ikpil 3c43623769 SOH in DrawPolyBoundaries 2024-05-04 09:34:22 +09:00
ikpil 08c8db2d19 SOH 2024-05-03 22:44:04 +09:00
ikpil 4acb0a163a SOH for Gizmo 2024-05-03 22:09:37 +09:00
ikpil a5a101c0c2 SOH allocation issues
https://github.com/ikpil/DotRecast/issues/41
2024-05-03 22:00:51 +09:00
ikpil bef346a8cb SOH
https://github.com/ikpil/DotRecast/issues/41
2024-05-03 00:31:10 +09:00
ikpil 9e9a3f0dc2 SOH
https://github.com/ikpil/DotRecast/issues/41
2024-05-03 00:19:12 +09:00
ikpil daf552b4b7 SOH - Span<float> verts = stackalloc float[m_maxVertPerPoly * 3];
https://github.com/ikpil/DotRecast/issues/41
2024-05-02 23:12:33 +09:00
ikpil 1e0ef4f5cc SOH-2 - Span<float> pat = stackalloc float[(DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2];
- https://github.com/ikpil/DotRecast/issues/41
2024-05-01 13:03:34 +09:00
ikpil a84d66195e added RcRentedArray test 2024-05-01 12:54:57 +09:00
ikpil 29fab9f5b2 soh-1 2024-04-30 00:13:52 +09:00
ikpil f81975d97a benchmark 2024-04-30 00:13:42 +09:00
455 changed files with 5700 additions and 3801 deletions

View File

@ -39,7 +39,10 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: ${{ matrix.dotnet-version }}.x dotnet-version: |
6
7
8
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore

View File

@ -6,12 +6,122 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] - yyyy-mm-dd ## [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<byte> 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<T> 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<byte> 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<float> 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<int, List<DtMeshTile>>` to `DtMeshTile[]` to optimize memory usage
- Changed `MAX_STEER_POINTS` from class constant to local.
- Changed `List<DtStraightPath>` to `Span<DtStraightPath>` 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
- Added RcCircularBuffer<T> [@ikpil](https://github.com/ikpil) - Added RcCircularBuffer<T> [@ikpil](https://github.com/ikpil)
- Added struct DtCrowdScopedTimer to avoid allocations in scoped timer calls. [@wrenge](https://github.com/wrenge) - 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 struct RcScopedTimer to avoid allocations in RcContext scoped timer [@ikpil](https://github.com/ikpil)
- Added RcSpans [@ikpil](https://github.com/ikpil)
### Fixed ### 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
- Changed DtPathCorridor.Init(int maxPath) function to allow setting the maximum path [@ikpil](https://github.com/ikpil) - Changed DtPathCorridor.Init(int maxPath) function to allow setting the maximum path [@ikpil](https://github.com/ikpil)

View File

@ -39,6 +39,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.Extras.Tes
EndProject 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}" 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 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{10395C8F-DFBD-4263-8A20-EA3500A6E55A}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{FFE40BBF-843B-41FA-8504-F4ABD166762E} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {FFE40BBF-843B-41FA-8504-F4ABD166762E} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{38933A87-4568-40A5-A3DA-E2445E8C2B99} = {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} {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} {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} {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} {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} {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} {023E1E6A-4895-4573-89AE-3D5D8E0B39C8} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD}
{DF987948-8C23-4337-AF83-D87D6407518D} = {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} {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 EndGlobalSection
EndGlobal EndGlobal

View File

@ -1,28 +1,22 @@
<h1 align="center">DotRecast</h1> # DotRecast
<p align="center">
<i>DotRecast is C# Recast & Detour, a port of <a href="https://github.com/recastnavigation/recastnavigation">recastnavigation</a> and <a href="https://github.com/ppiastucki/recast4j">recast4j</a> to the C# language.</i> *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.*
</p> *If you'd like to support the project, we'd appreciate starring(⭐) our repos on Github for more visibility.*
<p align="center">
<i>If you'd like to support the project, we'd appreciate starring(⭐) our repos on Github for more visibility.</i>
</p>
--- ---
<p align="center"> ![GitHub License](https://img.shields.io/github/license/ikpil/DotRecast?style=for-the-badge)
<img alt="![GitHub License]" src="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)
<img alt="Languages" src="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)
<img alt="GitHub repo size" src="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)
<a href="https://github.com/ikpil/DotRecast"><img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ikpil/DotRecast?style=for-the-badge&logo=github"></a> [![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)
<a href="https://github.com/ikpil/DotRecast/actions/workflows/dotnet.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/dotnet.yml?style=for-the-badge&logo=github"></a> [![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)
<a href="https://github.com/ikpil/DotRecast/actions/workflows/codeql.yml"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/codeql.yml?style=for-the-badge&logo=github&label=CODEQL"></a> [![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)
<a href="https://github.com/ikpil/DotRecast/commits"><img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/ikpil/DotRecast?style=for-the-badge&logo=github"></a> [![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)
<a href="https://github.com/ikpil/DotRecast/issues"><img alt="GitHub issues" src="https://img.shields.io/github/issues-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=44cc11"></a> [![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)
<a href="https://github.com/ikpil/DotRecast/issues"><img alt="GitHub closed issues" src="https://img.shields.io/github/issues-closed-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=a371f7"></a> [![NuGet Version](https://img.shields.io/nuget/vpre/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core)
<a href="https://www.nuget.org/packages/DotRecast.Core"><img alt="NuGet Version" src="https://img.shields.io/nuget/vpre/DotRecast.Core?style=for-the-badge&logo=nuget"></a> [![NuGet Downloads](https://img.shields.io/nuget/dt/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core)
<a href="https://www.nuget.org/packages/DotRecast.Core"><img alt="NuGet Downloads" src="https://img.shields.io/nuget/dt/DotRecast.Core?style=for-the-badge&logo=nuget"></a> [![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)
<a href="https://visitorbadge.io/status?path=ikpil%2FDotRecast"><img alt="Visitors" src="https://api.visitorbadge.io/api/daily?path=ikpil%2FDotRecast&countColor=%23263759"></a>
<a href="https://github.com/sponsors/ikpil"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/ikpil?style=for-the-badge&logo=GitHub-Sponsors&link=https%3A%2F%2Fgithub.com%2Fsponsors%2Fikpil"></a>
</p>
--- ---

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Security; using System.Net.Security;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections
{
public sealed class RcBinaryMinHeap<T>
{
private readonly List<T> _items;
private readonly Comparison<T> _comparision;
public int Count => _items.Count;
public int Capacity => _items.Capacity;
public RcBinaryMinHeap(Comparison<T> comparision)
{
_items = new List<T>();
_comparision = comparision;
}
public RcBinaryMinHeap(int capacity, Comparison<T> comparison) : this(comparison)
{
if (capacity <= 0)
throw new ArgumentException("capacity must greater than zero");
_items = new List<T>(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<T> ToList()
{
return new List<T>(_items);
}
}
}

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections
{ {
public readonly partial struct RcImmutableArray<T> public readonly partial struct RcImmutableArray<T>
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Collections namespace DotRecast.Core.Collections

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Core namespace DotRecast.Core
{ {
public interface IRcRand public interface IRcRand
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Numerics namespace DotRecast.Core.Numerics

View File

@ -1,20 +1,15 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core.Numerics namespace DotRecast.Core.Numerics
{ {
public static class RcVecUtils public static class RcVec
{ {
public const float EPSILON = 1e-6f; public const float EPSILON = 1e-6f;
public static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RcVec3f Create(float[] values) public static RcVec3f Create(Span<float> values, int n)
{
return Create(values, 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RcVec3f Create(float[] values, int n)
{ {
return new RcVec3f(values[n + 0], values[n + 1], values[n + 2]); return new RcVec3f(values[n + 0], values[n + 1], values[n + 2]);
} }
@ -42,10 +37,73 @@ namespace DotRecast.Core.Numerics
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] /// Performs a 'sloppy' colocation check of the specified points.
public static RcVec3f Scale(this RcVec3f v, float scale) /// @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) /// 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; @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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Cross(float[] dest, float[] v1, float[] v2) public static void Cross(float[] dest, float[] v1, float[] v2)
{ {
@ -119,19 +137,11 @@ namespace DotRecast.Core.Numerics
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Dot(float[] v1, float[] v2) public static void Copy(Span<float> @out, int n, Span<float> @in, int m)
{ {
return v1[0] * v2[0] + @out[n + 0] = @in[m + 0];
v1[1] * v2[1] + @out[n + 1] = @in[m + 1];
v1[2] * v2[2]; @out[n + 2] = @in[m + 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;
} }
/// Returns the distance between two points. /// Returns the distance between two points.
@ -167,26 +177,6 @@ namespace DotRecast.Core.Numerics
return v; 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. /// Derives the distance between the specified points on the xz-plane.
/// @param[in] v1 A point. [(x, y, z)] /// @param[in] v1 A point. [(x, y, z)]
/// @param[in] v2 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); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Dist2DSqr(RcVec3f v1, RcVec3f v2) public static float Dist2DSqr(RcVec3f v1, RcVec3f v2)
{ {
@ -262,7 +256,7 @@ namespace DotRecast.Core.Numerics
/// @param[in] v2 The destination vector. /// @param[in] v2 The destination vector.
/// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0] /// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static RcVec3f Lerp(float[] verts, int v1, int v2, float t) public static RcVec3f Lerp(Span<float> verts, int v1, int v2, float t)
{ {
return new RcVec3f( return new RcVec3f(
verts[v1 + 0] + (verts[v2 + 0] - verts[v1 + 0]) * t, verts[v1 + 0] + (verts[v2 + 0] - verts[v1 + 0]) * t,

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -49,6 +50,19 @@ namespace DotRecast.Core.Numerics
Z = f; Z = f;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RcVec3f(ReadOnlySpan<float> values)
{
if (values.Length < 3)
{
RcThrowHelper.ThrowArgumentOutOfRangeException(nameof(values));
}
X = values[0];
Y = values[1];
Z = values[2];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly float Length() public readonly float Length()

View File

@ -0,0 +1,9 @@
namespace DotRecast.Core.Numerics
{
public struct RcVec3i
{
public int X;
public int Y;
public int Z;
}
}

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core namespace DotRecast.Core

View File

@ -1,4 +1,4 @@
using System.Threading; using System.Threading;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System.Threading; using System.Threading;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System.Threading; using System.Threading;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Threading; using System.Threading;
namespace DotRecast.Core namespace DotRecast.Core

View File

@ -1,4 +1,4 @@
namespace DotRecast.Core namespace DotRecast.Core
{ {
public enum RcByteOrder public enum RcByteOrder
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
namespace DotRecast.Core namespace DotRecast.Core

View File

@ -1,4 +1,4 @@
namespace DotRecast.Core namespace DotRecast.Core
{ {
public class RcEdge public class RcEdge
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
namespace DotRecast.Core namespace DotRecast.Core

View File

@ -1,4 +1,4 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core namespace DotRecast.Core
{ {

165
src/DotRecast.Core/RcIO.cs Normal file
View File

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

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -29,5 +29,11 @@ namespace DotRecast.Core
{ {
return f * f; return f * f;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Lerp(float value1, float value2, float amount)
{
return (value1 * (1.0f - amount)) + (value2 * amount);
}
} }
} }

View File

@ -1,6 +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 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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View File

@ -1,4 +1,4 @@
using System; using System;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

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

View File

@ -1,4 +1,4 @@
using System; using System;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace DotRecast.Core namespace DotRecast.Core
@ -6,17 +6,31 @@ namespace DotRecast.Core
public static class RcSpans public static class RcSpans
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(Span<T> source, Span<T> destination) public static void Copy<T>(Span<T> src, Span<T> dst)
{ {
Copy(source, 0, destination, 0, source.Length); src.CopyTo(dst);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(Span<T> source, int sourceIdx, Span<T> destination, int destinationIdx, int length) public static void Copy<T>(Span<T> src, int srcIdx, Span<T> dst, int dstIdx, int length)
{ {
var src = source.Slice(sourceIdx, length); var slicedSrc = src.Slice(srcIdx, length);
var dst = destination.Slice(destinationIdx); var slicedDst = dst.Slice(dstIdx);
src.CopyTo(dst); slicedSrc.CopyTo(slicedDst);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Move<T>(Span<T> 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 Fill<T>(Span<T> span, T value, int start, int count)
{
span.Slice(start, count).Fill(value);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
namespace DotRecast.Core namespace DotRecast.Core
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using DotRecast.Core.Collections; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void StackOverflow() public static void StackOverflow()
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Core namespace DotRecast.Core
{ {
/// Recast performance timer categories. /// Recast performance timer categories.
/// @see rcContext /// @see rcContext

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Diagnostics;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Buffers; using DotRecast.Core.Buffers;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
@ -121,8 +121,9 @@ namespace DotRecast.Detour.Crowd
/// @ingroup crowd /// @ingroup crowd
public class DtCrowd public class DtCrowd
{ {
private readonly RcAtomicInteger _agentId = new RcAtomicInteger(); private readonly RcAtomicInteger _agentIdx;
private readonly List<DtCrowdAgent> _agents; private readonly Dictionary<int, DtCrowdAgent> _agents;
private readonly List<DtCrowdAgent> _activeAgents;
private readonly DtPathQueue _pathQ; private readonly DtPathQueue _pathQ;
@ -171,7 +172,9 @@ namespace DotRecast.Detour.Crowd
// Allocate temp buffer for merging paths. // Allocate temp buffer for merging paths.
_maxPathResult = DtCrowdConst.MAX_PATH_RESULT; _maxPathResult = DtCrowdConst.MAX_PATH_RESULT;
_pathQ = new DtPathQueue(config); _pathQ = new DtPathQueue(config);
_agents = new List<DtCrowdAgent>(); _agentIdx = new RcAtomicInteger(0);
_agents = new Dictionary<int, DtCrowdAgent>();
_activeAgents = new List<DtCrowdAgent>();
// The navQuery is mostly used for local searches, no need for large node pool. // The navQuery is mostly used for local searches, no need for large node pool.
SetNavMesh(nav); 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. /// @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) public DtCrowdAgent AddAgent(RcVec3f pos, DtCrowdAgentParams option)
{ {
int idx = _agentId.GetAndIncrement(); int idx = _agentIdx.GetAndIncrement();
DtCrowdAgent ag = new DtCrowdAgent(idx); DtCrowdAgent ag = new DtCrowdAgent(idx);
ag.corridor.Init(_maxPathResult); ag.corridor.Init(_maxPathResult);
_agents.Add(ag); AddAgent(ag);
UpdateAgentParameters(ag, option); UpdateAgentParameters(ag, option);
// Find nearest position on navmesh and place the agent there. // Find nearest position on navmesh and place the agent there.
@ -257,6 +259,7 @@ namespace DotRecast.Detour.Crowd
ag.topologyOptTime = 0; ag.topologyOptTime = 0;
ag.targetReplanTime = 0; ag.targetReplanTime = 0;
ag.nneis = 0;
ag.dvel = RcVec3f.Zero; ag.dvel = RcVec3f.Zero;
ag.nvel = RcVec3f.Zero; ag.nvel = RcVec3f.Zero;
@ -279,15 +282,27 @@ namespace DotRecast.Detour.Crowd
return ag; return ag;
} }
/** public DtCrowdAgent GetAgent(int idx)
* Removes the agent from the crowd. {
* return _agents.GetValueOrDefault(idx);
* @param agent }
* Agent to be removed
*/ // 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) 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) private bool RequestMoveTargetReplan(DtCrowdAgent ag, long refs, RcVec3f pos)
@ -359,7 +374,7 @@ namespace DotRecast.Detour.Crowd
*/ */
public IList<DtCrowdAgent> GetActiveAgents() public IList<DtCrowdAgent> GetActiveAgents()
{ {
return _agents; return _activeAgents;
} }
public RcVec3f GetQueryExtents() public RcVec3f GetQueryExtents()
@ -590,7 +605,7 @@ namespace DotRecast.Detour.Crowd
if (ag.targetReplan) // && npath > 10) if (ag.targetReplan) // && npath > 10)
{ {
// Try to use existing steady path during replan if possible. // 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 else
{ {
@ -876,7 +891,7 @@ namespace DotRecast.Detour.Crowd
// Update the collision boundary after certain distance has been passed or // Update the collision boundary after certain distance has been passed or
// if it has become invalid. // if it has become invalid.
float updateThr = ag.option.collisionQueryRange * 0.25f; 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.IsValid(_navQuery, _filters[ag.option.queryFilterType]))
{ {
ag.boundary.Update(ag.corridor.GetFirstPoly(), ag.npos, ag.option.collisionQueryRange, _navQuery, ag.boundary.Update(ag.corridor.GetFirstPoly(), ag.npos, ag.option.collisionQueryRange, _navQuery,
@ -884,21 +899,66 @@ namespace DotRecast.Detour.Crowd
} }
// Query neighbour agents // 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);
} }
} }
public static int AddNeighbour(DtCrowdAgent idx, float dist, Span<DtCrowdNeighbour> neis, int nneis, int maxNeis)
private int GetNeighbours(RcVec3f pos, float height, float range, DtCrowdAgent skip, ref List<DtCrowdNeighbour> result, DtProximityGrid grid)
{ {
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<int> 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) for (int i = 0; i < nids; ++i)
{ {
var ag = ids[i]; var ag = GetAgent(ids[i]);
if (ag == skip) if (ag == skip)
{ {
continue; continue;
@ -918,11 +978,10 @@ namespace DotRecast.Detour.Crowd
continue; continue;
} }
result.Add(new DtCrowdNeighbour(ag, distSqr)); n = AddNeighbour(ag, distSqr, result, n, maxResult);
} }
result.Sort((o1, o2) => o1.dist.CompareTo(o2.dist)); return n;
return result.Count;
} }
private void FindCorners(IList<DtCrowdAgent> agents, DtCrowdAgentDebugInfo debug) private void FindCorners(IList<DtCrowdAgent> agents, DtCrowdAgentDebugInfo debug)
@ -945,13 +1004,13 @@ namespace DotRecast.Detour.Crowd
} }
// Find corners for steering // 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, // Check to see if the corner after the next corner is directly visible,
// and short cut to there. // 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, ag.corridor.OptimizePathVisibility(target, ag.option.pathOptimizationRange, _navQuery,
_filters[ag.option.queryFilterType]); _filters[ag.option.queryFilterType]);
@ -1001,18 +1060,18 @@ namespace DotRecast.Detour.Crowd
// Adjust the path over the off-mesh connection. // Adjust the path over the off-mesh connection.
long[] refs = new long[2]; 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)) ref anim.endPos, _navQuery))
{ {
anim.initPos = ag.npos; anim.initPos = ag.npos;
anim.polyRef = refs[1]; anim.polyRef = refs[1];
anim.active = true; anim.active = true;
anim.t = 0.0f; 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.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH;
ag.corners.Clear(); ag.ncorners = 0;
ag.neis.Clear(); ag.nneis = 0;
continue; continue;
} }
else else
@ -1064,7 +1123,7 @@ namespace DotRecast.Detour.Crowd
float speedScale = ag.GetDistanceToGoal(slowDownRadius) / slowDownRadius; float speedScale = ag.GetDistanceToGoal(slowDownRadius) / slowDownRadius;
ag.desiredSpeed = ag.option.maxSpeed; ag.desiredSpeed = ag.option.maxSpeed;
dvel = dvel.Scale(ag.desiredSpeed * speedScale); dvel = dvel * (ag.desiredSpeed * speedScale);
} }
// Separation // Separation
@ -1077,7 +1136,7 @@ namespace DotRecast.Detour.Crowd
float w = 0; float w = 0;
RcVec3f disp = new RcVec3f(); 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; DtCrowdAgent nei = ag.neis[j].agent;
@ -1098,20 +1157,20 @@ namespace DotRecast.Detour.Crowd
float dist = MathF.Sqrt(distSqr); float dist = MathF.Sqrt(distSqr);
float weight = separationWeight * (1.0f - RcMath.Sqr(dist * invSeparationDist)); 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; w += 1.0f;
} }
if (w > 0.0001f) if (w > 0.0001f)
{ {
// Adjust desired velocity. // 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. // Clamp desired velocity to desired speed.
float speedSqr = dvel.LengthSquared(); float speedSqr = dvel.LengthSquared();
float desiredSqr = RcMath.Sqr(ag.desiredSpeed); float desiredSqr = RcMath.Sqr(ag.desiredSpeed);
if (speedSqr > desiredSqr) if (speedSqr > desiredSqr)
{ {
dvel = dvel.Scale(desiredSqr / speedSqr); dvel = dvel * (desiredSqr / speedSqr);
} }
} }
} }
@ -1139,7 +1198,7 @@ namespace DotRecast.Detour.Crowd
_obstacleQuery.Reset(); _obstacleQuery.Reset();
// Add neighbours as obstacles. // 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; DtCrowdAgent nei = ag.neis[j].agent;
if(!nei.option.contributeObstacleAvoidance || nei.option.obstacleAvoidanceWeight < ag.option.obstacleAvoidanceWeight) if(!nei.option.contributeObstacleAvoidance || nei.option.obstacleAvoidanceWeight < ag.option.obstacleAvoidanceWeight)
@ -1230,7 +1289,7 @@ namespace DotRecast.Detour.Crowd
float w = 0; 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; DtCrowdAgent nei = ag.neis[j].agent;
long idx1 = nei.idx; long idx1 = nei.idx;
@ -1264,7 +1323,7 @@ namespace DotRecast.Detour.Crowd
pen = (1.0f / dist) * (pen * 0.5f) * _config.collisionResolveFactor; 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; w += 1.0f;
} }
@ -1272,7 +1331,7 @@ namespace DotRecast.Detour.Crowd
if (w > 0.0001f) if (w > 0.0001f)
{ {
float iw = 1.0f / w; float iw = 1.0f / w;
ag.disp = ag.disp.Scale(iw); ag.disp = ag.disp * iw;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -28,7 +28,7 @@ namespace DotRecast.Detour.Crowd
/// @ingroup crowd /// @ingroup crowd
public class DtCrowdAgent public class DtCrowdAgent
{ {
public readonly long idx; public readonly int idx;
/// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState) /// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
public DtCrowdAgentState state; public DtCrowdAgentState state;
@ -37,16 +37,19 @@ namespace DotRecast.Detour.Crowd
public bool partial; public bool partial;
/// The path corridor the agent is using. /// The path corridor the agent is using.
public DtPathCorridor corridor; public readonly DtPathCorridor corridor;
/// The local boundary data for the agent. /// The local boundary data for the agent.
public DtLocalBoundary boundary; public readonly DtLocalBoundary boundary;
/// Time since the agent's path corridor was optimized. /// Time since the agent's path corridor was optimized.
public float topologyOptTime; public float topologyOptTime;
/// The known neighbors of the agent. /// The known neighbors of the agent.
public List<DtCrowdNeighbour> neis = new List<DtCrowdNeighbour>(); public readonly DtCrowdNeighbour[] neis = new DtCrowdNeighbour[DtCrowdConst.DT_CROWDAGENT_MAX_NEIGHBOURS];
/// The number of neighbors.
public int nneis;
/// The desired speed. /// The desired speed.
public float desiredSpeed; public float desiredSpeed;
@ -61,7 +64,10 @@ namespace DotRecast.Detour.Crowd
public DtCrowdAgentParams option; public DtCrowdAgentParams option;
/// The local path corridor corners for the agent. /// The local path corridor corners for the agent.
public List<DtStraightPath> corners = new List<DtStraightPath>(); 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 DtMoveRequestState targetState; // < State of the movement request.
public long targetRef; // < Target polyref 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); RcVec3f dv = RcVec3f.Subtract(nvel, vel);
float ds = dv.Length(); float ds = dv.Length();
if (ds > maxDelta) if (ds > maxDelta)
dv = dv.Scale(maxDelta / ds); dv = dv * (maxDelta / ds);
vel = RcVec3f.Add(vel, dv); vel = RcVec3f.Add(vel, dv);
// Integrate // Integrate
if (vel.Length() > 0.0001f) if (vel.Length() > 0.0001f)
npos = RcVecUtils.Mad(npos, vel, dt); npos = RcVec.Mad(npos, vel, dt);
else else
vel = RcVec3f.Zero; vel = RcVec3f.Zero;
} }
public bool OverOffmeshConnection(float radius) public bool OverOffmeshConnection(float radius)
{ {
if (0 == corners.Count) if (0 == ncorners)
return false; return false;
bool offMeshConnection = ((corners[corners.Count - 1].flags bool offMeshConnection = ((corners[ncorners - 1].flags
& DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
? true ? true
: false; : false;
if (offMeshConnection) 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) if (distSq < radius * radius)
return true; return true;
} }
@ -119,12 +125,12 @@ namespace DotRecast.Detour.Crowd
public float GetDistanceToGoal(float range) public float GetDistanceToGoal(float range)
{ {
if (0 == corners.Count) if (0 == ncorners)
return range; 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) 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; return range;
} }
@ -132,10 +138,10 @@ namespace DotRecast.Detour.Crowd
public RcVec3f CalcSmoothSteerDirection() public RcVec3f CalcSmoothSteerDirection()
{ {
RcVec3f dir = new RcVec3f(); RcVec3f dir = new RcVec3f();
if (0 < corners.Count) if (0 < ncorners)
{ {
int ip0 = 0; int ip0 = 0;
int ip1 = Math.Min(1, corners.Count - 1); int ip1 = Math.Min(1, ncorners - 1);
var p0 = corners[ip0].pos; var p0 = corners[ip0].pos;
var p1 = corners[ip1].pos; var p1 = corners[ip1].pos;
@ -147,7 +153,7 @@ namespace DotRecast.Detour.Crowd
float len0 = dir0.Length(); float len0 = dir0.Length();
float len1 = dir1.Length(); float len1 = dir1.Length();
if (len1 > 0.001f) 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.X = dir0.X - dir1.X * len0 * 0.5f;
dir.Y = 0; dir.Y = 0;
@ -161,7 +167,7 @@ namespace DotRecast.Detour.Crowd
public RcVec3f CalcStraightSteerDirection() public RcVec3f CalcStraightSteerDirection()
{ {
RcVec3f dir = new RcVec3f(); RcVec3f dir = new RcVec3f();
if (0 < corners.Count) if (0 < ncorners)
{ {
dir = RcVec3f.Subtract(corners[0].pos, npos); dir = RcVec3f.Subtract(corners[0].pos, npos);
dir.Y = 0; dir.Y = 0;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
public class DtCrowdAgentConfig public class DtCrowdAgentConfig
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,23 +24,15 @@ namespace DotRecast.Detour.Crowd
/// @ingroup crowd /// @ingroup crowd
public class DtCrowdAgentParams public class DtCrowdAgentParams
{ {
/// < Agent radius. [Limit: >= 0] public float radius; // < Agent radius. [Limit: >= 0]
public float radius; public float height; // < Agent height. [Limit: > 0]
public float maxAcceleration; // < Maximum allowed acceleration. [Limit: >= 0]
/// < Agent height. [Limit: > 0] public float maxSpeed; // < Maximum allowed speed. [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] /// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
public float collisionQueryRange; public float collisionQueryRange;
/// < The path visibility optimization range. [Limit: > 0] public float pathOptimizationRange; // < The path visibility optimization range. [Limit: > 0]
public float pathOptimizationRange;
/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0] /// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
public float separationWeight; public float separationWeight;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
/// The type of navigation mesh polygon the agent is currently traversing. /// The type of navigation mesh polygon the agent is currently traversing.
/// @ingroup crowd /// @ingroup crowd

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
/// Crowd agent update flags. /// Crowd agent update flags.
/// @ingroup crowd /// @ingroup crowd

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
public static class DtCrowdConst public static class DtCrowdConst
{ {

View File

@ -1,16 +1,13 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
/// Provides neighbor data for agents managed by the crowd. /// Provides neighbor data for agents managed by the crowd.
/// @ingroup crowd /// @ingroup crowd
/// @see dtCrowdAgent::neis, dtCrowd /// @see dtCrowdAgent::neis, dtCrowd
public readonly struct DtCrowdNeighbour public readonly struct DtCrowdNeighbour
{ {
public readonly DtCrowdAgent agent; 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.
/// < 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) public DtCrowdNeighbour(DtCrowdAgent agent, float dist)
{ {
this.agent = agent; this.agent = agent;

View File

@ -1,4 +1,4 @@
using System; using System;
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
public class DtCrowdTimerLabel public class DtCrowdTimerLabel
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; 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) 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) if (startRef == 0)
{ {
Reset(); Reset();
@ -104,18 +107,17 @@ namespace DotRecast.Detour.Crowd
{ {
// Secondly, store all polygon edges. // Secondly, store all polygon edges.
m_segs.Clear(); m_segs.Clear();
Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS_PER_POLY];
var segmentVerts = new List<RcSegmentVert>(); int nsegs = 0;
var segmentRefs = new List<long>();
for (int j = 0; j < m_polys.Count; ++j) 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()) 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 s0 = s.vmin;
var s3 = s.vmax; var s3 = s.vmax;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
public enum DtMoveRequestState public enum DtMoveRequestState
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
public class DtObstacleAvoidanceParams public class DtObstacleAvoidanceParams
{ {
@ -8,18 +8,11 @@
public float weightSide; public float weightSide;
public float weightToi; public float weightToi;
public float horizTime; 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() public DtObstacleAvoidanceParams()
{ {
velBias = 0.4f; velBias = 0.4f;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Runtime.CompilerServices;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Buffers;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -28,11 +28,9 @@ namespace DotRecast.Detour.Crowd
{ {
public class DtObstacleAvoidanceQuery public class DtObstacleAvoidanceQuery
{ {
public const int DT_MAX_PATTERN_DIVS = 32; public const int DT_MAX_PATTERN_DIVS = 32; // < Max numver of adaptive divs.
/// < Max numver of adaptive divs.
public const int DT_MAX_PATTERN_RINGS = 4; public const int DT_MAX_PATTERN_RINGS = 4;
public const float DT_PI = 3.14159265f;
private DtObstacleAvoidanceParams m_params; private DtObstacleAvoidanceParams m_params;
private float m_invHorizTime; private float m_invHorizTime;
@ -188,16 +186,16 @@ namespace DotRecast.Detour.Crowd
{ {
RcVec3f v = RcVec3f.Subtract(bq, bp); RcVec3f v = RcVec3f.Subtract(bq, bp);
RcVec3f w = RcVec3f.Subtract(ap, 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) if (MathF.Abs(d) < 1e-6f)
return false; return false;
d = 1.0f / d; d = 1.0f / d;
t = RcVecUtils.Perp2D(v, w) * d; t = RcVec.Perp2D(v, w) * d;
if (t < 0 || t > 1) if (t < 0 || t > 1)
return false; return false;
float s = RcVecUtils.Perp2D(u, w) * d; float s = RcVec.Perp2D(u, w) * d;
if (s < 0 || s > 1) if (s < 0 || s > 1)
return false; return false;
@ -218,8 +216,8 @@ namespace DotRecast.Detour.Crowd
float minPenalty, DtObstacleAvoidanceDebugData debug) float minPenalty, DtObstacleAvoidanceDebugData debug)
{ {
// penalty for straying away from the desired and current velocities // penalty for straying away from the desired and current velocities
float vpen = m_params.weightDesVel * (RcVecUtils.Dist2D(vcand, dvel) * m_invVmax); float vpen = m_params.weightDesVel * (RcVec.Dist2D(vcand, dvel) * m_invVmax);
float vcpen = m_params.weightCurVel * (RcVecUtils.Dist2D(vcand, vel) * 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 // find the threshold hit time to bail out based on the early out penalty
// (see how the penalty is calculated below to understand) // (see how the penalty is calculated below to understand)
@ -238,7 +236,7 @@ namespace DotRecast.Detour.Crowd
DtObstacleCircle cir = m_circles[i]; DtObstacleCircle cir = m_circles[i];
// RVO // RVO
RcVec3f vab = vcand.Scale(2); RcVec3f vab = vcand * 2;
vab = RcVec3f.Subtract(vab, vel); vab = RcVec3f.Subtract(vab, vel);
vab = RcVec3f.Subtract(vab, cir.vel); vab = RcVec3f.Subtract(vab, cir.vel);
@ -363,7 +361,8 @@ namespace DotRecast.Detour.Crowd
} }
// vector normalization that ignores the y-component. // vector normalization that ignores the y-component.
void DtNormalize2D(float[] v) [MethodImpl(MethodImplOptions.AggressiveInlining)]
void DtNormalize2D(Span<float> v)
{ {
float d = MathF.Sqrt(v[0] * v[0] + v[2] * v[2]); float d = MathF.Sqrt(v[0] * v[0] + v[2] * v[2]);
if (d == 0) if (d == 0)
@ -374,7 +373,8 @@ namespace DotRecast.Detour.Crowd
} }
// vector normalization that ignores the y-component. // vector normalization that ignores the y-component.
RcVec3f DtRotate2D(float[] v, float ang) [MethodImpl(MethodImplOptions.AggressiveInlining)]
RcVec3f DtRotate2D(Span<float> v, float ang)
{ {
RcVec3f dest = new RcVec3f(); RcVec3f dest = new RcVec3f();
float c = MathF.Cos(ang); float c = MathF.Cos(ang);
@ -385,7 +385,6 @@ namespace DotRecast.Detour.Crowd
return dest; 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, public int SampleVelocityAdaptive(RcVec3f pos, float rad, float vmax, RcVec3f vel, RcVec3f dvel, out RcVec3f nvel,
DtObstacleAvoidanceParams option, DtObstacleAvoidanceParams option,
@ -403,7 +402,7 @@ namespace DotRecast.Detour.Crowd
debug.Reset(); debug.Reset();
// Build sampling pattern aligned to desired velocity. // Build sampling pattern aligned to desired velocity.
using var pat = RcRentedArray.Rent<float>((DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2); Span<float> pat = stackalloc float[(DT_MAX_PATTERN_DIVS * DT_MAX_PATTERN_RINGS + 1) * 2];
int npat = 0; int npat = 0;
int ndivs = m_params.adaptiveDivs; int ndivs = m_params.adaptiveDivs;
@ -417,7 +416,7 @@ namespace DotRecast.Detour.Crowd
float sa = MathF.Sin(da); float sa = MathF.Sin(da);
// desired direction // desired direction
float[] ddir = new float[6]; Span<float> ddir = stackalloc float[6];
ddir[0] = dvel.X; ddir[0] = dvel.X;
ddir[1] = dvel.Y; ddir[1] = dvel.Y;
ddir[2] = dvel.Z; ddir[2] = dvel.Z;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 RcVec3f m_target;
private List<long> m_path; private List<long> m_path;
private int m_npath;
private int m_maxPath; private int m_maxPath;
/** /**
@ -88,7 +89,8 @@ namespace DotRecast.Detour.Crowd
/// @return True if the initialization succeeded. /// @return True if the initialization succeeded.
public bool Init(int maxPath) public bool Init(int maxPath)
{ {
m_path = new List<long>(); m_path = new List<long>(maxPath);
m_npath = 0;
m_maxPath = maxPath; m_maxPath = maxPath;
return true; return true;
} }
@ -107,6 +109,7 @@ namespace DotRecast.Detour.Crowd
m_target = pos; m_target = pos;
m_path.Clear(); m_path.Clear();
m_path.Add(refs); 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] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation. /// @param[in] filter The filter to apply to the operation.
/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners] /// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
public int FindCorners(ref List<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter) public int FindCorners(Span<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
const float MIN_TARGET_DIST = 0.01f; const float MIN_TARGET_DIST = 0.01f;
var result = navquery.FindStraightPath(m_pos, m_target, m_path, ref corners, maxCorners, 0); int ncorners = 0;
if (result.Succeeded()) 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. // Prune points in the beginning of the path which are too close.
int start = 0; while (0 < ncorners)
foreach (DtStraightPath spi in corners)
{ {
if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 if ((corners[0].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 ||
|| RcVecUtils.Dist2DSqr(spi.pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST)) RcVec.Dist2DSqr(corners[0].pos, m_pos) > RcMath.Sqr(MIN_TARGET_DIST))
{ {
break; break;
} }
start++; ncorners--;
if (0 < ncorners)
{
RcSpans.Move(corners, 1, 0, 3);
}
} }
int end = corners.Count;
// Prune points after an off-mesh connection. // Prune points after an off-mesh connection.
for (int i = start; i < corners.Count; i++) for (int i = 0; i < ncorners; ++i)
{ {
DtStraightPath spi = corners[i]; if ((corners[i].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
if ((spi.flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0)
{ {
end = i + 1; ncorners = i + 1;
break; break;
} }
} }
corners = corners.GetRange(start, end - start);
}
return corners.Count; return ncorners;
} }
/** /**
@ -194,7 +197,7 @@ namespace DotRecast.Detour.Crowd
public void OptimizePathVisibility(RcVec3f next, float pathOptimizationRange, DtNavMeshQuery navquery, IDtQueryFilter filter) public void OptimizePathVisibility(RcVec3f next, float pathOptimizationRange, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Clamp the ray to max distance. // 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 too close to the goal, do not try to optimize.
if (dist < 0.01f) if (dist < 0.01f)
@ -207,7 +210,7 @@ namespace DotRecast.Detour.Crowd
// Adjust ray length. // Adjust ray length.
var delta = RcVec3f.Subtract(next, m_pos); 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<long>(); var res = new List<long>();
var status = navquery.Raycast(m_path[0], m_pos, goal, filter, out var t, out var norm, ref res); 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) 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. /// @param[in] filter The filter to apply to the operation.
public bool OptimizePathTopology(DtNavMeshQuery navquery, IDtQueryFilter filter, int maxIterations) public bool OptimizePathTopology(DtNavMeshQuery navquery, IDtQueryFilter filter, int maxIterations)
{ {
if (m_path.Count < 3) if (m_npath < 3)
{ {
return false; return false;
} }
@ -243,11 +246,11 @@ namespace DotRecast.Detour.Crowd
var res = new List<long>(); var res = new List<long>();
navquery.InitSlicedFindPath(m_path[0], m_path[^1], m_pos, m_target, filter, 0); navquery.InitSlicedFindPath(m_path[0], m_path[^1], m_pos, m_target, filter, 0);
navquery.UpdateSlicedFindPath(maxIterations, out var _); 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) 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; return true;
} }
@ -259,21 +262,23 @@ namespace DotRecast.Detour.Crowd
// Advance the path up to and over the off-mesh connection. // Advance the path up to and over the off-mesh connection.
long prevRef = 0, polyRef = m_path[0]; long prevRef = 0, polyRef = m_path[0];
int npos = 0; int npos = 0;
while (npos < m_path.Count && polyRef != offMeshConRef) while (npos < m_npath && polyRef != offMeshConRef)
{ {
prevRef = polyRef; prevRef = polyRef;
polyRef = m_path[npos]; polyRef = m_path[npos];
npos++; npos++;
} }
if (npos == m_path.Count) if (npos == m_npath)
{ {
// Could not find offMeshConRef // Could not find offMeshConRef
return false; return false;
} }
// Prune path // 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[0] = prevRef;
refs[1] = polyRef; refs[1] = polyRef;
@ -312,11 +317,12 @@ namespace DotRecast.Detour.Crowd
public bool MovePosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool MovePosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Move along navmesh and update new position. // Move along navmesh and update new position.
var visited = new List<long>(); const int MAX_VISITED = 16;
var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, ref visited); Span<long> 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()) 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. // Adjust the position to stay on top of the navmesh.
m_pos = result; m_pos = result;
@ -354,11 +360,14 @@ namespace DotRecast.Detour.Crowd
public bool MoveTargetPosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool MoveTargetPosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Move along navmesh and update new position. // Move along navmesh and update new position.
var visited = new List<long>(); const int MAX_VISITED = 16;
var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, ref visited); Span<long> 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()) 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? // TODO: should we do that?
// Adjust the position to stay on top of the navmesh. // Adjust the position to stay on top of the navmesh.
/* /*
@ -386,24 +395,27 @@ namespace DotRecast.Detour.Crowd
{ {
m_target = target; m_target = target;
m_path = new List<long>(path); m_path = new List<long>(path);
m_npath = path.Count;
} }
public void FixPathStart(long safeRef, RcVec3f safePos) public void FixPathStart(long safeRef, RcVec3f safePos)
{ {
m_pos = 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.Clear();
m_path.Add(safeRef); m_path.Add(safeRef);
m_path.Add(0L); m_path.Add(0L);
m_path.Add(p); m_path.Add(p);
m_npath = 3;
} }
else else
{ {
m_path.Clear(); m_path.Clear();
m_path.Add(safeRef); m_path.Add(safeRef);
m_path.Add(0L); m_path.Add(0L);
m_npath = 2;
} }
} }
@ -411,12 +423,12 @@ namespace DotRecast.Detour.Crowd
{ {
// Keep valid path as far as possible. // Keep valid path as far as possible.
int n = 0; 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++; n++;
} }
if (m_path.Count == n) if (m_npath == n)
{ {
// All valid, no need to fix. // All valid, no need to fix.
return true; return true;
@ -424,18 +436,20 @@ namespace DotRecast.Detour.Crowd
else if (n == 0) else if (n == 0)
{ {
// The first polyref is bad, use current safe values. // The first polyref is bad, use current safe values.
m_pos = RcVecUtils.Create(safePos); m_pos = new RcVec3f(safePos);
m_path.Clear(); m_path.Clear();
m_path.Add(safeRef); m_path.Add(safeRef);
m_npath = 1;
} }
else if (n < m_path.Count) else if (n < m_npath)
{ {
// The path is partially usable. // The path is partially usable.
m_path = m_path.GetRange(0, n); m_path = m_path.GetRange(0, n);
m_npath = n;
} }
// Clamp target pos to last poly // 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; return true;
} }
@ -451,7 +465,7 @@ namespace DotRecast.Detour.Crowd
public bool IsValid(int maxLookAhead, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool IsValid(int maxLookAhead, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Check that all polygons still pass query 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) for (int i = 0; i < n; ++i)
{ {
if (!navquery.IsValidPolyRef(m_path[i], filter)) 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.) /// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
public long GetFirstPoly() 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. /// 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.) /// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
public long GetLastPoly() 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. /// The corridor's path.
@ -502,7 +516,7 @@ namespace DotRecast.Detour.Crowd
/// @return The number of polygons in the current corridor path. /// @return The number of polygons in the current corridor path.
public int GetPathCount() public int GetPathCount()
{ {
return m_path.Count; return m_npath;
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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<int> ids, int maxIds)
{ {
int iminx = (int)MathF.Floor(minx * _invCellSize); int iminx = (int)MathF.Floor(minx * _invCellSize);
int iminy = (int)MathF.Floor(miny * _invCellSize); int iminy = (int)MathF.Floor(miny * _invCellSize);
@ -110,7 +110,7 @@ namespace DotRecast.Detour.Crowd
// Check if the id exists already. // Check if the id exists already.
int end = n; int end = n;
int i = 0; int i = 0;
while (i != end && ids[i] != item) while (i != end && ids[i] != item.idx)
{ {
++i; ++i;
} }
@ -118,7 +118,7 @@ namespace DotRecast.Detour.Crowd
// Item not found, add it. // Item not found, add it.
if (i == n) if (i == n)
{ {
ids[n++] = item; ids[n++] = item.idx;
if (n >= maxIds) if (n >= maxIds)
return n; return n;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections;
using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Detour.Dynamic.Io; using DotRecast.Detour.Dynamic.Io;
using DotRecast.Recast; using DotRecast.Recast;
@ -62,8 +63,8 @@ namespace DotRecast.Detour.Dynamic
navMeshParams.orig.X = voxelFile.bounds[0]; navMeshParams.orig.X = voxelFile.bounds[0];
navMeshParams.orig.Y = voxelFile.bounds[1]; navMeshParams.orig.Y = voxelFile.bounds[1];
navMeshParams.orig.Z = voxelFile.bounds[2]; navMeshParams.orig.Z = voxelFile.bounds[2];
navMeshParams.tileWidth = voxelFile.cellSize * voxelFile.tileSizeX; navMeshParams.tileWidth = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeX : voxelFile.bounds[3] - voxelFile.bounds[0];
navMeshParams.tileHeight = voxelFile.cellSize * voxelFile.tileSizeZ; navMeshParams.tileHeight = voxelFile.useTiles ? voxelFile.cellSize * voxelFile.tileSizeZ : voxelFile.bounds[5] - voxelFile.bounds[2];
navMeshParams.maxTiles = voxelFile.tiles.Count; navMeshParams.maxTiles = voxelFile.tiles.Count;
navMeshParams.maxPolys = 0x8000; navMeshParams.maxPolys = 0x8000;
foreach (var t in voxelFile.tiles) foreach (var t in voxelFile.tiles)
@ -188,17 +189,17 @@ namespace DotRecast.Detour.Dynamic
return _tiles.Values; return _tiles.Values;
} }
int minx = (int)MathF.Floor((bounds[0] - navMeshParams.orig.X) / navMeshParams.tileWidth); 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); 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); 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); int maxz = (int)MathF.Floor((bounds[5] - navMeshParams.orig.Z) / navMeshParams.tileHeight) + 1;
List<DtDynamicTile> tiles = new List<DtDynamicTile>(); List<DtDynamicTile> tiles = new List<DtDynamicTile>();
for (int z = minz; z <= maxz; ++z) for (int z = minz; z <= maxz; ++z)
{ {
for (int x = minx; x <= maxx; ++x) for (int x = minx; x <= maxx; ++x)
{ {
DtDynamicTile tile = GetTileAt(x, z); DtDynamicTile tile = GetTileAt(x, z);
if (tile != null) if (tile != null && IntersectsXZ(tile, bounds))
{ {
tiles.Add(tile); tiles.Add(tile);
} }
@ -208,6 +209,12 @@ namespace DotRecast.Detour.Dynamic
return tiles; 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<DtDynamicTile> GetTilesByCollider(long cid) private List<DtDynamicTile> GetTilesByCollider(long cid)
{ {
return _tiles.Values.Where(t => t.ContainsCollider(cid)).ToList(); return _tiles.Values.Where(t => t.ContainsCollider(cid)).ToList();
@ -224,12 +231,17 @@ namespace DotRecast.Detour.Dynamic
{ {
if (_dirty) 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) foreach (var t in _tiles.Values)
{
t.AddTo(navMesh); t.AddTo(navMesh);
}
_navMesh = navMesh; _navMesh = navMesh;
_dirty = false;
return true; return true;
} }
@ -257,5 +269,21 @@ namespace DotRecast.Detour.Dynamic
{ {
return _tiles.Values.Select(t => t.recastResult).ToList(); 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;
}
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.buildBvTree = true;
option.offMeshConCount = 0; option.offMeshConCount = 0;
option.offMeshConVerts = new float[0]; option.offMeshConVerts = Array.Empty<float>();
option.offMeshConRad = new float[0]; option.offMeshConRad = Array.Empty<float>();
option.offMeshConDir = new int[0]; option.offMeshConDir = Array.Empty<int>();
option.offMeshConAreas = new int[0]; option.offMeshConAreas = Array.Empty<int>();
option.offMeshConFlags = new int[0]; option.offMeshConFlags = Array.Empty<int>();
option.offMeshConUserID = new int[0]; option.offMeshConUserID = Array.Empty<int>();
return option; return option;
} }
@ -181,7 +181,7 @@ namespace DotRecast.Detour.Dynamic
{ {
if (meshData != null) if (meshData != null)
{ {
id = navMesh.AddTile(meshData, 0, 0); navMesh.AddTile(meshData, 0, 0, out var id);
} }
else else
{ {
@ -189,5 +189,10 @@ namespace DotRecast.Detour.Dynamic
id = 0; id = 0;
} }
} }
public void SetMeshData(DtMeshData data)
{
this.meshData = data;
}
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -78,7 +78,7 @@ namespace DotRecast.Detour.Dynamic.Io
walkbableAreaMod, buildMeshDetail); walkbableAreaMod, buildMeshDetail);
} }
public static DtVoxelFile From(RcConfig config, List<RcBuilderResult> results) public static DtVoxelFile From(RcConfig config, IList<RcBuilderResult> results)
{ {
DtVoxelFile f = new DtVoxelFile(); DtVoxelFile f = new DtVoxelFile();
f.version = 1; f.version = 1;
@ -109,13 +109,14 @@ namespace DotRecast.Detour.Dynamic.Io
}; };
foreach (RcBuilderResult r in results) 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.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[1] = Math.Min(f.bounds[1], r.SolidHeightfiled.bmin.Y);
f.bounds[2] = Math.Min(f.bounds[2], r.SolidHeightfiled.bmin.Z); 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); 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[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; return f;
@ -155,12 +156,13 @@ namespace DotRecast.Detour.Dynamic.Io
{ {
RcHeightfield heightfield = vt.Heightfield(); RcHeightfield heightfield = vt.Heightfield();
f.tiles.Add(new DtVoxelTile(vt.tileX, vt.tileZ, 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[1] = Math.Min(f.bounds[1], vt.boundsMin.Y);
f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z); f.bounds[2] = Math.Min(f.bounds[2], vt.boundsMin.Z + pad);
f.bounds[3] = Math.Max(f.bounds[3], vt.boundsMax.X); 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[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; return f;

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public DtVoxelFile Read(BinaryReader stream)
{ {
RcByteBuffer buf = IOUtils.ToByteBuffer(stream); RcByteBuffer buf = RcIO.ToByteBuffer(stream);
DtVoxelFile file = new DtVoxelFile(); DtVoxelFile file = new DtVoxelFile();
int magic = buf.GetInt(); int magic = buf.GetInt();
if (magic != DtVoxelFile.MAGIC) if (magic != DtVoxelFile.MAGIC)
{ {
magic = IOUtils.SwapEndianness(magic); magic = RcIO.SwapEndianness(magic);
if (magic != DtVoxelFile.MAGIC) if (magic != DtVoxelFile.MAGIC)
{ {
throw new IOException("Invalid magic"); throw new IOException("Invalid magic");

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System.IO;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics;
using DotRecast.Detour.Io; using DotRecast.Detour.Io;
namespace DotRecast.Detour.Dynamic.Io namespace DotRecast.Detour.Dynamic.Io
{ {
public class DtVoxelFileWriter : DtWriter public class DtVoxelFileWriter
{ {
private readonly IRcCompressor _compressor; private readonly IRcCompressor _compressor;
@ -40,34 +39,34 @@ namespace DotRecast.Detour.Dynamic.Io
public void Write(BinaryWriter stream, DtVoxelFile f, RcByteOrder byteOrder, bool compression) public void Write(BinaryWriter stream, DtVoxelFile f, RcByteOrder byteOrder, bool compression)
{ {
Write(stream, DtVoxelFile.MAGIC, byteOrder); RcIO.Write(stream, DtVoxelFile.MAGIC, byteOrder);
Write(stream, DtVoxelFile.VERSION_EXPORTER_RECAST4J | (compression ? DtVoxelFile.VERSION_COMPRESSION_LZ4 : 0), byteOrder); RcIO.Write(stream, DtVoxelFile.VERSION_EXPORTER_RECAST4J | (compression ? DtVoxelFile.VERSION_COMPRESSION_LZ4 : 0), byteOrder);
Write(stream, f.walkableRadius, byteOrder); RcIO.Write(stream, f.walkableRadius, byteOrder);
Write(stream, f.walkableHeight, byteOrder); RcIO.Write(stream, f.walkableHeight, byteOrder);
Write(stream, f.walkableClimb, byteOrder); RcIO.Write(stream, f.walkableClimb, byteOrder);
Write(stream, f.walkableSlopeAngle, byteOrder); RcIO.Write(stream, f.walkableSlopeAngle, byteOrder);
Write(stream, f.cellSize, byteOrder); RcIO.Write(stream, f.cellSize, byteOrder);
Write(stream, f.maxSimplificationError, byteOrder); RcIO.Write(stream, f.maxSimplificationError, byteOrder);
Write(stream, f.maxEdgeLen, byteOrder); RcIO.Write(stream, f.maxEdgeLen, byteOrder);
Write(stream, f.minRegionArea, byteOrder); RcIO.Write(stream, f.minRegionArea, byteOrder);
Write(stream, f.regionMergeArea, byteOrder); RcIO.Write(stream, f.regionMergeArea, byteOrder);
Write(stream, f.vertsPerPoly, byteOrder); RcIO.Write(stream, f.vertsPerPoly, byteOrder);
Write(stream, f.buildMeshDetail); RcIO.Write(stream, f.buildMeshDetail);
Write(stream, f.detailSampleDistance, byteOrder); RcIO.Write(stream, f.detailSampleDistance, byteOrder);
Write(stream, f.detailSampleMaxError, byteOrder); RcIO.Write(stream, f.detailSampleMaxError, byteOrder);
Write(stream, f.useTiles); RcIO.Write(stream, f.useTiles);
Write(stream, f.tileSizeX, byteOrder); RcIO.Write(stream, f.tileSizeX, byteOrder);
Write(stream, f.tileSizeZ, byteOrder); RcIO.Write(stream, f.tileSizeZ, byteOrder);
Write(stream, f.rotation.X, byteOrder); RcIO.Write(stream, f.rotation.X, byteOrder);
Write(stream, f.rotation.Y, byteOrder); RcIO.Write(stream, f.rotation.Y, byteOrder);
Write(stream, f.rotation.Z, byteOrder); RcIO.Write(stream, f.rotation.Z, byteOrder);
Write(stream, f.bounds[0], byteOrder); RcIO.Write(stream, f.bounds[0], byteOrder);
Write(stream, f.bounds[1], byteOrder); RcIO.Write(stream, f.bounds[1], byteOrder);
Write(stream, f.bounds[2], byteOrder); RcIO.Write(stream, f.bounds[2], byteOrder);
Write(stream, f.bounds[3], byteOrder); RcIO.Write(stream, f.bounds[3], byteOrder);
Write(stream, f.bounds[4], byteOrder); RcIO.Write(stream, f.bounds[4], byteOrder);
Write(stream, f.bounds[5], byteOrder); RcIO.Write(stream, f.bounds[5], byteOrder);
Write(stream, f.tiles.Count, byteOrder); RcIO.Write(stream, f.tiles.Count, byteOrder);
foreach (DtVoxelTile t in f.tiles) foreach (DtVoxelTile t in f.tiles)
{ {
WriteTile(stream, t, byteOrder, compression); 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) public void WriteTile(BinaryWriter stream, DtVoxelTile tile, RcByteOrder byteOrder, bool compression)
{ {
Write(stream, tile.tileX, byteOrder); RcIO.Write(stream, tile.tileX, byteOrder);
Write(stream, tile.tileZ, byteOrder); RcIO.Write(stream, tile.tileZ, byteOrder);
Write(stream, tile.width, byteOrder); RcIO.Write(stream, tile.width, byteOrder);
Write(stream, tile.depth, byteOrder); RcIO.Write(stream, tile.depth, byteOrder);
Write(stream, tile.borderSize, byteOrder); RcIO.Write(stream, tile.borderSize, byteOrder);
Write(stream, tile.boundsMin.X, byteOrder); RcIO.Write(stream, tile.boundsMin.X, byteOrder);
Write(stream, tile.boundsMin.Y, byteOrder); RcIO.Write(stream, tile.boundsMin.Y, byteOrder);
Write(stream, tile.boundsMin.Z, byteOrder); RcIO.Write(stream, tile.boundsMin.Z, byteOrder);
Write(stream, tile.boundsMax.X, byteOrder); RcIO.Write(stream, tile.boundsMax.X, byteOrder);
Write(stream, tile.boundsMax.Y, byteOrder); RcIO.Write(stream, tile.boundsMax.Y, byteOrder);
Write(stream, tile.boundsMax.Z, byteOrder); RcIO.Write(stream, tile.boundsMax.Z, byteOrder);
Write(stream, tile.cellSize, byteOrder); RcIO.Write(stream, tile.cellSize, byteOrder);
Write(stream, tile.cellHeight, byteOrder); RcIO.Write(stream, tile.cellHeight, byteOrder);
byte[] bytes = tile.spanData; byte[] bytes = tile.spanData;
if (compression) if (compression)
{ {
bytes = _compressor.Compress(bytes); bytes = _compressor.Compress(bytes);
} }
Write(stream, bytes.Length, byteOrder); RcIO.Write(stream, bytes.Length, byteOrder);
stream.Write(bytes); stream.Write(bytes);
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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(); BVItem it = new BVItem();
items[i] = it; items[i] = it;
it.i = i; it.i = i;
RcVec3f bmin = RcVecUtils.Create(data.verts, data.polys[i].verts[0] * 3); RcVec3f bmin = RcVec.Create(data.verts, data.polys[i].verts[0] * 3);
RcVec3f bmax = RcVecUtils.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++) for (int j = 1; j < data.polys[i].vertCount; j++)
{ {
bmin = RcVecUtils.Min(bmin, data.verts, data.polys[i].verts[j] * 3); bmin = RcVec3f.Min(bmin, RcVec.Create(data.verts, data.polys[i].verts[j] * 3));
bmax = RcVecUtils.Max(bmax, 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.X = 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.Y = 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.bmin.Z = 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.X = 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.Y = 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.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); return DtNavMeshBuilder.Subdivide(items, data.header.polyCount, 0, data.header.polyCount, 0, nodes);

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -11,7 +11,7 @@ namespace DotRecast.Detour.Extras.Jumplink
protected void SampleGround(JumpLinkBuilderConfig acfg, EdgeSampler es, ComputeNavMeshHeight heightFunc) protected void SampleGround(JumpLinkBuilderConfig acfg, EdgeSampler es, ComputeNavMeshHeight heightFunc)
{ {
float cs = acfg.cellSize; 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)); int ngsamples = Math.Max(2, (int)MathF.Ceiling(dist / cs));
SampleGroundSegment(heightFunc, es.start, ngsamples); SampleGroundSegment(heightFunc, es.start, ngsamples);

View File

@ -1,17 +1,18 @@
using System; using System;
using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Extras.Jumplink 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() return new RcVec3f()
{ {
X = Lerp(start.X, end.X, Math.Min(2f * u, 1f)), X = RcMath.Lerp(start.X, end.X, Math.Min(2f * u, 1f)),
Y = Lerp(start.Y, end.Y, Math.Max(0f, 2f * u - 1f)), Y = RcMath.Lerp(start.Y, end.Y, Math.Max(0f, 2f * u - 1f)),
Z = Lerp(start.Z, end.Z, Math.Min(2f * u, 1f)) Z = RcMath.Lerp(start.Z, end.Z, Math.Min(2f * u, 1f))
}; };
} }
} }

View File

@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Recast; using DotRecast.Recast;
using static DotRecast.Recast.RcConstants;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
using static RcRecast;
public class EdgeExtractor public class EdgeExtractor
{ {
public JumpEdge[] ExtractEdges(RcPolyMesh mesh) public JumpEdge[] ExtractEdges(RcPolyMesh mesh)

View File

@ -7,13 +7,13 @@ namespace DotRecast.Detour.Extras.Jumplink
{ {
public readonly GroundSegment start = new GroundSegment(); public readonly GroundSegment start = new GroundSegment();
public readonly List<GroundSegment> end = new List<GroundSegment>(); public readonly List<GroundSegment> end = new List<GroundSegment>();
public readonly Trajectory trajectory; public readonly ITrajectory trajectory;
public readonly RcVec3f ax = new RcVec3f(); public readonly RcVec3f ax = new RcVec3f();
public readonly RcVec3f ay = new RcVec3f(); public readonly RcVec3f ay = new RcVec3f();
public readonly RcVec3f az = new RcVec3f(); public readonly RcVec3f az = new RcVec3f();
public EdgeSampler(JumpEdge edge, Trajectory trajectory) public EdgeSampler(JumpEdge edge, ITrajectory trajectory)
{ {
this.trajectory = trajectory; this.trajectory = trajectory;
ax = RcVec3f.Subtract(edge.sq, edge.sp); ax = RcVec3f.Subtract(edge.sq, edge.sp);

View File

@ -3,7 +3,7 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
class EdgeSamplerFactory public class EdgeSamplerFactory
{ {
public EdgeSampler Get(JumpLinkBuilderConfig acfg, JumpLinkType type, JumpEdge edge) public EdgeSampler Get(JumpLinkBuilderConfig acfg, JumpLinkType type, JumpEdge edge)
{ {

View File

@ -4,7 +4,7 @@ namespace DotRecast.Detour.Extras.Jumplink
{ {
public class GroundSample public class GroundSample
{ {
public RcVec3f p = new RcVec3f(); public RcVec3f p;
public bool validTrajectory; public bool validTrajectory;
public bool validHeight; public bool validHeight;
} }

View File

@ -4,8 +4,8 @@ namespace DotRecast.Detour.Extras.Jumplink
{ {
public class GroundSegment public class GroundSegment
{ {
public RcVec3f p = new RcVec3f(); public RcVec3f p;
public RcVec3f q = new RcVec3f(); public RcVec3f q;
public GroundSample[] gsamples; public GroundSample[] gsamples;
public float height; public float height;
} }

View File

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

View File

@ -10,6 +10,6 @@ namespace DotRecast.Detour.Extras.Jumplink
public GroundSample[] endSamples; public GroundSample[] endSamples;
public GroundSegment start; public GroundSegment start;
public GroundSegment end; public GroundSegment end;
public Trajectory trajectory; public ITrajectory trajectory;
} }
} }

View File

@ -59,7 +59,7 @@ namespace DotRecast.Detour.Extras.Jumplink
GroundSegment end = es.end[js.groundSegment]; GroundSegment end = es.end[js.groundSegment];
RcVec3f ep = end.gsamples[js.startSample].p; RcVec3f ep = end.gsamples[js.startSample].p;
RcVec3f eq = end.gsamples[js.startSample + js.samples - 1].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) if (d >= 4 * acfg.agentRadius * acfg.agentRadius)
{ {
JumpLink link = new JumpLink(); JumpLink link = new JumpLink();

View File

@ -4,7 +4,7 @@ using DotRecast.Core;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
class JumpSegmentBuilder public class JumpSegmentBuilder
{ {
public JumpSegment[] Build(JumpLinkBuilderConfig acfg, EdgeSampler es) public JumpSegment[] Build(JumpLinkBuilderConfig acfg, EdgeSampler es)
{ {

View File

@ -1,9 +1,10 @@
using System; using System;
using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
public class JumpTrajectory : Trajectory public class JumpTrajectory : ITrajectory
{ {
private readonly float jumpHeight; private readonly float jumpHeight;
@ -12,13 +13,13 @@ namespace DotRecast.Detour.Extras.Jumplink
this.jumpHeight = jumpHeight; 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 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), Y = InterpolateHeight(start.Y, end.Y, u),
Z = Lerp(start.Z, end.Z, u) Z = RcMath.Lerp(start.Z, end.Z, u)
}; };
} }

View File

@ -1,11 +1,10 @@
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Recast; using DotRecast.Recast;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
class NavMeshGroundSampler : AbstractGroundSampler public class NavMeshGroundSampler : AbstractGroundSampler
{ {
public override void Sample(JumpLinkBuilderConfig acfg, RcBuilderResult result, EdgeSampler es) 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.cs = r.Mesh.cs;
option.ch = r.Mesh.ch; option.ch = r.Mesh.ch;
option.buildBvTree = true; 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(); RcAtomicBoolean found = new RcAtomicBoolean();
RcAtomicFloat minHeight = new RcAtomicFloat(pt.Y); 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); var status = navMeshQuery.GetPolyHeight(refs, pt, out var h);
if (status.Succeeded()) if (status.Succeeded())

View File

@ -1,19 +0,0 @@
using System;
namespace DotRecast.Detour.Extras.Jumplink
{
public class PolyQueryInvoker : IDtPolyQuery
{
public readonly Action<DtMeshTile, DtPoly, long> _callback;
public PolyQueryInvoker(Action<DtMeshTile, DtPoly, long> callback)
{
_callback = callback;
}
public void Process(DtMeshTile tile, DtPoly poly, long refs)
{
_callback?.Invoke(tile, poly, refs);
}
}
}

View File

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

View File

@ -5,7 +5,7 @@ using DotRecast.Recast;
namespace DotRecast.Detour.Extras.Jumplink namespace DotRecast.Detour.Extras.Jumplink
{ {
class TrajectorySampler public class TrajectorySampler
{ {
public void Sample(JumpLinkBuilderConfig acfg, RcHeightfield heightfield, EdgeSampler es) 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 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)); int nsamples = Math.Max(2, (int)MathF.Ceiling(d / cs));
for (int i = 0; i < nsamples; ++i) for (int i = 0; i < nsamples; ++i)
{ {

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour.Extras.Unity.Astar
{ {
using static DtDetour;
public class GraphMeshDataReader : ZipBinaryReader public class GraphMeshDataReader : ZipBinaryReader
{ {
public const float INT_PRECISION_FACTOR = 1000f; public const float INT_PRECISION_FACTOR = 1000f;
@ -76,7 +79,7 @@ namespace DotRecast.Detour.Extras.Unity.Astar
int nodeCount = buffer.GetInt(); int nodeCount = buffer.GetInt();
DtPoly[] nodes = new DtPoly[nodeCount]; DtPoly[] nodes = new DtPoly[nodeCount];
DtPolyDetail[] detailNodes = new DtPolyDetail[nodeCount]; DtPolyDetail[] detailNodes = new DtPolyDetail[nodeCount];
float[] detailVerts = new float[0]; float[] detailVerts = Array.Empty<float>();
int[] detailTris = new int[4 * nodeCount]; int[] detailTris = new int[4 * nodeCount];
int vertMask = GetVertMask(vertsCount); int vertMask = GetVertMask(vertsCount);
float ymin = float.PositiveInfinity; 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[1] * 3 + 1]);
ymax = Math.Max(ymax, verts[nodes[i].verts[2] * 3 + 1]); ymax = Math.Max(ymax, verts[nodes[i].verts[2] * 3 + 1]);
int vertBase = 0; int vertBase = 0;
int vertCount = 0; byte vertCount = 0;
int triBase = i; int triBase = i;
int triCount = 1; byte triCount = 1;
detailNodes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); detailNodes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount);
detailTris[4 * i] = 0; detailTris[4 * i] = 0;
detailTris[4 * i + 1] = 1; detailTris[4 * i + 1] = 1;
@ -116,15 +119,15 @@ namespace DotRecast.Detour.Extras.Unity.Astar
tiles[tileIndex].detailVerts = detailVerts; tiles[tileIndex].detailVerts = detailVerts;
tiles[tileIndex].detailTris = detailTris; tiles[tileIndex].detailTris = detailTris;
DtMeshHeader header = new DtMeshHeader(); DtMeshHeader header = new DtMeshHeader();
header.magic = DtNavMesh.DT_NAVMESH_MAGIC; header.magic = DT_NAVMESH_MAGIC;
header.version = DtNavMesh.DT_NAVMESH_VERSION; header.version = DT_NAVMESH_VERSION;
header.x = x; header.x = x;
header.y = z; header.y = z;
header.polyCount = nodeCount; header.polyCount = nodeCount;
header.vertCount = vertsCount; header.vertCount = vertsCount;
header.detailMeshCount = nodeCount; header.detailMeshCount = nodeCount;
header.detailTriCount = 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 + header.bmin.X = meta.forcedBoundsCenter.x - 0.5f * meta.forcedBoundsSize.x +
meta.cellSize * meta.tileSizeX * x; meta.cellSize * meta.tileSizeX * x;
header.bmin.Y = ymin; header.bmin.Y = ymin;

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour.Extras.Unity.Astar
{ {
using static DtDetour;
public class LinkBuilder public class LinkBuilder
{ {
// Process connections and transform them into recast neighbour flags // 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) 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) 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) 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 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;
} }
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.X = -0.5f * graphMeta.forcedBoundsSize.x + graphMeta.forcedBoundsCenter.x;
option.orig.Y = -0.5f * graphMeta.forcedBoundsSize.y + graphMeta.forcedBoundsCenter.y; option.orig.Y = -0.5f * graphMeta.forcedBoundsSize.y + graphMeta.forcedBoundsCenter.y;
option.orig.Z = -0.5f * graphMeta.forcedBoundsSize.z + graphMeta.forcedBoundsCenter.z; 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) foreach (DtMeshData t in graphMeshData.tiles)
{ {
mesh.AddTile(t, 0, 0); mesh.AddTile(t, 0, 0, out _);
} }
meshes[graphIndex] = mesh; meshes[graphIndex] = mesh;

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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); ZipArchiveEntry graphReferences = file.GetEntry(filename);
using var entryStream = graphReferences.Open(); using var entryStream = graphReferences.Open();
using var br = new BinaryReader(entryStream); using var br = new BinaryReader(entryStream);
RcByteBuffer buffer = IOUtils.ToByteBuffer(br); RcByteBuffer buffer = RcIO.ToByteBuffer(br);
buffer.Order(RcByteOrder.LITTLE_ENDIAN); buffer.Order(RcByteOrder.LITTLE_ENDIAN);
return buffer; return buffer;
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {
/// Flags for addTile /// Flags for addTile
public class DtCompressedTileFlags public class DtCompressedTileFlags

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {
@ -7,8 +7,9 @@ namespace DotRecast.Detour.TileCache
public const int DT_LAYER_MAX_NEIS = 16; public const int DT_LAYER_MAX_NEIS = 16;
public int area; public int area;
public List<int> neis = new List<int>(DT_LAYER_MAX_NEIS); public byte[] neis = new byte[DT_LAYER_MAX_NEIS];
public int regId; public byte nneis;
public int areaId; public byte regId;
public byte areaId;
}; };
} }

View File

@ -0,0 +1,10 @@
using DotRecast.Core.Numerics;
namespace DotRecast.Detour.TileCache
{
public class DtObstacleBox
{
public RcVec3f bmin;
public RcVec3f bmax;
}
}

View File

@ -0,0 +1,11 @@
using DotRecast.Core.Numerics;
namespace DotRecast.Detour.TileCache
{
public class DtObstacleCylinder
{
public RcVec3f pos;
public float radius;
public float height;
}
}

View File

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

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour.TileCache
{ {
using static DtDetour;
public class DtTileCache public class DtTileCache
{ {
private int m_tileLutSize; // < Tile hash lookup size (must be pot). private int m_tileLutSize; // < Tile hash lookup size (must be pot).
@ -51,9 +53,6 @@ namespace DotRecast.Detour.TileCache
private readonly List<DtObstacleRequest> m_reqs = new List<DtObstacleRequest>(); private readonly List<DtObstacleRequest> m_reqs = new List<DtObstacleRequest>();
private readonly List<long> m_update = new List<long>(); private readonly List<long> m_update = new List<long>();
private readonly DtTileCacheBuilder builder = new DtTileCacheBuilder();
private readonly DtTileCacheLayerHeaderReader tileReader = new DtTileCacheLayerHeaderReader();
public DtTileCache(DtTileCacheParams option, DtTileCacheStorageParams storageParams, DtNavMesh navmesh, IRcCompressor tcomp, IDtTileCacheMeshProcess tmprocs) public DtTileCache(DtTileCacheParams option, DtTileCacheStorageParams storageParams, DtNavMesh navmesh, IRcCompressor tcomp, IDtTileCacheMeshProcess tmprocs)
{ {
m_params = option; m_params = option;
@ -160,7 +159,7 @@ namespace DotRecast.Detour.TileCache
List<long> tiles = new List<long>(); List<long> tiles = new List<long>();
// Find tile based on hash. // 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]; DtCompressedTile tile = m_posLookup[h];
while (tile != null) while (tile != null)
{ {
@ -178,7 +177,7 @@ namespace DotRecast.Detour.TileCache
DtCompressedTile GetTileAt(int tx, int ty, int tlayer) DtCompressedTile GetTileAt(int tx, int ty, int tlayer)
{ {
// Find tile based on hash. // 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]; DtCompressedTile tile = m_posLookup[h];
while (tile != null) while (tile != null)
{ {
@ -243,7 +242,7 @@ namespace DotRecast.Detour.TileCache
// Make sure the data is in right format. // Make sure the data is in right format.
RcByteBuffer buf = new RcByteBuffer(data); RcByteBuffer buf = new RcByteBuffer(data);
buf.Order(m_storageParams.Order); 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. // Make sure the location is free.
if (GetTileAt(header.tx, header.ty, header.tlayer) != null) if (GetTileAt(header.tx, header.ty, header.tlayer) != null)
{ {
@ -266,7 +265,7 @@ namespace DotRecast.Detour.TileCache
} }
// Insert tile into the position lut. // 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]; tile.next = m_posLookup[h];
m_posLookup[h] = tile; m_posLookup[h] = tile;
@ -305,7 +304,7 @@ namespace DotRecast.Detour.TileCache
} }
// Remove tile from hash lookup. // 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 prev = null;
DtCompressedTile cur = m_posLookup[h]; DtCompressedTile cur = m_posLookup[h];
while (cur != null) while (cur != null)
@ -349,11 +348,11 @@ namespace DotRecast.Detour.TileCache
public long AddObstacle(RcVec3f pos, float radius, float height) public long AddObstacle(RcVec3f pos, float radius, float height)
{ {
DtTileCacheObstacle ob = AllocObstacle(); DtTileCacheObstacle ob = AllocObstacle();
ob.type = DtTileCacheObstacleType.CYLINDER; ob.type = DtTileCacheObstacleType.DT_OBSTACLE_CYLINDER;
ob.pos = pos; ob.cylinder.pos = pos;
ob.radius = radius; ob.cylinder.radius = radius;
ob.height = height; ob.cylinder.height = height;
return AddObstacleRequest(ob).refs; return AddObstacleRequest(ob).refs;
} }
@ -362,10 +361,10 @@ namespace DotRecast.Detour.TileCache
public long AddBoxObstacle(RcVec3f bmin, RcVec3f bmax) public long AddBoxObstacle(RcVec3f bmin, RcVec3f bmax)
{ {
DtTileCacheObstacle ob = AllocObstacle(); DtTileCacheObstacle ob = AllocObstacle();
ob.type = DtTileCacheObstacleType.BOX; ob.type = DtTileCacheObstacleType.DT_OBSTACLE_BOX;
ob.bmin = bmin; ob.box.bmin = bmin;
ob.bmax = bmax; ob.box.bmax = bmax;
return AddObstacleRequest(ob).refs; return AddObstacleRequest(ob).refs;
} }
@ -374,13 +373,13 @@ namespace DotRecast.Detour.TileCache
public long AddBoxObstacle(RcVec3f center, RcVec3f extents, float yRadians) public long AddBoxObstacle(RcVec3f center, RcVec3f extents, float yRadians)
{ {
DtTileCacheObstacle ob = AllocObstacle(); DtTileCacheObstacle ob = AllocObstacle();
ob.type = DtTileCacheObstacleType.ORIENTED_BOX; ob.type = DtTileCacheObstacleType.DT_OBSTACLE_ORIENTED_BOX;
ob.center = center; ob.orientedBox.center = center;
ob.extents = extents; ob.orientedBox.extents = extents;
float coshalf = MathF.Cos(0.5f * yRadians); float coshalf = MathF.Cos(0.5f * yRadians);
float sinhalf = MathF.Sin(-0.5f * yRadians); float sinhalf = MathF.Sin(-0.5f * yRadians);
ob.rotAux[0] = coshalf * sinhalf; ob.orientedBox.rotAux[0] = coshalf * sinhalf;
ob.rotAux[1] = coshalf * coshalf - 0.5f; ob.orientedBox.rotAux[1] = coshalf * coshalf - 0.5f;
return AddObstacleRequest(ob).refs; return AddObstacleRequest(ob).refs;
} }
@ -613,26 +612,26 @@ namespace DotRecast.Detour.TileCache
if (Contains(ob.touched, refs)) 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 // Build navmesh
builder.BuildTileCacheRegions(layer, walkableClimbVx); DtTileCacheBuilder.BuildTileCacheRegions(layer, walkableClimbVx);
DtTileCacheContourSet lcset = builder.BuildTileCacheContours(layer, walkableClimbVx, DtTileCacheContourSet lcset = DtTileCacheBuilder.BuildTileCacheContours(layer, walkableClimbVx, m_params.maxSimplificationError);
m_params.maxSimplificationError); DtTileCachePolyMesh polyMesh = DtTileCacheBuilder.BuildTileCachePolyMesh(lcset, m_navmesh.GetMaxVertsPerPoly());
DtTileCachePolyMesh polyMesh = builder.BuildTileCachePolyMesh(lcset, m_navmesh.GetMaxVertsPerPoly());
// Early out if the mesh tile is empty. // Early out if the mesh tile is empty.
if (polyMesh.npolys == 0) if (polyMesh.npolys == 0)
{ {
@ -670,13 +669,13 @@ namespace DotRecast.Detour.TileCache
// Add new tile, or leave the location empty. if (navData) { // Let the // Add new tile, or leave the location empty. if (navData) { // Let the
if (meshData != null) if (meshData != null)
{ {
m_navmesh.AddTile(meshData, 0, 0); m_navmesh.AddTile(meshData, 0, 0, out var result);
} }
} }
public DtTileCacheLayer DecompressTile(DtCompressedTile tile) 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; return layer;
} }
@ -693,29 +692,29 @@ namespace DotRecast.Detour.TileCache
public void GetObstacleBounds(DtTileCacheObstacle ob, ref RcVec3f bmin, ref RcVec3f bmax) 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.X = ob.cylinder.pos.X - ob.cylinder.radius;
bmin.Y = ob.pos.Y; bmin.Y = ob.cylinder.pos.Y;
bmin.Z = ob.pos.Z - ob.radius; bmin.Z = ob.cylinder.pos.Z - ob.cylinder.radius;
bmax.X = ob.pos.X + ob.radius; bmax.X = ob.cylinder.pos.X + ob.cylinder.radius;
bmax.Y = ob.pos.Y + ob.height; bmax.Y = ob.cylinder.pos.Y + ob.cylinder.height;
bmax.Z = ob.pos.Z + ob.radius; 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; bmin = ob.box.bmin;
bmax = ob.bmax; 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); float maxr = 1.41f * Math.Max(ob.orientedBox.extents.X, ob.orientedBox.extents.Z);
bmin.X = ob.center.X - maxr; bmin.X = ob.orientedBox.center.X - maxr;
bmax.X = ob.center.X + maxr; bmax.X = ob.orientedBox.center.X + maxr;
bmin.Y = ob.center.Y - ob.extents.Y; bmin.Y = ob.orientedBox.center.Y - ob.orientedBox.extents.Y;
bmax.Y = ob.center.Y + ob.extents.Y; bmax.Y = ob.orientedBox.center.Y + ob.orientedBox.extents.Y;
bmin.Z = ob.center.Z - maxr; bmin.Z = ob.orientedBox.center.Z - maxr;
bmax.Z = ob.center.Z + maxr; bmax.Z = ob.orientedBox.center.Z + maxr;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -29,22 +29,21 @@ using DotRecast.Recast;
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {
public class DtTileCacheBuilder public static class DtTileCacheBuilder
{ {
public const int DT_TILECACHE_NULL_AREA = 0; public const byte DT_TILECACHE_NULL_AREA = 0;
public const int DT_TILECACHE_WALKABLE_AREA = 63; public const byte DT_TILECACHE_WALKABLE_AREA = 63;
public const int DT_TILECACHE_NULL_IDX = 0xffff; public const int DT_TILECACHE_NULL_IDX = 0xffff;
private static readonly int[] DirOffsetX = { -1, 0, 1, 0, }; private static readonly int[] DirOffsetX = { -1, 0, 1, 0, };
private static readonly int[] DirOffsetY = { 0, 1, 0, -1 }; private static readonly int[] DirOffsetY = { 0, 1, 0, -1 };
private readonly DtTileCacheLayerHeaderReader reader = new DtTileCacheLayerHeaderReader(); public static void BuildTileCacheRegions(DtTileCacheLayer layer, int walkableClimb)
public void BuildTileCacheRegions(DtTileCacheLayer layer, int walkableClimb)
{ {
int w = layer.header.width; int w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
Array.Fill(layer.regs, (short)0x00FF); Array.Fill(layer.regs, (byte)0xFF);
int nsweeps = w; int nsweeps = w;
RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps]; RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps];
for (int i = 0; i < sweeps.Length; i++) for (int i = 0; i < sweeps.Length; i++)
@ -53,14 +52,14 @@ namespace DotRecast.Detour.TileCache
} }
// Partition walkable area into monotone regions. // Partition walkable area into monotone regions.
int[] prevCount = new int[256]; Span<byte> prevCount = stackalloc byte[256];
int regId = 0; byte regId = 0;
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
if (regId > 0) if (regId > 0)
{ {
Array.Fill(prevCount, 0, 0, regId); RcSpans.Fill<byte>(prevCount, 0, 0, regId);
} }
// Memset(prevCount,0,Sizeof(char)*regId); // Memset(prevCount,0,Sizeof(char)*regId);
@ -93,7 +92,7 @@ namespace DotRecast.Detour.TileCache
int yidx = x + (y - 1) * w; int yidx = x + (y - 1) * w;
if (y > 0 && IsConnected(layer, idx, yidx, walkableClimb)) if (y > 0 && IsConnected(layer, idx, yidx, walkableClimb))
{ {
int nr = layer.regs[yidx]; byte nr = layer.regs[yidx];
if (nr != 0xff) if (nr != 0xff)
{ {
// Set neighbour when first valid neighbour is // Set neighbour when first valid neighbour is
@ -147,12 +146,12 @@ namespace DotRecast.Detour.TileCache
{ {
int idx = x + y * w; int idx = x + y * w;
if (layer.regs[idx] != 0xff) 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. // Allocate and init layer regions.
int nregs = regId; byte nregs = regId;
DtLayerMonotoneRegion[] regs = new DtLayerMonotoneRegion[nregs]; DtLayerMonotoneRegion[] regs = new DtLayerMonotoneRegion[nregs];
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
@ -167,7 +166,7 @@ namespace DotRecast.Detour.TileCache
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
int idx = x + y * w; int idx = x + y * w;
int ri = layer.regs[idx]; byte ri = layer.regs[idx];
if (ri == 0xff) if (ri == 0xff)
continue; continue;
@ -179,17 +178,17 @@ namespace DotRecast.Detour.TileCache
int ymi = x + (y - 1) * w; int ymi = x + (y - 1) * w;
if (y > 0 && IsConnected(layer, idx, ymi, walkableClimb)) if (y > 0 && IsConnected(layer, idx, ymi, walkableClimb))
{ {
int rai = layer.regs[ymi]; byte rai = layer.regs[ymi];
if (rai != 0xff && rai != ri) if (rai != 0xff && rai != ri)
{ {
AddUniqueLast(regs[ri].neis, rai); AddUniqueLast(regs[ri].neis, ref regs[ri].nneis, rai);
AddUniqueLast(regs[rai].neis, ri); 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; regs[i].regId = i;
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
@ -198,8 +197,9 @@ namespace DotRecast.Detour.TileCache
int merge = -1; int merge = -1;
int mergea = 0; 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]; DtLayerMonotoneRegion regn = regs[nei];
if (reg.regId == regn.regId) if (reg.regId == regn.regId)
continue; continue;
@ -218,7 +218,7 @@ namespace DotRecast.Detour.TileCache
if (merge != -1) if (merge != -1)
{ {
int oldId = reg.regId; int oldId = reg.regId;
int newId = regs[merge].regId; byte newId = regs[merge].regId;
for (int j = 0; j < nregs; ++j) for (int j = 0; j < nregs; ++j)
if (regs[j].regId == oldId) if (regs[j].regId == oldId)
regs[j].regId = newId; regs[j].regId = newId;
@ -226,7 +226,7 @@ namespace DotRecast.Detour.TileCache
} }
// Compact ids. // Compact ids.
int[] remap = new int[256]; Span<byte> remap = stackalloc byte[256];
// Find number of unique regions. // Find number of unique regions.
regId = 0; regId = 0;
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
@ -243,19 +243,20 @@ namespace DotRecast.Detour.TileCache
for (int i = 0; i < w * h; ++i) for (int i = 0; i < w * h; ++i)
{ {
if (layer.regs[i] != 0xff) 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<int> 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) if (n > 0 && a[n - 1] == v)
return; 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]) if (layer.areas[ia] != layer.areas[ib])
return false; return false;
@ -264,7 +265,7 @@ namespace DotRecast.Detour.TileCache
return true; 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; int count = 0;
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
@ -272,9 +273,11 @@ namespace DotRecast.Detour.TileCache
DtLayerMonotoneRegion reg = regs[i]; DtLayerMonotoneRegion reg = regs[i];
if (reg.regId != oldRegId) if (reg.regId != oldRegId)
continue; 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++; count++;
} }
} }
@ -282,7 +285,7 @@ namespace DotRecast.Detour.TileCache
return count == 1; 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. // Try to merge with existing segments.
if (cont.nverts > 1) if (cont.nverts > 1)
@ -316,7 +319,7 @@ namespace DotRecast.Detour.TileCache
cont.nverts++; 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 w = layer.header.width;
int ia = ax + ay * w; int ia = ax + ay * w;
@ -339,17 +342,17 @@ namespace DotRecast.Detour.TileCache
return layer.regs[ib]; return layer.regs[ib];
} }
private int GetDirOffsetX(int dir) public static int GetDirOffsetX(int dir)
{ {
return DirOffsetX[dir & 0x03]; return DirOffsetX[dir & 0x03];
} }
private int GetDirOffsetY(int dir) public static int GetDirOffsetY(int dir)
{ {
return DirOffsetY[dir & 0x03]; 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 w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
@ -434,7 +437,7 @@ namespace DotRecast.Detour.TileCache
cont.nverts--; 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 pqx = qx - px;
float pqz = qz - pz; float pqz = qz - pz;
@ -455,7 +458,7 @@ namespace DotRecast.Detour.TileCache
return dx * dx + dz * dz; return dx * dx + dz * dz;
} }
private void SimplifyContour(DtTempContour cont, float maxError) public static void SimplifyContour(DtTempContour cont, float maxError)
{ {
cont.poly.Clear(); 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 w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
@ -634,7 +637,7 @@ namespace DotRecast.Detour.TileCache
} }
// TODO: move this somewhere else, once the layer meshing is done. // 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 w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
@ -656,7 +659,7 @@ namespace DotRecast.Detour.TileCache
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
int idx = x + y * w; int idx = x + y * w;
int ri = layer.regs[idx]; byte ri = layer.regs[idx];
if (ri == 0xff) if (ri == 0xff)
continue; continue;
@ -711,7 +714,7 @@ namespace DotRecast.Detour.TileCache
const uint VERTEX_BUCKET_COUNT2 = (1 << 8); 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 h1 = 0x8da6b343; // Large multiplicative constants;
uint h2 = 0xd8163841; // here arbitrarily chosen primes uint h2 = 0xd8163841; // here arbitrarily chosen primes
@ -720,7 +723,7 @@ namespace DotRecast.Detour.TileCache
return (int)(n & (VERTEX_BUCKET_COUNT2 - 1)); 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 bucket = ComputeVertexHash2(x, 0, z);
int i = firstVert[bucket]; int i = firstVert[bucket];
@ -743,7 +746,7 @@ namespace DotRecast.Detour.TileCache
return i; 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) int maxVertsPerPoly)
{ {
// Based on code by Eric Lengyel from: // 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; 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; 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; 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]) return (verts[b] - verts[a]) * (verts[c + 2] - verts[a + 2])
- (verts[c] - verts[a]) * (verts[b + 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 // Returns true iff c is strictly to the left of the directed
// line through a to b. // 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; 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; 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; return Area2(verts, a, b, c) == 0;
} }
@ -995,7 +998,7 @@ namespace DotRecast.Detour.TileCache
// Returns true iff ab properly intersects cd: they share // Returns true iff ab properly intersects cd: they share
// a point interior to both segments. The properness of the // a point interior to both segments. The properness of the
// intersection is ensured by using strict leftness. // 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. // Eliminate improper cases.
if (Collinear(verts, a, b, c) || Collinear(verts, a, b, d) || Collinear(verts, c, d, a) 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 // Returns T iff (a,b,c) are collinear and point c lies
// on the closed segment ab. // 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)) if (!Collinear(verts, a, b, c))
return false; return false;
@ -1021,7 +1024,7 @@ namespace DotRecast.Detour.TileCache
} }
// Returns true iff segments ab and cd intersect, properly or improperly. // 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)) if (IntersectProp(verts, a, b, c, d))
return true; return true;
@ -1032,14 +1035,14 @@ namespace DotRecast.Detour.TileCache
return false; 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]; return verts[a] == verts[b] && verts[a + 2] == verts[b + 2];
} }
// Returns T iff (v_i, v_j) is a proper internal *or* external // 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*. // 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 d0 = (indices[i] & 0x7fff) * 4;
int d1 = (indices[j] & 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 // Returns true iff the diagonal (i,j) is strictly internal to the
// polygon P in the neighborhood of the i endpoint. // 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 pi = (indices[i] & 0x7fff) * 4;
int pj = (indices[j] & 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 // Returns T iff (v_i, v_j) is a proper internal
// diagonal of P. // 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); 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 ntris = 0;
int dst = 0; // tris; int dst = 0; // tris;
@ -1174,7 +1177,7 @@ namespace DotRecast.Detour.TileCache
return ntris; 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) for (int i = 0; i < maxVertsPerPoly; ++i)
if (polys[p + i] == DT_TILECACHE_NULL_IDX) if (polys[p + i] == DT_TILECACHE_NULL_IDX)
@ -1182,13 +1185,13 @@ namespace DotRecast.Detour.TileCache
return maxVertsPerPoly; 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]) return (verts[b] - verts[a]) * (verts[c + 2] - verts[a + 2])
- (verts[c] - verts[a]) * (verts[b + 2] - verts[a + 2]) < 0; - (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; ea = 0;
eb = 0; eb = 0;
@ -1259,7 +1262,7 @@ namespace DotRecast.Detour.TileCache
return (dx * dx) + (dy * dy); 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]; int[] tmp = new int[maxVertsPerPoly * 2];
@ -1278,19 +1281,19 @@ namespace DotRecast.Detour.TileCache
RcArrays.Copy(tmp, 0, polys, pa, maxVertsPerPoly); RcArrays.Copy(tmp, 0, polys, pa, maxVertsPerPoly);
} }
private int PushFront(int v, List<int> arr) public static int PushFront(int v, List<int> arr)
{ {
arr.Insert(0, v); arr.Insert(0, v);
return arr.Count; return arr.Count;
} }
private int PushBack(int v, List<int> arr) public static int PushBack(int v, List<int> arr)
{ {
arr.Add(v); arr.Add(v);
return arr.Count; return arr.Count;
} }
private bool CanRemoveVertex(DtTileCachePolyMesh mesh, int rem) public static bool CanRemoveVertex(DtTileCachePolyMesh mesh, int rem)
{ {
// Count number of polygons to remove. // Count number of polygons to remove.
int maxVertsPerPoly = mesh.nvp; int maxVertsPerPoly = mesh.nvp;
@ -1388,7 +1391,7 @@ namespace DotRecast.Detour.TileCache
return true; 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. // Count number of polygons to remove.
int maxVertsPerPoly = mesh.nvp; 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 maxVertices = 0;
int maxTris = 0; int maxTris = 0;
@ -1801,7 +1804,7 @@ namespace DotRecast.Detour.TileCache
return mesh; 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 bmin = new RcVec3f();
RcVec3f bmax = new RcVec3f(); RcVec3f bmax = new RcVec3f();
@ -1857,12 +1860,12 @@ namespace DotRecast.Detour.TileCache
int y = layer.heights[x + z * w]; int y = layer.heights[x + z * w];
if (y < miny || y > maxy) if (y < miny || y > maxy)
continue; 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 w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
@ -1901,12 +1904,12 @@ namespace DotRecast.Detour.TileCache
int y = layer.heights[x + z * w]; int y = layer.heights[x + z * w];
if (y < miny || y > maxy) if (y < miny || y > maxy)
continue; 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 ms = new MemoryStream();
using var bw = new BinaryWriter(ms); 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 ms = new MemoryStream();
using var bw = new BinaryWriter(ms); 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); RcByteBuffer buf = new RcByteBuffer(compressed);
buf.Order(order); buf.Order(order);
DtTileCacheLayer layer = new DtTileCacheLayer(); DtTileCacheLayer layer = new DtTileCacheLayer();
try try
{ {
layer.header = reader.Read(buf, cCompatibility); layer.header = DtTileCacheLayerHeaderReader.Read(buf, cCompatibility);
} }
catch (IOException e) catch (IOException e)
{ {
@ -1976,22 +1979,22 @@ namespace DotRecast.Detour.TileCache
int gridSize = layer.header.width * layer.header.height; int gridSize = layer.header.width * layer.header.height;
byte[] grids = comp.Decompress(compressed, buf.Position(), compressed.Length - buf.Position(), gridSize * 3); byte[] grids = comp.Decompress(compressed, buf.Position(), compressed.Length - buf.Position(), gridSize * 3);
layer.heights = new short[gridSize]; layer.heights = new byte[gridSize];
layer.areas = new short[gridSize]; layer.areas = new byte[gridSize];
layer.cons = new short[gridSize]; layer.cons = new byte[gridSize];
layer.regs = new short[gridSize]; layer.regs = new byte[gridSize];
for (int i = 0; i < gridSize; i++) for (int i = 0; i < gridSize; i++)
{ {
layer.heights[i] = (short)(grids[i] & 0xFF); layer.heights[i] = (byte)(grids[i] & 0xFF);
layer.areas[i] = (short)(grids[i + gridSize] & 0xFF); layer.areas[i] = (byte)(grids[i + gridSize] & 0xFF);
layer.cons[i] = (short)(grids[i + gridSize * 2] & 0xFF); layer.cons[i] = (byte)(grids[i + gridSize * 2] & 0xFF);
} }
return layer; return layer;
} }
public void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f center, RcVec3f extents, public static void MarkBoxArea(DtTileCacheLayer layer, RcVec3f orig, float cs, float ch, RcVec3f center, RcVec3f extents,
float[] rotAux, int areaId) float[] rotAux, byte areaId)
{ {
int w = layer.header.width; int w = layer.header.width;
int h = layer.header.height; int h = layer.header.height;
@ -2044,7 +2047,7 @@ namespace DotRecast.Detour.TileCache
int y = layer.heights[x + z * w]; int y = layer.heights[x + z * w];
if (y < miny || y > maxy) if (y < miny || y > maxy)
continue; continue;
layer.areas[x + z * w] = (short)areaId; layer.areas[x + z * w] = areaId;
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 nverts;
public int[] verts; public int[] verts;
public int reg; public byte reg;
public int area; public byte area;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 class DtTileCacheLayer
{ {
public DtTileCacheLayerHeader header; public DtTileCacheLayerHeader header;
public int regCount; public byte regCount; // < Region count.
public byte[] heights; // unsigned char
/// < Region count. public byte[] areas; // unsigned char
public short[] heights; // char public byte[] cons; // unsigned char
public byte[] regs; // unsigned char
public short[] areas; // char
public short[] cons; // char
public short[] regs; // char
} }
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -100,7 +100,6 @@ namespace DotRecast.Detour.TileCache
List<byte[]> result = new List<byte[]>(); List<byte[]> result = new List<byte[]>();
if (lset != null) if (lset != null)
{ {
DtTileCacheBuilder builder = new DtTileCacheBuilder();
for (int i = 0; i < lset.layers.Length; ++i) for (int i = 0; i < lset.layers.Length; ++i)
{ {
RcHeightfieldLayer layer = lset.layers[i]; RcHeightfieldLayer layer = lset.layers[i];
@ -128,7 +127,7 @@ namespace DotRecast.Detour.TileCache
header.hmax = layer.hmax; header.hmax = layer.hmax;
var comp = _compFactory.Create(storageParams.Compatibility ? 0 : 1); 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); result.Add(bytes);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System.Collections.Generic;
using DotRecast.Core.Numerics;
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {
public class DtTileCacheObstacle public class DtTileCacheObstacle
{ {
public readonly int index; public readonly int index;
public DtTileCacheObstacleType type;
public RcVec3f pos = new RcVec3f(); public DtObstacleCylinder cylinder = new DtObstacleCylinder();
public RcVec3f bmin = new RcVec3f(); public DtObstacleBox box = new DtObstacleBox();
public RcVec3f bmax = new RcVec3f(); public DtObstacleOrientedBox orientedBox = new DtObstacleOrientedBox();
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 List<long> touched = new List<long>(); public List<long> touched = new List<long>();
public readonly List<long> pending = new List<long>(); public readonly List<long> pending = new List<long>();
public int salt; public int salt;
public DtTileCacheObstacleType type;
public DtObstacleState state = DtObstacleState.DT_OBSTACLE_EMPTY; public DtObstacleState state = DtObstacleState.DT_OBSTACLE_EMPTY;
public DtTileCacheObstacle next; public DtTileCacheObstacle next;
public DtTileCacheObstacle(int index) public DtTileCacheObstacle(int index)
{ {
salt = 1;
this.index = index; this.index = index;
salt = 1;
} }
} }
} }

View File

@ -1,9 +1,9 @@
namespace DotRecast.Detour.TileCache namespace DotRecast.Detour.TileCache
{ {
public enum DtTileCacheObstacleType public enum DtTileCacheObstacleType
{ {
CYLINDER, DT_OBSTACLE_CYLINDER,
BOX, DT_OBSTACLE_BOX, // AABB
ORIENTED_BOX DT_OBSTACLE_ORIENTED_BOX // OBB
}; };
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 class DtTileCachePolyMesh
{ {
public int nvp; 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) public DtTileCachePolyMesh(int nvp)
{ {
this.nvp = nvp; this.nvp = nvp;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
namespace DotRecast.Detour.TileCache.Io.Compress namespace DotRecast.Detour.TileCache.Io.Compress

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 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(); DtTileCacheLayerHeader header = new DtTileCacheLayerHeader();
header.magic = data.GetInt(); header.magic = data.GetInt();

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour.TileCache.Io
{ {
public class DtTileCacheLayerHeaderWriter : DtWriter public class DtTileCacheLayerHeaderWriter
{ {
public void Write(BinaryWriter stream, DtTileCacheLayerHeader header, RcByteOrder order, bool cCompatibility) public void Write(BinaryWriter stream, DtTileCacheLayerHeader header, RcByteOrder order, bool cCompatibility)
{ {
Write(stream, header.magic, order); RcIO.Write(stream, header.magic, order);
Write(stream, header.version, order); RcIO.Write(stream, header.version, order);
Write(stream, header.tx, order); RcIO.Write(stream, header.tx, order);
Write(stream, header.ty, order); RcIO.Write(stream, header.ty, order);
Write(stream, header.tlayer, order); RcIO.Write(stream, header.tlayer, order);
Write(stream, header.bmin.X, order); RcIO.Write(stream, header.bmin.X, order);
Write(stream, header.bmin.Y, order); RcIO.Write(stream, header.bmin.Y, order);
Write(stream, header.bmin.Z, order); RcIO.Write(stream, header.bmin.Z, order);
Write(stream, header.bmax.X, order); RcIO.Write(stream, header.bmax.X, order);
Write(stream, header.bmax.Y, order); RcIO.Write(stream, header.bmax.Y, order);
Write(stream, header.bmax.Z, order); RcIO.Write(stream, header.bmax.Z, order);
Write(stream, (short)header.hmin, order); RcIO.Write(stream, (short)header.hmin, order);
Write(stream, (short)header.hmax, order); RcIO.Write(stream, (short)header.hmax, order);
Write(stream, (byte)header.width); RcIO.Write(stream, (byte)header.width);
Write(stream, (byte)header.height); RcIO.Write(stream, (byte)header.height);
Write(stream, (byte)header.minx); RcIO.Write(stream, (byte)header.minx);
Write(stream, (byte)header.maxx); RcIO.Write(stream, (byte)header.maxx);
Write(stream, (byte)header.miny); RcIO.Write(stream, (byte)header.miny);
Write(stream, (byte)header.maxy); RcIO.Write(stream, (byte)header.maxy);
if (cCompatibility) if (cCompatibility)
{ {
Write(stream, (short)0, order); // C struct padding RcIO.Write(stream, (short)0, order); // C struct padding
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public DtTileCache Read(BinaryReader @is, int maxVertPerPoly, IDtTileCacheMeshProcess meshProcessor)
{ {
RcByteBuffer bb = IOUtils.ToByteBuffer(@is); RcByteBuffer bb = RcIO.ToByteBuffer(@is);
return Read(bb, maxVertPerPoly, meshProcessor); return Read(bb, maxVertPerPoly, meshProcessor);
} }
@ -47,7 +47,7 @@ namespace DotRecast.Detour.TileCache.Io
header.magic = bb.GetInt(); header.magic = bb.GetInt();
if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC) if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC)
{ {
header.magic = IOUtils.SwapEndianness(header.magic); header.magic = RcIO.SwapEndianness(header.magic);
if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC) if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC)
{ {
throw new IOException("Invalid magic"); throw new IOException("Invalid magic");
@ -69,7 +69,8 @@ namespace DotRecast.Detour.TileCache.Io
header.numTiles = bb.GetInt(); header.numTiles = bb.GetInt();
header.meshParams = paramReader.Read(bb); header.meshParams = paramReader.Read(bb);
header.cacheParams = ReadCacheParams(bb, cCompatibility); 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); IRcCompressor comp = _compFactory.Create(cCompatibility ? 0 : 1);
DtTileCacheStorageParams storageParams = new DtTileCacheStorageParams(bb.Order(), cCompatibility); DtTileCacheStorageParams storageParams = new DtTileCacheStorageParams(bb.Order(), cCompatibility);
DtTileCache tc = new DtTileCache(header.cacheParams, storageParams, mesh, comp, meshProcessor); DtTileCache tc = new DtTileCache(header.cacheParams, storageParams, mesh, comp, meshProcessor);

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour.TileCache.Io
{ {
public class DtTileCacheWriter : DtWriter public class DtTileCacheWriter
{ {
private readonly DtNavMeshParamWriter paramWriter = new DtNavMeshParamWriter(); private readonly DtNavMeshParamWriter paramWriter = new DtNavMeshParamWriter();
private readonly DtTileCacheBuilder builder = new DtTileCacheBuilder();
private readonly IDtTileCacheCompressorFactory _compFactory; private readonly IDtTileCacheCompressorFactory _compFactory;
public DtTileCacheWriter(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) public void Write(BinaryWriter stream, DtTileCache cache, RcByteOrder order, bool cCompatibility)
{ {
Write(stream, DtTileCacheSetHeader.TILECACHESET_MAGIC, order); RcIO.Write(stream, DtTileCacheSetHeader.TILECACHESET_MAGIC, order);
Write(stream, cCompatibility RcIO.Write(stream, cCompatibility
? DtTileCacheSetHeader.TILECACHESET_VERSION ? DtTileCacheSetHeader.TILECACHESET_VERSION
: DtTileCacheSetHeader.TILECACHESET_VERSION_RECAST4J, order); : DtTileCacheSetHeader.TILECACHESET_VERSION_RECAST4J, order);
int numTiles = 0; int numTiles = 0;
@ -52,7 +51,7 @@ namespace DotRecast.Detour.TileCache.Io
numTiles++; numTiles++;
} }
Write(stream, numTiles, order); RcIO.Write(stream, numTiles, order);
paramWriter.Write(stream, cache.GetNavMesh().GetParams(), order); paramWriter.Write(stream, cache.GetNavMesh().GetParams(), order);
WriteCacheParams(stream, cache.GetParams(), order); WriteCacheParams(stream, cache.GetParams(), order);
for (int i = 0; i < cache.GetTileCount(); i++) for (int i = 0; i < cache.GetTileCount(); i++)
@ -60,32 +59,32 @@ namespace DotRecast.Detour.TileCache.Io
DtCompressedTile tile = cache.GetTile(i); DtCompressedTile tile = cache.GetTile(i);
if (tile == null || tile.data == null) if (tile == null || tile.data == null)
continue; continue;
Write(stream, (int)cache.GetTileRef(tile), order); RcIO.Write(stream, (int)cache.GetTileRef(tile), order);
byte[] data = tile.data; byte[] data = tile.data;
DtTileCacheLayer layer = cache.DecompressTile(tile); DtTileCacheLayer layer = cache.DecompressTile(tile);
var comp = _compFactory.Create(cCompatibility ? 0 : 1); var comp = _compFactory.Create(cCompatibility ? 0 : 1);
data = builder.CompressTileCacheLayer(comp, layer, order, cCompatibility); data = DtTileCacheBuilder.CompressTileCacheLayer(comp, layer, order, cCompatibility);
Write(stream, data.Length, order); RcIO.Write(stream, data.Length, order);
stream.Write(data); stream.Write(data);
} }
} }
private void WriteCacheParams(BinaryWriter stream, DtTileCacheParams option, RcByteOrder order) private void WriteCacheParams(BinaryWriter stream, DtTileCacheParams option, RcByteOrder order)
{ {
Write(stream, option.orig.X, order); RcIO.Write(stream, option.orig.X, order);
Write(stream, option.orig.Y, order); RcIO.Write(stream, option.orig.Y, order);
Write(stream, option.orig.Z, order); RcIO.Write(stream, option.orig.Z, order);
Write(stream, option.cs, order); RcIO.Write(stream, option.cs, order);
Write(stream, option.ch, order); RcIO.Write(stream, option.ch, order);
Write(stream, option.width, order); RcIO.Write(stream, option.width, order);
Write(stream, option.height, order); RcIO.Write(stream, option.height, order);
Write(stream, option.walkableHeight, order); RcIO.Write(stream, option.walkableHeight, order);
Write(stream, option.walkableRadius, order); RcIO.Write(stream, option.walkableRadius, order);
Write(stream, option.walkableClimb, order); RcIO.Write(stream, option.walkableClimb, order);
Write(stream, option.maxSimplificationError, order); RcIO.Write(stream, option.maxSimplificationError, order);
Write(stream, option.maxTiles, order); RcIO.Write(stream, option.maxTiles, order);
Write(stream, option.maxObstacles, order); RcIO.Write(stream, option.maxObstacles, order);
} }
} }
} }

View File

@ -1,9 +1,11 @@
namespace DotRecast.Detour using DotRecast.Core.Numerics;
namespace DotRecast.Detour
{ {
public class BVItem public class BVItem
{ {
public readonly int[] bmin = new int[3]; public RcVec3i bmin;
public readonly int[] bmax = new int[3]; public RcVec3i bmax;
public int i; public int i;
}; };
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
@ -12,7 +12,7 @@ namespace DotRecast.Detour
public int Compare(BVItem a, BVItem b) public int Compare(BVItem a, BVItem b)
{ {
return a.bmin[0].CompareTo(b.bmin[0]); return a.bmin.X.CompareTo(b.bmin.X);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
@ -12,7 +12,7 @@ namespace DotRecast.Detour
public int Compare(BVItem a, BVItem b) public int Compare(BVItem a, BVItem b)
{ {
return a.bmin[1].CompareTo(b.bmin[1]); return a.bmin.Y.CompareTo(b.bmin.Y);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
@ -12,7 +12,7 @@ namespace DotRecast.Detour
public int Compare(BVItem a, BVItem b) public int Compare(BVItem a, BVItem b)
{ {
return a.bmin[2].CompareTo(b.bmin[2]); return a.bmin.Z.CompareTo(b.bmin.Z);
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/** /// Bounding volume node.
* Bounding volume node. /// @note This structure is rarely if ever used by the end user.
* /// @see dtMeshTile
* @note This structure is rarely if ever used by the end user.
* @see MeshTile
*/
[Serializable]
public class DtBVNode public class DtBVNode
{ {
/** Minimum bounds of the node's AABB. [(x, y, z)] */ public RcVec3i bmin; //< Minimum bounds of the node's AABB. [(x, y, z)]
public int[] bmin = new int[3]; public RcVec3i bmax; //< Maximum bounds of the node's AABB. [(x, y, z)]
public int i; //< The node's index. (Negative for escape sequence.)
/** 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;
} }
} }

View File

@ -0,0 +1,22 @@
using System;
namespace DotRecast.Detour
{
public class DtCallbackPolyQuery : IDtPolyQuery
{
private readonly Action<DtMeshTile, DtPoly, long> _callback;
public DtCallbackPolyQuery(Action<DtMeshTile, DtPoly, long> callback)
{
_callback = callback;
}
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
{
for (int i = 0; i < count; ++i)
{
_callback?.Invoke(tile, poly[i], refs[i]);
}
}
}
}

View File

@ -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<long> refs, int count)
{
int numLeft = m_maxPolys - m_numCollected;
int toCopy = count;
if (toCopy > numLeft)
{
m_overflow = true;
toCopy = numLeft;
}
RcSpans.Copy<long>(refs, 0, m_polys, m_numCollected, toCopy);
m_numCollected += toCopy;
}
}
}

View File

@ -1,4 +1,4 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public enum DtConvexConvexInFlag public enum DtConvexConvexInFlag
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public enum DtConvexConvexIntersection public enum DtConvexConvexIntersection
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System;
using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour 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 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<float> p, Span<float> q)
{ {
int n = p.Length / 3; int n = p.Length / 3;
int m = q.Length / 3; int m = q.Length / 3;
float[] inters = new float[Math.Max(m, n) * 3 * 3]; Span<float> inters = stackalloc float[Math.Max(m, n) * 3 * 3];
int ii = 0; int ii = 0;
/* Initialize variables. */ /* Initialize variables. */
RcVec3f a = new RcVec3f(); RcVec3f a = new RcVec3f();
@ -54,10 +51,10 @@ namespace DotRecast.Detour
do do
{ {
a = RcVecUtils.Create(p, 3 * (ai % n)); a = RcVec.Create(p, 3 * (ai % n));
b = RcVecUtils.Create(q, 3 * (bi % m)); b = RcVec.Create(q, 3 * (bi % m));
a1 = RcVecUtils.Create(p, 3 * ((ai + n - 1) % n)); // prev a a1 = RcVec.Create(p, 3 * ((ai + n - 1) % n)); // prev a
b1 = RcVecUtils.Create(q, 3 * ((bi + m - 1) % m)); // prev b b1 = RcVec.Create(q, 3 * ((bi + m - 1) % m)); // prev b
RcVec3f A = RcVec3f.Subtract(a, a1); RcVec3f A = RcVec3f.Subtract(a, a1);
RcVec3f B = RcVec3f.Subtract(b, b1); RcVec3f B = RcVec3f.Subtract(b, b1);
@ -171,12 +168,11 @@ namespace DotRecast.Detour
return null; return null;
} }
float[] copied = new float[ii]; float[] copied = inters.Slice(0, ii).ToArray();
RcArrays.Copy(inters, copied, ii);
return copied; return copied;
} }
private static int AddVertex(float[] inters, int ii, RcVec3f p) private static int AddVertex(Span<float> inters, int ii, RcVec3f p)
{ {
if (ii > 0) if (ii > 0)
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public static class DtDetailTriEdgeFlags public static class DtDetailTriEdgeFlags
{ {

View File

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

View File

@ -7,27 +7,31 @@ namespace DotRecast.Detour
{ {
private readonly DtNavMeshQuery _query; private readonly DtNavMeshQuery _query;
private readonly RcVec3f _center; private readonly RcVec3f _center;
private long _nearestRef;
private RcVec3f _nearestPt;
private bool _overPoly;
private float _nearestDistanceSqr; private float _nearestDistanceSqr;
private long _nearestRef;
private RcVec3f _nearestPoint;
private bool _overPoly;
public DtFindNearestPolyQuery(DtNavMeshQuery query, RcVec3f center) public DtFindNearestPolyQuery(DtNavMeshQuery query, RcVec3f center)
{ {
this._query = query; _query = query;
this._center = center; _center = center;
_nearestDistanceSqr = float.MaxValue; _nearestDistanceSqr = float.MaxValue;
_nearestPt = center; _nearestPoint = center;
} }
public void Process(DtMeshTile tile, DtPoly poly, long refs) public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
{ {
for (int i = 0; i < count; ++i)
{
long polyRef = refs[i];
float d;
// Find nearest polygon amongst the nearby polygons. // Find nearest polygon amongst the nearby polygons.
_query.ClosestPointOnPoly(refs, _center, out var closestPtPoly, out var posOverPoly); _query.ClosestPointOnPoly(polyRef, _center, out var closestPtPoly, out var posOverPoly);
// If a point is directly over a polygon and closer than // If a point is directly over a polygon and closer than
// climb height, favor that instead of straight line nearest point. // climb height, favor that instead of straight line nearest point.
float d = 0;
RcVec3f diff = RcVec3f.Subtract(_center, closestPtPoly); RcVec3f diff = RcVec3f.Subtract(_center, closestPtPoly);
if (posOverPoly) if (posOverPoly)
{ {
@ -41,12 +45,13 @@ namespace DotRecast.Detour
if (d < _nearestDistanceSqr) if (d < _nearestDistanceSqr)
{ {
_nearestPt = closestPtPoly; _nearestPoint = closestPtPoly;
_nearestDistanceSqr = d; _nearestDistanceSqr = d;
_nearestRef = refs; _nearestRef = polyRef;
_overPoly = posOverPoly; _overPoly = posOverPoly;
} }
} }
}
public long NearestRef() public long NearestRef()
{ {
@ -55,7 +60,7 @@ namespace DotRecast.Detour
public RcVec3f NearestPt() public RcVec3f NearestPt()
{ {
return _nearestPt; return _nearestPoint;
} }
public bool OverPoly() public bool OverPoly()

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public readonly struct DtFindPathOption public readonly struct DtFindPathOption
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath /// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
public static class DtFindPathOptions public static class DtFindPathOptions

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/** /// Defines a link between polygons.
* Defines a link between polygons. /// @note This structure is rarely if ever used by the end user.
* /// @see dtMeshTile
* @note This structure is rarely if ever used by the end user.
* @see MeshTile
*/
public class DtLink public class DtLink
{ {
/** Neighbour reference. (The neighbor that is linked to.) */ public long refs; //< Neighbour reference. (The neighbor that is linked to.)
public long refs; public int next; //< Index of the next link.
public byte edge; //< Index of the polygon edge that owns this link.
/** Index of the next link. */ public byte side; //< If a boundary link, defines on which side the link is.
public int next; 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.
/** 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;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,33 +25,22 @@ namespace DotRecast.Detour
[Serializable] [Serializable]
public class DtMeshData public class DtMeshData
{ {
/** The tile header. */ public DtMeshHeader header; //< The tile header.
public DtMeshHeader 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] */ /// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
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] */
public float[] detailVerts; public float[] detailVerts;
/** /// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount].
* The detail mesh's triangles. [(vertA, vertB, vertC) * MeshHeader::detailTriCount] See DetailTriEdgeFlags and /// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags.
* NavMesh::getDetailTriEdgeFlags.
*/
public int[] detailTris; public int[] detailTris;
/** /// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
* The tile bounding volume nodes. [Size: MeshHeader::bvNodeCount] (Will be null if bounding volumes are disabled.) /// (Will be null if bounding volumes are disabled.)
*/
public DtBVNode[] bvTree; public DtBVNode[] bvTree;
/** The tile off-mesh connections. [Size: MeshHeader::offMeshConCount] */ public DtOffMeshConnection[] offMeshCons; //< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
public DtOffMeshConnection[] offMeshCons;
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System.Collections.Generic;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/** using static DtDetour;
* Defines a navigation mesh tile.
*/ /// Defines a navigation mesh tile.
/// @ingroup detour
public class DtMeshTile 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 flags; //< Tile flags. (See: #dtTileFlags)
public int salt; public DtMeshTile next; //< The next free tile, or the next tile in the spatial grid.
/** The tile data. */
public DtMeshData data;
public int[] polyLinks;
/** The tile links. */
public readonly List<DtLink> links = new List<DtLink>();
/** Index to the next free link. */
public int linksFreeList = DtNavMesh.DT_NULL_LINK; // FIXME: Remove
/** Tile flags. (See: #dtTileFlags) */
public int flags;
public DtMeshTile(int index) public DtMeshTile(int index)
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Buffers;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
using static DtDetour;
/// A navigation mesh based on tiles of convex polygons.
/// @ingroup detour
public class DtNavMesh public class DtNavMesh
{ {
/** A magic number used to detect compatibility of navigation tile data. */ private DtNavMeshParams m_params; //< Current initialization params. TODO: do not store this info twice.
public const int DT_NAVMESH_MAGIC = 'D' << 24 | 'N' << 16 | 'A' << 8 | 'V'; 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. */ private DtMeshTile[] m_posLookup; //< Tile hash lookup.
public const int DT_NAVMESH_VERSION = 7; 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<int, List<DtMeshTile>> posLookup = new Dictionary<int, List<DtMeshTile>>();
private readonly LinkedList<DtMeshTile> availableTiles = new LinkedList<DtMeshTile>();
private readonly DtMeshTile[] m_tiles;
/// < List of tiles.
/** The maximum number of vertices per navigation polygon. */ /** The maximum number of vertices per navigation polygon. */
private readonly int m_maxVertPerPoly; private int m_maxVertPerPoly;
private int m_tileCount; private int m_tileCount;
public DtNavMesh(DtMeshData data, int maxVertsPerPoly, int flags) public DtStatus Init(DtNavMeshParams param, int maxVertsPerPoly)
: this(GetNavMeshParams(data), 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 // Init tiles
m_maxTiles = option.maxTiles;
m_maxVertPerPoly = maxVertsPerPoly; 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]; 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] = new DtMeshTile(i);
m_tiles[i].salt = 1; 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) private static DtNavMeshParams GetNavMeshParams(DtMeshData data)
@ -169,76 +135,10 @@ namespace DotRecast.Detour
return EncodePolyId(tile.salt, it, 0); 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) private int AllocLink(DtMeshTile tile)
{ {
if (tile.linksFreeList == DT_NULL_LINK) if (tile.linksFreeList == DT_NULL_LINK)
{ return DT_NULL_LINK;
DtLink link = new DtLink();
link.next = DT_NULL_LINK;
tile.links.Add(link);
return tile.links.Count - 1;
}
int linkIdx = tile.linksFreeList; int linkIdx = tile.linksFreeList;
tile.linksFreeList = tile.links[linkIdx].next; tile.linksFreeList = tile.links[linkIdx].next;
@ -359,8 +259,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax; var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor; float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box // Calculate quantized box
Span<int> bmin = stackalloc int[3]; RcVec3i bmin;
Span<int> bmax = stackalloc int[3]; RcVec3i bmax;
// dtClamp query box to world box. // dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; 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 maxy = Math.Clamp(qmax.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
float maxz = Math.Clamp(qmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z; float maxz = Math.Clamp(qmax.Z, tbmin.Z, tbmax.Z) - tbmin.Z;
// Quantize // Quantize
bmin[0] = (int)(qfac * minx) & 0x7ffffffe; bmin.X = (int)(qfac * minx) & 0x7ffffffe;
bmin[1] = (int)(qfac * miny) & 0x7ffffffe; bmin.Y = (int)(qfac * miny) & 0x7ffffffe;
bmin[2] = (int)(qfac * minz) & 0x7ffffffe; bmin.Z = (int)(qfac * minz) & 0x7ffffffe;
bmax[0] = (int)(qfac * maxx + 1) | 1; bmax.X = (int)(qfac * maxx + 1) | 1;
bmax[1] = (int)(qfac * maxy + 1) | 1; bmax.Y = (int)(qfac * maxy + 1) | 1;
bmax[2] = (int)(qfac * maxz + 1) | 1; bmax.Z = (int)(qfac * maxz + 1) | 1;
// Traverse tree // Traverse tree
long @base = GetPolyRefBase(tile); long @base = GetPolyRefBase(tile);
@ -382,7 +282,7 @@ namespace DotRecast.Detour
while (nodeIndex < end) while (nodeIndex < end)
{ {
DtBVNode node = tile.data.bvTree[nodeIndex]; 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; bool isLeafNode = node.i >= 0;
if (isLeafNode && overlap) if (isLeafNode && overlap)
@ -419,13 +319,13 @@ namespace DotRecast.Detour
// Calc polygon bounds. // Calc polygon bounds.
int v = p.verts[0] * 3; int v = p.verts[0] * 3;
bmin = RcVecUtils.Create(tile.data.verts, v); bmin = RcVec.Create(tile.data.verts, v);
bmax = RcVecUtils.Create(tile.data.verts, v); bmax = RcVec.Create(tile.data.verts, v);
for (int j = 1; j < p.vertCount; ++j) for (int j = 1; j < p.vertCount; ++j)
{ {
v = p.verts[j] * 3; v = p.verts[j] * 3;
bmin = RcVecUtils.Min(bmin, tile.data.verts, v); bmin = RcVec3f.Min(bmin, RcVec.Create(tile.data.verts, v));
bmax = RcVecUtils.Max(bmax, tile.data.verts, v); bmax = RcVec3f.Max(bmax, RcVec.Create(tile.data.verts, v));
} }
if (DtUtils.OverlapBounds(qmin, qmax, bmin, bmax)) if (DtUtils.OverlapBounds(qmin, qmax, bmin, bmax))
@ -438,30 +338,20 @@ 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); long refs = GetTileRefAt(data.header.x, data.header.y, data.header.layer);
refs = RemoveTile(refs); 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 /// @par
/// ///
/// The add operation will fail if the data is in the wrong format, the /// The add operation will fail if the data is in the wrong format, the allocated tile
/// allocated tile
/// space is full, or there is a tile already at the specified reference. /// 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 /// 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 /// 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 /// tile will be restored to the same values they were before the tile was
/// removed. /// removed.
/// ///
@ -471,64 +361,91 @@ namespace DotRecast.Detour
/// removed from this nav mesh. /// removed from this nav mesh.
/// ///
/// @see dtCreateNavMeshData, #removeTile /// @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. // Make sure the data is in right format.
DtMeshHeader header = data.header; DtMeshHeader header = data.header;
// Make sure the location is free. // Make sure the location is free.
if (GetTileAt(header.x, header.y, header.layer) != null) 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. // Allocate a tile.
DtMeshTile tile = null; DtMeshTile tile = null;
if (lastRef == 0) if (lastRef == 0)
{ {
// Make sure we could allocate a tile. if (null != m_nextFree)
if (0 == availableTiles.Count)
{ {
throw new Exception("Could not allocate a tile"); tile = m_nextFree;
} m_nextFree = tile.next;
tile.next = null;
tile = availableTiles.First?.Value;
availableTiles.RemoveFirst();
m_tileCount++; m_tileCount++;
} }
}
else else
{ {
// Try to relocate the tile to specific index with same salt. // Try to relocate the tile to specific index with same salt.
int tileIndex = DecodePolyIdTile(lastRef); int tileIndex = DecodePolyIdTile(lastRef);
if (tileIndex >= m_maxTiles) 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. // Try to find the specific tile id from the free list.
DtMeshTile target = m_tiles[tileIndex]; DtMeshTile target = m_tiles[tileIndex];
// Remove from freelist DtMeshTile prev = null;
if (!availableTiles.Remove(target)) tile = m_nextFree;
while (null != tile && tile != target)
{ {
// Could not find the correct location. prev = tile;
throw new Exception("Could not find 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. // Restore salt.
tile.salt = DecodePolyIdSalt(lastRef); tile.salt = DecodePolyIdSalt(lastRef);
} }
tile.data = data; // Make sure we could allocate a tile.
tile.flags = flags; if (null == tile)
tile.links.Clear(); {
tile.polyLinks = new int[data.polys.Length]; return DtStatus.DT_FAILURE | DtStatus.DT_OUT_OF_MEMORY;
Array.Fill(tile.polyLinks, DtNavMesh.DT_NULL_LINK); }
// Insert tile into the position lut. // 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. // 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 there are no items in the bvtree, reset the tree pointer.
if (tile.data.bvTree != null && tile.data.bvTree.Length == 0) if (tile.data.bvTree != null && tile.data.bvTree.Length == 0)
@ -536,16 +453,29 @@ namespace DotRecast.Detour
tile.data.bvTree = null; 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. // Init tile.
tile.flags = flags;
ConnectIntLinks(tile); ConnectIntLinks(tile);
// Base off-mesh connections to their starting polygons and connect connections inside the tile. // Base off-mesh connections to their starting polygons and connect connections inside the tile.
BaseOffMeshLinks(tile); BaseOffMeshLinks(tile);
ConnectExtOffMeshLinks(tile, tile, -1); 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. // Connect with layers in current tile.
List<DtMeshTile> neis = GetTilesAt(header.x, header.y); nneis = GetTilesAt(header.x, header.y, neis, MAX_NEIS);
for (int j = 0; j < neis.Count; ++j) for (int j = 0; j < nneis; ++j)
{ {
if (neis[j] == tile) if (neis[j] == tile)
{ {
@ -561,8 +491,8 @@ namespace DotRecast.Detour
// Connect with neighbour tiles. // Connect with neighbour tiles.
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
neis = GetNeighbourTilesAt(header.x, header.y, i); nneis = GetNeighbourTilesAt(header.x, header.y, i, neis, MAX_NEIS);
for (int j = 0; j < neis.Count; ++j) for (int j = 0; j < nneis; ++j)
{ {
ConnectExtLinks(tile, neis[j], i); ConnectExtLinks(tile, neis[j], i);
ConnectExtLinks(neis[j], tile, DtUtils.OppositeTile(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. /// Removes the specified tile from the navigation mesh.
@ -604,39 +535,52 @@ namespace DotRecast.Detour
} }
// Remove tile from hash lookup. // Remove tile from hash lookup.
GetTileListByPos(tile.data.header.x, tile.data.header.y).Remove(tile); int h = ComputeTileHash(tile.data.header.x, tile.data.header.y, m_tileLutMask);
DtMeshTile prev = null;
// Remove connections to neighbour tiles. DtMeshTile cur = m_posLookup[h];
// Create connections with neighbour tiles. while (null != cur)
// Disconnect from other layers in current tile.
List<DtMeshTile> nneis = GetTilesAt(tile.data.header.x, tile.data.header.y);
foreach (DtMeshTile j in nneis)
{ {
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. // Disconnect from neighbour tiles.
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i); nneis = GetNeighbourTilesAt(tile.data.header.x, tile.data.header.y, i, neis, MAX_NEIS);
foreach (DtMeshTile j in nneis) for (int j = 0; j < nneis; ++j)
{ {
UnconnectLinks(j, tile); UnconnectLinks(neis[j], tile);
} }
} }
// Reset tile. // Reset tile.
tile.data = null; tile.data = null;
tile.flags = 0; tile.flags = 0;
tile.links.Clear(); tile.links = null;
tile.linksFreeList = DtNavMesh.DT_NULL_LINK; tile.linksFreeList = DT_NULL_LINK;
// Update salt, salt should never be zero. // Update salt, salt should never be zero.
tile.salt = (tile.salt + 1) & ((1 << DT_SALT_BITS) - 1); tile.salt = (tile.salt + 1) & ((1 << DT_SALT_BITS) - 1);
@ -646,7 +590,8 @@ namespace DotRecast.Detour
} }
// Add to free list. // Add to free list.
availableTiles.AddFirst(tile); tile.next = m_nextFree;
m_nextFree = tile;
m_tileCount--; m_tileCount--;
return GetTileRef(tile); return GetTileRef(tile);
} }
@ -664,7 +609,7 @@ namespace DotRecast.Detour
for (int i = 0; i < tile.data.header.polyCount; ++i) for (int i = 0; i < tile.data.header.polyCount; ++i)
{ {
DtPoly poly = tile.data.polys[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) if (poly.GetPolyType() == DtPolyTypes.DT_POLYTYPE_OFFMESH_CONNECTION)
{ {
@ -684,17 +629,17 @@ namespace DotRecast.Detour
int idx = AllocLink(tile); int idx = AllocLink(tile);
DtLink link = tile.links[idx]; DtLink link = tile.links[idx];
link.refs = @base | (long)(poly.neis[j] - 1); link.refs = @base | (long)(poly.neis[j] - 1);
link.edge = j; link.edge = (byte)j;
link.side = 0xff; link.side = 0xff;
link.bmin = link.bmax = 0; link.bmin = link.bmax = 0;
// Add to linked list. // Add to linked list.
link.next = tile.polyLinks[poly.index]; link.next = poly.firstLink;
tile.polyLinks[poly.index] = idx; poly.firstLink = idx;
} }
} }
} }
/// Removes external links at specified side.V /// Removes external links at specified side.
void UnconnectLinks(DtMeshTile tile, DtMeshTile target) void UnconnectLinks(DtMeshTile tile, DtMeshTile target)
{ {
if (tile == null || target == null) if (tile == null || target == null)
@ -707,7 +652,7 @@ namespace DotRecast.Detour
for (int i = 0; i < tile.data.header.polyCount; ++i) for (int i = 0; i < tile.data.header.polyCount; ++i)
{ {
DtPoly poly = tile.data.polys[i]; DtPoly poly = tile.data.polys[i];
int j = tile.polyLinks[poly.index]; int j = poly.firstLink;
int pj = DT_NULL_LINK; int pj = DT_NULL_LINK;
while (j != DT_NULL_LINK) while (j != DT_NULL_LINK)
{ {
@ -717,7 +662,7 @@ namespace DotRecast.Detour
int nj = tile.links[j].next; int nj = tile.links[j].next;
if (pj == DT_NULL_LINK) if (pj == DT_NULL_LINK)
{ {
tile.polyLinks[poly.index] = nj; poly.firstLink = nj;
} }
else else
{ {
@ -777,13 +722,15 @@ namespace DotRecast.Detour
foreach (var connectPoly in connectPolys) foreach (var connectPoly in connectPolys)
{ {
int idx = AllocLink(tile); int idx = AllocLink(tile);
if (idx != DT_NULL_LINK)
{
DtLink link = tile.links[idx]; DtLink link = tile.links[idx];
link.refs = connectPoly.refs; link.refs = connectPoly.refs;
link.edge = j; link.edge = (byte)j;
link.side = dir; link.side = (byte)dir;
link.next = tile.polyLinks[poly.index]; link.next = poly.firstLink;
tile.polyLinks[poly.index] = idx; poly.firstLink = idx;
// Compress portal limits to a byte value. // Compress portal limits to a byte value.
if (dir == 0 || dir == 4) if (dir == 0 || dir == 4)
@ -799,8 +746,8 @@ namespace DotRecast.Detour
tmax = temp; tmax = temp;
} }
link.bmin = (int)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); link.bmin = (byte)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); link.bmax = (byte)MathF.Round(Math.Clamp(tmax, 0.0f, 1.0f) * 255.0f);
} }
else if (dir == 2 || dir == 6) else if (dir == 2 || dir == 6)
{ {
@ -815,8 +762,9 @@ namespace DotRecast.Detour
tmax = temp; tmax = temp;
} }
link.bmin = (int)MathF.Round(Math.Clamp(tmin, 0.0f, 1.0f) * 255.0f); link.bmin = (byte)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); 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]; DtPoly targetPoly = target.data.polys[targetCon.poly];
// Skip off-mesh connections which start location could not be // Skip off-mesh connections which start location could not be
// connected at all. // connected at all.
if (target.polyLinks[targetPoly.index] == DT_NULL_LINK) if (targetPoly.firstLink == DT_NULL_LINK)
{ {
continue; continue;
} }
@ -884,11 +832,11 @@ namespace DotRecast.Detour
DtLink link = target.links[idx]; DtLink link = target.links[idx];
link.refs = refs; link.refs = refs;
link.edge = 1; link.edge = 1;
link.side = oppositeSide; link.side = (byte)oppositeSide;
link.bmin = link.bmax = 0; link.bmin = link.bmax = 0;
// Add to linked list. // Add to linked list.
link.next = target.polyLinks[targetPoly.index]; link.next = targetPoly.firstLink;
target.polyLinks[targetPoly.index] = idx; targetPoly.firstLink = idx;
// Link target poly to off-mesh connection. // Link target poly to off-mesh connection.
if ((targetCon.flags & DT_OFFMESH_CON_BIDIR) != 0) if ((targetCon.flags & DT_OFFMESH_CON_BIDIR) != 0)
@ -899,11 +847,11 @@ namespace DotRecast.Detour
link = tile.links[tidx]; link = tile.links[tidx];
link.refs = GetPolyRefBase(target) | (long)targetCon.poly; link.refs = GetPolyRefBase(target) | (long)targetCon.poly;
link.edge = 0xff; link.edge = 0xff;
link.side = (side == -1 ? 0xff : side); link.side = (byte)(side == -1 ? 0xff : side);
link.bmin = link.bmax = 0; link.bmin = link.bmax = 0;
// Add to linked list. // Add to linked list.
link.next = tile.polyLinks[landPoly.index]; link.next = landPoly.firstLink;
tile.polyLinks[landPoly.index] = tidx; landPoly.firstLink = tidx;
} }
} }
} }
@ -970,59 +918,7 @@ namespace DotRecast.Detour
return n; return n;
} }
static float GetSlabCoord(float[] verts, int va, int side) private bool OverlapSlabs(RcVec2f amin, RcVec2f amax, RcVec2f bmin, RcVec2f bmax, float px, float py)
{
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)
{ {
// Check for horizontal overlap. // Check for horizontal overlap.
// The segment is shrunken a little so that slabs which touch // The segment is shrunken a little so that slabs which touch
@ -1113,8 +1009,8 @@ namespace DotRecast.Detour
link.side = 0xff; link.side = 0xff;
link.bmin = link.bmax = 0; link.bmin = link.bmax = 0;
// Add to linked list. // Add to linked list.
link.next = tile.polyLinks[poly.index]; link.next = poly.firstLink;
tile.polyLinks[poly.index] = idx; poly.firstLink = idx;
// Start end-point is always connect back to off-mesh connection. // Start end-point is always connect back to off-mesh connection.
int tidx = AllocLink(tile); int tidx = AllocLink(tile);
@ -1126,8 +1022,8 @@ namespace DotRecast.Detour
link.side = 0xff; link.side = 0xff;
link.bmin = link.bmax = 0; link.bmin = link.bmax = 0;
// Add to linked list. // Add to linked list.
link.next = tile.polyLinks[landPoly.index]; link.next = landPoly.firstLink;
tile.polyLinks[landPoly.index] = tidx; landPoly.firstLink = tidx;
} }
} }
@ -1149,6 +1045,7 @@ namespace DotRecast.Detour
RcVec3f pmin = new RcVec3f(); RcVec3f pmin = new RcVec3f();
RcVec3f pmax = new RcVec3f(); RcVec3f pmax = new RcVec3f();
Span<RcVec3f> tempV = stackalloc RcVec3f[3];
if (tile.data.detailMeshes != null) if (tile.data.detailMeshes != null)
{ {
ref DtPolyDetail pd = ref tile.data.detailMeshes[ip]; ref DtPolyDetail pd = ref tile.data.detailMeshes[ip];
@ -1161,7 +1058,7 @@ namespace DotRecast.Detour
continue; continue;
} }
RcVec3f[] v = new RcVec3f[3]; Span<RcVec3f> v = tempV;
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
{ {
if (tris[ti + j] < poly.vertCount) if (tris[ti + j] < poly.vertCount)
@ -1209,7 +1106,7 @@ namespace DotRecast.Detour
} }
else else
{ {
RcVec3f[] v = new RcVec3f[2]; Span<RcVec3f> v = tempV.Slice(0, 2);
for (int j = 0; j < poly.vertCount; ++j) for (int j = 0; j < poly.vertCount; ++j)
{ {
int k = (j + 1) % poly.vertCount; int k = (j + 1) % poly.vertCount;
@ -1247,26 +1144,27 @@ namespace DotRecast.Detour
int ip = poly.index; int ip = poly.index;
using var verts = RcRentedArray.Rent<float>(m_maxVertPerPoly * 3); Span<float> verts = stackalloc float[m_maxVertPerPoly * 3];
int nv = poly.vertCount; int nv = poly.vertCount;
for (int i = 0; i < nv; ++i) 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; return false;
} }
// Find height at the location. // Find height at the location.
Span<RcVec3f> tempV = stackalloc RcVec3f[3];
if (tile.data.detailMeshes != null) if (tile.data.detailMeshes != null)
{ {
ref DtPolyDetail pd = ref tile.data.detailMeshes[ip]; ref DtPolyDetail pd = ref tile.data.detailMeshes[ip];
for (int j = 0; j < pd.triCount; ++j) for (int j = 0; j < pd.triCount; ++j)
{ {
int t = (pd.triBase + j) * 4; int t = (pd.triBase + j) * 4;
RcVec3f[] v = new RcVec3f[3]; Span<RcVec3f> v = tempV;
for (int k = 0; k < 3; ++k) for (int k = 0; k < 3; ++k)
{ {
if (tile.data.detailTris[t + k] < poly.vertCount) if (tile.data.detailTris[t + k] < poly.vertCount)
@ -1300,7 +1198,7 @@ namespace DotRecast.Detour
} }
else else
{ {
RcVec3f[] v = new RcVec3f[3]; Span<RcVec3f> v = tempV;
v[0].X = tile.data.verts[poly.verts[0] * 3]; v[0].X = tile.data.verts[poly.verts[0] * 3];
v[0].Y = tile.data.verts[poly.verts[0] * 3 + 1]; v[0].Y = tile.data.verts[poly.verts[0] * 3 + 1];
v[0].Z = tile.data.verts[poly.verts[0] * 3 + 2]; 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) 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 if (null != tile.data &&
&& tile.data.header.layer == layer) null != tile.data.header &&
tile.data.header.x == x &&
tile.data.header.y == y &&
tile.data.header.layer == layer)
{ {
return tile; return tile;
} }
tile = tile.next;
} }
return null; return null;
} }
List<DtMeshTile> GetNeighbourTilesAt(int x, int y, int side) int GetNeighbourTilesAt(int x, int y, int side, DtMeshTile[] tiles, int maxTiles)
{ {
int nx = x, ny = y; int nx = x, ny = y;
switch (side) switch (side)
@ -1455,21 +1361,31 @@ namespace DotRecast.Detour
break; break;
} }
return GetTilesAt(nx, ny); return GetTilesAt(nx, ny, tiles, maxTiles);
} }
public List<DtMeshTile> GetTilesAt(int x, int y) public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
{ {
List<DtMeshTile> tiles = new List<DtMeshTile>(); int n = 0;
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) 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;
} }
return tiles; tile = tile.next;
}
return n;
} }
public long GetTileRefAt(int x, int y, int layer) public long GetTileRefAt(int x, int y, int layer)
@ -1510,14 +1426,6 @@ namespace DotRecast.Detour
return EncodePolyId(tile.salt, tile.index, 0); 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". /// 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] prevRef The reference of the polygon before the connection.
/// @param[in] polyRef The reference of the off-mesh connection polygon. /// @param[in] polyRef The reference of the off-mesh connection polygon.
@ -1569,7 +1477,7 @@ namespace DotRecast.Detour
int idx0 = 0, idx1 = 1; int idx0 = 0, idx1 = 1;
// Find link that points to first vertex. // 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) if (tile.links[i].edge == 0)
{ {
@ -1583,8 +1491,8 @@ namespace DotRecast.Detour
} }
} }
startPos = RcVecUtils.Create(tile.data.verts, poly.verts[idx0] * 3); startPos = RcVec.Create(tile.data.verts, poly.verts[idx0] * 3);
endPos = RcVecUtils.Create(tile.data.verts, poly.verts[idx1] * 3); endPos = RcVec.Create(tile.data.verts, poly.verts[idx1] * 3);
return DtStatus.DT_SUCCESS; return DtStatus.DT_SUCCESS;
} }
@ -1599,9 +1507,9 @@ namespace DotRecast.Detour
return m_tileCount; return m_tileCount;
} }
public int GetAvailableTileCount() public bool IsAvailableTileCount()
{ {
return availableTiles.Count; return null != m_nextFree;
} }
public DtStatus SetPolyFlags(long refs, int flags) public DtStatus SetPolyFlags(long refs, int flags)
@ -1759,32 +1667,6 @@ namespace DotRecast.Detour
return center; 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<DtMeshTile> GetTileListByPos(int x, int z)
{
var tileHash = ComputeTileHash(x, z, m_tileLutMask);
if (!posLookup.TryGetValue(tileHash, out var tiles))
{
tiles = new List<DtMeshTile>();
posLookup.Add(tileHash, tiles);
}
return tiles;
}
public void ComputeBounds(out RcVec3f bmin, out RcVec3f bmax) public void ComputeBounds(out RcVec3f bmin, out RcVec3f bmax)
{ {
bmin = new RcVec3f(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); bmin = new RcVec3f(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,42 +24,35 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
using static DtDetour;
public static class DtNavMeshBuilder public static class DtNavMeshBuilder
{ {
const int MESH_NULL_IDX = 0xffff; 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]; bmin = items[imin].bmin;
int[] bmax = new int[3]; bmax = items[imin].bmax;
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];
for (int i = imin + 1; i < imax; ++i) for (int i = imin + 1; i < imax; ++i)
{ {
BVItem it = items[i]; BVItem it = items[i];
if (it.bmin[0] < bmin[0]) if (it.bmin.X < bmin.X)
bmin[0] = it.bmin[0]; bmin.X = it.bmin.X;
if (it.bmin[1] < bmin[1]) if (it.bmin.Y < bmin.Y)
bmin[1] = it.bmin[1]; bmin.Y = it.bmin.Y;
if (it.bmin[2] < bmin[2]) if (it.bmin.Z < bmin.Z)
bmin[2] = it.bmin[2]; bmin.Z = it.bmin.Z;
if (it.bmax[0] > bmax[0]) if (it.bmax.X > bmax.X)
bmax[0] = it.bmax[0]; bmax.X = it.bmax.X;
if (it.bmax[1] > bmax[1]) if (it.bmax.Y > bmax.Y)
bmax[1] = it.bmax[1]; bmax.Y = it.bmax.Y;
if (it.bmax[2] > bmax[2]) if (it.bmax.Z > bmax.Z)
bmax[2] = it.bmax[2]; bmax.Z = it.bmax.Z;
} }
return new int[][] { bmin, bmax };
} }
private static int LongestAxis(int x, int y, int z) private static int LongestAxis(int x, int y, int z)
@ -92,25 +85,21 @@ namespace DotRecast.Detour
if (inum == 1) if (inum == 1)
{ {
// Leaf // Leaf
node.bmin[0] = items[imin].bmin[0]; node.bmin = items[imin].bmin;
node.bmin[1] = items[imin].bmin[1]; node.bmax = items[imin].bmax;
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.i = items[imin].i; node.i = items[imin].i;
} }
else else
{ {
// Split // Split
int[][] minmax = CalcExtends(items, nitems, imin, imax); CalcExtends(items, nitems, imin, imax, ref node.bmin, ref node.bmax);
node.bmin = minmax[0];
node.bmax = minmax[1];
int axis = LongestAxis(node.bmax[0] - node.bmin[0], node.bmax[1] - node.bmin[1], int axis = LongestAxis(
node.bmax[2] - node.bmin[2]); node.bmax.X - node.bmin.X,
node.bmax.Y - node.bmin.Y,
node.bmax.Z - node.bmin.Z
);
if (axis == 0) if (axis == 0)
{ {
@ -159,29 +148,29 @@ namespace DotRecast.Detour
int vb = option.detailMeshes[i * 4 + 0]; int vb = option.detailMeshes[i * 4 + 0];
int ndv = option.detailMeshes[i * 4 + 1]; int ndv = option.detailMeshes[i * 4 + 1];
int dv = vb * 3; int dv = vb * 3;
var bmin = RcVecUtils.Create(option.detailVerts, dv); var bmin = RcVec.Create(option.detailVerts, dv);
var bmax = RcVecUtils.Create(option.detailVerts, dv); var bmax = RcVec.Create(option.detailVerts, dv);
for (int j = 1; j < ndv; j++) for (int j = 1; j < ndv; j++)
{ {
bmin = RcVecUtils.Min(bmin, option.detailVerts, dv + j * 3); bmin = RcVec3f.Min(bmin, RcVec.Create(option.detailVerts, dv + j * 3));
bmax = RcVecUtils.Max(bmax, option.detailVerts, dv + j * 3); bmax = RcVec3f.Max(bmax, RcVec.Create(option.detailVerts, dv + j * 3));
} }
// BV-tree uses cs for all dimensions // BV-tree uses cs for all dimensions
it.bmin[0] = Math.Clamp((int)((bmin.X - option.bmin.X) * quantFactor), 0, int.MaxValue); it.bmin.X = 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.Y = 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.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.X = 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.Y = 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.Z = Math.Clamp((int)((bmax.Z - option.bmin.Z) * quantFactor), 0, int.MaxValue);
} }
else else
{ {
int p = i * option.nvp * 2; int p = i * option.nvp * 2;
it.bmin[0] = it.bmax[0] = option.verts[option.polys[p] * 3 + 0]; it.bmin.X = it.bmax.X = option.verts[option.polys[p] * 3 + 0];
it.bmin[1] = it.bmax[1] = option.verts[option.polys[p] * 3 + 1]; it.bmin.Y = it.bmax.Y = option.verts[option.polys[p] * 3 + 1];
it.bmin[2] = it.bmax[2] = option.verts[option.polys[p] * 3 + 2]; it.bmin.Z = it.bmax.Z = option.verts[option.polys[p] * 3 + 2];
for (int j = 1; j < option.nvp; ++j) 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 y = option.verts[option.polys[p + j] * 3 + 1];
int z = option.verts[option.polys[p + j] * 3 + 2]; int z = option.verts[option.polys[p + j] * 3 + 2];
if (x < it.bmin[0]) if (x < it.bmin.X)
it.bmin[0] = x; it.bmin.X = x;
if (y < it.bmin[1]) if (y < it.bmin.Y)
it.bmin[1] = y; it.bmin.Y = y;
if (z < it.bmin[2]) if (z < it.bmin.Z)
it.bmin[2] = z; it.bmin.Z = z;
if (x > it.bmax[0]) if (x > it.bmax.X)
it.bmax[0] = x; it.bmax.X = x;
if (y > it.bmax[1]) if (y > it.bmax.Y)
it.bmax[1] = y; it.bmax.Y = y;
if (z > it.bmax[2]) if (z > it.bmax.Z)
it.bmax[2] = z; it.bmax.Z = z;
} }
// Remap y // Remap y
it.bmin[1] = (int)MathF.Floor(it.bmin[1] * option.ch * quantFactor); it.bmin.Y = (int)MathF.Floor(it.bmin.Y * option.ch * quantFactor);
it.bmax[1] = (int)MathF.Ceiling(it.bmax[1] * option.ch * quantFactor); it.bmax.Y = (int)MathF.Ceiling(it.bmax.Y * option.ch * quantFactor);
} }
} }
@ -251,14 +240,15 @@ namespace DotRecast.Detour
return 0xff; return 0xff;
} }
/** // TODO: Better error handling.
* Builds navigation mesh tile data from the provided tile creation data.
* /// @par
* @param option ///
* Tile creation data. /// 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
* @return created tile data /// mesh.
*/ ///
/// @see dtNavMesh, dtNavMesh::addTile()
public static DtMeshData CreateNavMeshData(DtNavMeshCreateParams option) public static DtMeshData CreateNavMeshData(DtNavMeshCreateParams option)
{ {
if (option.vertCount >= 0xffff) if (option.vertCount >= 0xffff)
@ -316,8 +306,8 @@ namespace DotRecast.Detour
for (int i = 0; i < option.offMeshConCount; ++i) for (int i = 0; i < option.offMeshConCount; ++i)
{ {
var p0 = RcVecUtils.Create(option.offMeshConVerts, (i * 2 + 0) * 3); var p0 = RcVec.Create(option.offMeshConVerts, (i * 2 + 0) * 3);
var p1 = RcVecUtils.Create(option.offMeshConVerts, (i * 2 + 1) * 3); var p1 = RcVec.Create(option.offMeshConVerts, (i * 2 + 1) * 3);
offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(p0, bmin, bmax); offMeshConClass[i * 2 + 0] = ClassifyOffMeshPoint(p0, bmin, bmax);
offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(p1, bmin, bmax); offMeshConClass[i * 2 + 1] = ClassifyOffMeshPoint(p1, bmin, bmax);
@ -424,8 +414,8 @@ namespace DotRecast.Detour
DtOffMeshConnection[] offMeshCons = new DtOffMeshConnection[storedOffMeshConCount]; DtOffMeshConnection[] offMeshCons = new DtOffMeshConnection[storedOffMeshConCount];
// Store header // Store header
header.magic = DtNavMesh.DT_NAVMESH_MAGIC; header.magic = DT_NAVMESH_MAGIC;
header.version = DtNavMesh.DT_NAVMESH_VERSION; header.version = DT_NAVMESH_VERSION;
header.x = option.tileX; header.x = option.tileX;
header.y = option.tileZ; header.y = option.tileZ;
header.layer = option.tileLayer; header.layer = option.tileLayer;
@ -497,13 +487,13 @@ namespace DotRecast.Detour
if (dir == 0xf) // Border if (dir == 0xf) // Border
p.neis[j] = 0; p.neis[j] = 0;
else if (dir == 0) // Portal x- 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+ 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+ 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- else if (dir == 3) // Portal z-
p.neis[j] = DtNavMesh.DT_EXT_LINK | 6; p.neis[j] = DT_EXT_LINK | 6;
} }
else else
{ {
@ -550,9 +540,9 @@ namespace DotRecast.Detour
int ndv = option.detailMeshes[i * 4 + 1]; int ndv = option.detailMeshes[i * 4 + 1];
int nv = navPolys[i].vertCount; int nv = navPolys[i].vertCount;
int vertBase = vbase; int vertBase = vbase;
int vertCount = (ndv - nv); byte vertCount = (byte)(ndv - nv);
int triBase = option.detailMeshes[i * 4 + 2]; 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); navDMeshes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount);
// Copy vertices except the first 'nv' verts which are equal to // Copy vertices except the first 'nv' verts which are equal to
// nav poly verts. // nav poly verts.
@ -574,9 +564,9 @@ namespace DotRecast.Detour
{ {
int nv = navPolys[i].vertCount; int nv = navPolys[i].vertCount;
int vertBase = 0; int vertBase = 0;
int vertCount = 0; byte vertCount = 0;
int triBase = tbase; int triBase = tbase;
int triCount = (nv - 2); byte triCount = (byte)(nv - 2);
navDMeshes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); navDMeshes[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount);
// Triangulate polygon (local indices). // Triangulate polygon (local indices).
for (int j = 2; j < nv; ++j) for (int j = 2; j < nv; ++j)
@ -618,11 +608,11 @@ namespace DotRecast.Detour
int endPts = i * 2 * 3; int endPts = i * 2 * 3;
for (int j = 0; j < 2; ++j) 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.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]; con.side = offMeshConClass[i * 2 + 1];
if (option.offMeshConUserID != null) if (option.offMeshConUserID != null)
con.userId = option.offMeshConUserID[i]; con.userId = option.offMeshConUserID[i];

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -23,27 +23,16 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/** /// Configuration parameters used to define multi-tile navigation meshes.
* Configuration parameters used to define multi-tile navigation meshes. The values are used to allocate space during /// The values are used to allocate space during the initialization of a navigation mesh.
* the initialization of a navigation mesh. /// @see dtNavMesh::init()
* /// @ingroup detour
* @see NavMesh
*/
public struct DtNavMeshParams public struct DtNavMeshParams
{ {
/** The world space origin of the navigation mesh's tile space. [(x, y, z)] */ public RcVec3f orig; //< The world space origin of the navigation mesh's tile space. [(x, y, z)]
public RcVec3f orig; public float tileWidth; //< The width of each tile. (Along the x-axis.)
public float tileHeight; //< The height of each tile. (Along the z-axis.)
/** The width of each tile. (Along the x-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 float tileWidth; 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.
/** 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;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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<long> path, int pathSize,
Span<DtStraightPath> 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;
}
}
}

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -48,6 +49,7 @@ namespace DotRecast.Detour
private static bool Raycast(DtMeshTile tile, RcVec3f sp, RcVec3f sq, out float hitTime) private static bool Raycast(DtMeshTile tile, RcVec3f sp, RcVec3f sq, out float hitTime)
{ {
hitTime = 0.0f; hitTime = 0.0f;
Span<RcVec3f> tempVerts = stackalloc RcVec3f[3];
for (int i = 0; i < tile.data.header.polyCount; ++i) for (int i = 0; i < tile.data.header.polyCount; ++i)
{ {
DtPoly p = tile.data.polys[i]; DtPoly p = tile.data.polys[i];
@ -58,7 +60,7 @@ namespace DotRecast.Detour
ref DtPolyDetail pd = ref tile.data.detailMeshes[i]; ref DtPolyDetail pd = ref tile.data.detailMeshes[i];
RcVec3f[] verts = new RcVec3f[3]; Span<RcVec3f> verts = tempVerts;
for (int j = 0; j < pd.triCount; ++j) for (int j = 0; j < pd.triCount; ++j)
{ {
int t = (pd.triBase + j) * 4; int t = (pd.triBase + j) * 4;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public static class DtNodeFlags public static class DtNodeFlags
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,22 +24,24 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
using static DtDetour;
public static class DtPathUtils public static class DtPathUtils
{ {
private const int MAX_STEER_POINTS = 3;
public static bool GetSteerTarget(DtNavMeshQuery navQuery, RcVec3f startPos, RcVec3f endPos, public static bool GetSteerTarget(DtNavMeshQuery navQuery, RcVec3f startPos, RcVec3f endPos,
float minTargetDist, float minTargetDist,
List<long> path, List<long> path, int pathSize,
out RcVec3f steerPos, out int steerPosFlag, out long steerPosRef) out RcVec3f steerPos, out int steerPosFlag, out long steerPosRef)
{ {
const int MAX_STEER_POINTS = 3;
steerPos = RcVec3f.Zero; steerPos = RcVec3f.Zero;
steerPosFlag = 0; steerPosFlag = 0;
steerPosRef = 0; steerPosRef = 0;
// Find steer target. // Find steer target.
var straightPath = new List<DtStraightPath>(MAX_STEER_POINTS); Span<DtStraightPath> straightPath = stackalloc DtStraightPath[MAX_STEER_POINTS];
var result = navQuery.FindStraightPath(startPos, endPos, path, ref straightPath, MAX_STEER_POINTS, 0); var result = navQuery.FindStraightPath(startPos, endPos, path, pathSize, straightPath, out var nsteerPath, MAX_STEER_POINTS, 0);
if (result.Failed()) if (result.Failed())
{ {
return false; return false;
@ -47,7 +49,7 @@ namespace DotRecast.Detour
// Find vertex far enough to steer to. // Find vertex far enough to steer to.
int ns = 0; int ns = 0;
while (ns < straightPath.Count) while (ns < nsteerPath)
{ {
// Stop at Off-Mesh link or when point is further than slop away. // Stop at Off-Mesh link or when point is further than slop away.
if (((straightPath[ns].flags & DtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0) 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. // Failed to find good point to steer to.
if (ns >= straightPath.Count) if (ns >= nsteerPath)
return false; return false;
steerPos = straightPath[ns].pos; steerPos = straightPath[ns].pos;
@ -88,29 +90,32 @@ namespace DotRecast.Detour
// +-S-+-T-+ // +-S-+-T-+
// |:::| | <-- the step can end up in here, resulting U-turn path. // |:::| | <-- the step can end up in here, resulting U-turn path.
// +---+---+ // +---+---+
public static List<long> FixupShortcuts(List<long> path, DtNavMeshQuery navQuery) public static int FixupShortcuts(ref List<long> path, int npath, DtNavMeshQuery navQuery)
{ {
if (path.Count < 3) if (npath < 3)
{ {
return path; return npath;
} }
// Get connected polygons // Get connected polygons
List<long> neis = new List<long>(); const int maxNeis = 16;
Span<long> neis = stackalloc long[maxNeis];
int nneis = 0;
var status = navQuery.GetAttachedNavMesh().GetTileAndPolyByRef(path[0], out var tile, out var poly); var status = navQuery.GetAttachedNavMesh().GetTileAndPolyByRef(path[0], out var tile, out var poly);
if (status.Failed()) 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]; DtLink link = tile.links[k];
if (link.refs != 0) 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. // in the path, short cut to that polygon directly.
const int maxLookAhead = 6; const int maxLookAhead = 6;
int cut = 0; 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]) if (path[i] == neis[j])
{ {
@ -134,23 +139,25 @@ namespace DotRecast.Detour
{ {
List<long> shortcut = new List<long>(); List<long> shortcut = new List<long>();
shortcut.Add(path[0]); shortcut.Add(path[0]);
shortcut.AddRange(path.GetRange(cut, path.Count - cut)); shortcut.AddRange(path.GetRange(cut, npath - cut));
return shortcut;
path = shortcut;
return shortcut.Count;
} }
return path; return npath;
} }
public static List<long> MergeCorridorStartMoved(List<long> path, int npath, int maxPath, List<long> visited) public static int MergeCorridorStartMoved(ref List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;
// Find furthest common polygon. // Find furthest common polygon.
for (int i = path.Count - 1; i >= 0; --i) for (int i = npath - 1; i >= 0; --i)
{ {
bool found = false; 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]) if (path[i] == visited[j])
{ {
@ -169,7 +176,7 @@ namespace DotRecast.Detour
// If no intersection found just return current path. // If no intersection found just return current path.
if (furthestPath == -1 || furthestVisited == -1) if (furthestPath == -1 || furthestVisited == -1)
{ {
return path; return npath;
} }
// Concatenate paths. // Concatenate paths.
@ -177,25 +184,27 @@ namespace DotRecast.Detour
// Adjust beginning of the buffer to include the visited. // Adjust beginning of the buffer to include the visited.
List<long> result = new List<long>(); List<long> result = new List<long>();
// Store visited // Store visited
for (int i = visited.Count - 1; i > furthestVisited; --i) for (int i = nvisited - 1; i > furthestVisited; --i)
{ {
result.Add(visited[i]); result.Add(visited[i]);
} }
result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath)); result.AddRange(path.GetRange(furthestPath, npath - furthestPath));
return result;
path = result;
return result.Count;
} }
public static List<long> MergeCorridorEndMoved(List<long> path, int npath, int maxPath, List<long> visited) public static int MergeCorridorEndMoved(ref List<long> path, int npath, int maxPath, Span<long> visited, int nvisited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;
// Find furthest common polygon. // Find furthest common polygon.
for (int i = 0; i < path.Count; ++i) for (int i = 0; i < npath; ++i)
{ {
bool found = false; 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]) if (path[i] == visited[j])
{ {
@ -214,25 +223,30 @@ namespace DotRecast.Detour
// If no intersection found just return current path. // If no intersection found just return current path.
if (furthestPath == -1 || furthestVisited == -1) if (furthestPath == -1 || furthestVisited == -1)
{ {
return path; return npath;
} }
// Concatenate paths. // Concatenate paths.
List<long> result = path.GetRange(0, furthestPath); List<long> result = path.GetRange(0, furthestPath);
result.AddRange(visited.GetRange(furthestVisited, visited.Count - furthestVisited)); foreach (var v in visited.Slice(furthestVisited, nvisited - furthestVisited))
return result; {
result.Add(v);
} }
public static List<long> MergeCorridorStartShortcut(List<long> path, int npath, int maxPath, List<long> visited) path = result;
return result.Count;
}
public static int MergeCorridorStartShortcut(ref List<long> path, int npath, int maxPath, List<long> visited, int nvisited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;
// Find furthest common polygon. // Find furthest common polygon.
for (int i = path.Count - 1; i >= 0; --i) for (int i = npath - 1; i >= 0; --i)
{ {
bool found = false; 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]) if (path[i] == visited[j])
{ {
@ -251,15 +265,17 @@ namespace DotRecast.Detour
// If no intersection found just return current path. // If no intersection found just return current path.
if (furthestPath == -1 || furthestVisited <= 0) if (furthestPath == -1 || furthestVisited <= 0)
{ {
return path; return npath;
} }
// Concatenate paths. // Concatenate paths.
// Adjust beginning of the buffer to include the visited. // Adjust beginning of the buffer to include the visited.
List<long> result = visited.GetRange(0, furthestVisited); List<long> result = visited.GetRange(0, furthestVisited);
result.AddRange(path.GetRange(furthestPath, path.Count - furthestPath)); result.AddRange(path.GetRange(furthestPath, npath - furthestPath));
return result;
path = result;
return result.Count;
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/** Defines a polygon within a MeshTile object. */ /// Defines a polygon within a dtMeshTile object.
[Serializable] /// @ingroup detour
public class DtPoly public class DtPoly
{ {
public int index; public readonly int index;
/** The indices of the polygon's vertices. The actual vertices are located in MeshTile::verts. */ /// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.)
public int[] verts; public int firstLink;
/** Packed data representing neighbor polygons references and flags for each edge. */ /// The indices of the polygon's vertices.
public int[] neis; /// The actual vertices are located in dtMeshTile::verts.
public readonly int[] verts;
/** The user defined polygon flags. */ /// Packed data representing neighbor polygons references and flags for each edge.
public readonly int[] neis;
/// The user defined polygon flags.
public int flags; public int flags;
/** The number of vertices in the polygon. */ /// The number of vertices in the polygon.
public int vertCount; public int vertCount;
/// The bit packed area id and polygon type. /// The bit packed area id and polygon type.
@ -51,25 +53,25 @@ namespace DotRecast.Detour
neis = new int[maxVertsPerPoly]; neis = new int[maxVertsPerPoly];
} }
/** Sets the user defined area id. [Limit: &lt; {@link org.recast4j.detour.NavMesh#DT_MAX_AREAS}] */ /// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
public void SetArea(int a) public void SetArea(int a)
{ {
areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f);
} }
/** Sets the polygon type. (See: #dtPolyTypes.) */ /// Sets the polygon type. (See: #dtPolyTypes.)
public void SetPolyType(int t) public void SetPolyType(int t)
{ {
areaAndtype = (areaAndtype & 0x3f) | (t << 6); areaAndtype = (areaAndtype & 0x3f) | (t << 6);
} }
/** Gets the user defined area id. */ /// Gets the user defined area id.
public int GetArea() public int GetArea()
{ {
return areaAndtype & 0x3f; return areaAndtype & 0x3f;
} }
/** Gets the polygon type. (See: #dtPolyTypes) */ /// Gets the polygon type. (See: #dtPolyTypes)
public int GetPolyType() public int GetPolyType()
{ {
return areaAndtype >> 6; return areaAndtype >> 6;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Detour
{ {
/** Defines the location of detail sub-mesh data within a dtMeshTile. */ /// Defines the location of detail sub-mesh data within a dtMeshTile.
public struct DtPolyDetail public readonly struct DtPolyDetail
{ {
/** The offset of the vertices in the MeshTile::detailVerts array. */ public readonly int vertBase; //< The offset of the vertices in the dtMeshTile::detailVerts array.
public int vertBase; 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 DtPolyDetail(int vertBase, int triBase, byte vertCount, byte triCount)
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)
{ {
this.vertBase = vertBase; this.vertBase = vertBase;
this.triBase = triBase; this.triBase = triBase;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Flags representing the type of a navigation mesh polygon. /// Flags representing the type of a navigation mesh polygon.
public static class DtPolyTypes public static class DtPolyTypes

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -23,6 +23,8 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
using static DtDetour;
/** /**
* <b>The Default Implementation</b> * <b>The Default Implementation</b>
* *
@ -50,7 +52,7 @@ namespace DotRecast.Detour
*/ */
public class DtQueryDefaultFilter : IDtQueryFilter 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_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.) 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_includeFlags = 0xffff;
m_excludeFlags = 0; 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; m_areaCost[i] = 1.0f;
} }
@ -68,12 +70,12 @@ namespace DotRecast.Detour
{ {
m_includeFlags = includeFlags; m_includeFlags = includeFlags;
m_excludeFlags = excludeFlags; 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]; 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; m_areaCost[i] = 1.0f;
} }

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Options for dtNavMeshQuery::raycast /// Options for dtNavMeshQuery::raycast
public static class DtRaycastOptions public static class DtRaycastOptions

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public readonly struct DtSegInterval public readonly struct DtSegInterval
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,11 +25,16 @@ namespace DotRecast.Detour
//TODO: (PP) Add comments //TODO: (PP) Add comments
public readonly struct DtStraightPath public readonly struct DtStraightPath
{ {
/// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
public readonly RcVec3f pos; 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 readonly long refs;
public DtStraightPath(RcVec3f pos, int flags, long refs) public DtStraightPath(RcVec3f pos, byte flags, long refs)
{ {
this.pos = pos; this.pos = pos;
this.flags = flags; this.flags = flags;

View File

@ -1,10 +1,10 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Vertex flags returned by dtNavMeshQuery::findStraightPath. /// Vertex flags returned by dtNavMeshQuery::findStraightPath.
public static class DtStraightPathFlags public static class DtStraightPathFlags
{ {
public const int DT_STRAIGHTPATH_START = 0x01; //< The vertex is the start position in the path. public const byte 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 byte 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_OFFMESH_CONNECTION = 0x04; //< The vertex is the start of an off-mesh connection.
} }
} }

View File

@ -1,4 +1,4 @@
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
namespace DotRecast.Detour namespace DotRecast.Detour

View File

@ -1,4 +1,4 @@
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Options for dtNavMeshQuery::findStraightPath. /// Options for dtNavMeshQuery::findStraightPath.
public static class DtStraightPathOptions public static class DtStraightPathOptions

View File

@ -1,15 +1,13 @@
using System; using System;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour 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 public class DtStrictDtPolygonByCircleConstraint : IDtPolygonByCircleConstraint
{ {
private const int CIRCLE_SEGMENTS = 12; 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(); 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]; var temp = new float[CIRCLE_SEGMENTS * 3];
for (int i = 0; i < CIRCLE_SEGMENTS; i++) for (int i = 0; i < CIRCLE_SEGMENTS; i++)
@ -31,13 +29,24 @@ namespace DotRecast.Detour
return temp; return temp;
} }
public static void ScaleCircle(Span<float> src, RcVec3f center, float radius, Span<float> 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) public float[] Apply(float[] verts, RcVec3f center, float radius)
{ {
float radiusSqr = radius * radius; float radiusSqr = radius * radius;
int outsideVertex = -1; int outsideVertex = -1;
for (int pv = 0; pv < verts.Length; pv += 3) 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; outsideVertex = pv;
break; break;
@ -50,29 +59,16 @@ namespace DotRecast.Detour
return verts; return verts;
} }
float[] qCircle = Circle(center, radius); Span<float> qCircle = stackalloc float[UnitCircle.Length];
ScaleCircle(UnitCircle, center, radius, qCircle);
float[] intersection = DtConvexConvexIntersections.Intersect(verts, qCircle); float[] intersection = DtConvexConvexIntersections.Intersect(verts, qCircle);
if (intersection == null && DtUtils.PointInPolygon(center, verts, verts.Length / 3)) if (intersection == null && DtUtils.PointInPolygon(center, verts, verts.Length / 3))
{ {
// circle inside polygon // circle inside polygon
return qCircle; return qCircle.ToArray();
} }
return intersection; 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;
}
} }
} }

View File

@ -1,13 +1,10 @@
using System; using System;
using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
public static class DtUtils public static class DtUtils
{ {
private static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f);
public static int NextPow2(int v) public static int NextPow2(int v)
{ {
v--; v--;
@ -39,24 +36,6 @@ namespace DotRecast.Detour
return r; 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. /// Determines if two axis-aligned bounding boxes overlap.
/// @param[in] amin Minimum bounds of box A. [(x, y, z)] /// @param[in] amin Minimum bounds of box A. [(x, y, z)]
/// @param[in] amax Maximum 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)] /// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
/// @return True if the two AABB's overlap. /// @return True if the two AABB's overlap.
/// @see dtOverlapBounds /// @see dtOverlapBounds
public static bool OverlapQuantBounds(Span<int> amin, Span<int> amax, Span<int> bmin, Span<int> bmax) public static bool OverlapQuantBounds(ref RcVec3i amin, ref RcVec3i amax, ref RcVec3i bmin, ref RcVec3i bmax)
{ {
bool overlap = true; bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; overlap = (amin.X > bmax.X || amax.X < bmin.X) ? false : overlap;
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; overlap = (amin.Y > bmax.Y || amax.Y < bmin.Y) ? false : overlap;
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; overlap = (amin.Z > bmax.Z || amax.Z < bmin.Z) ? false : overlap;
return overlap; return overlap;
} }
@ -97,7 +76,7 @@ namespace DotRecast.Detour
/// @par /// @par
/// ///
/// All vertices are projected onto the xz-plane, so the y-values are ignored. /// 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<float> polya, int npolya, Span<float> polyb, int npolyb)
{ {
const float eps = 1e-4f; const float eps = 1e-4f;
for (int i = 0, j = npolya - 1; i < npolya; j = i++) 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] b Vertex B. [(x, y, z)]
/// @param[in] c Vertex C. [(x, y, z)] /// @param[in] c Vertex C. [(x, y, z)]
/// @return The signed xz-plane area of the triangle. /// @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<float> verts, int a, int b, int c)
{ {
float abx = verts[b] - verts[a]; float abx = verts[b] - verts[a];
float abz = verts[b + 2] - verts[a + 2]; float abz = verts[b + 2] - verts[a + 2];
@ -165,7 +144,7 @@ namespace DotRecast.Detour
// Returns a random point in a convex polygon. // Returns a random point in a convex polygon.
// Adapted from Graphics Gems article. // Adapted from Graphics Gems article.
public static RcVec3f RandomPointInConvexPoly(float[] pts, int npts, float[] areas, float s, float t) public static void RandomPointInConvexPoly(Span<float> pts, int npts, Span<float> areas, float s, float t, out RcVec3f @out)
{ {
// Calc triangle araes // Calc triangle araes
float areasum = 0.0f; float areasum = 0.0f;
@ -202,7 +181,7 @@ namespace DotRecast.Detour
int pb = (tri - 1) * 3; int pb = (tri - 1) * 3;
int pc = tri * 3; int pc = tri * 3;
return new RcVec3f() @out = new RcVec3f()
{ {
X = a * pts[pa] + b * pts[pb] + c * pts[pc], X = a * pts[pa] + b * pts[pb] + c * pts[pc],
Y = a * pts[pa + 1] + b * pts[pb + 1] + c * pts[pc + 1], Y = a * pts[pa + 1] + b * pts[pb + 1] + c * pts[pc + 1],
@ -246,13 +225,13 @@ namespace DotRecast.Detour
return false; return false;
} }
public static RcVec2f ProjectPoly(RcVec3f axis, float[] poly, int npoly) public static RcVec2f ProjectPoly(RcVec3f axis, Span<float> poly, int npoly)
{ {
float rmin, rmax; float rmin, rmax;
rmin = rmax = axis.Dot2D(poly, 0); rmin = rmax = axis.Dot2D(new RcVec3f(poly));
for (int i = 1; i < npoly; ++i) 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); rmin = Math.Min(rmin, d);
rmax = Math.Max(rmax, d); rmax = Math.Max(rmax, d);
} }
@ -267,7 +246,7 @@ namespace DotRecast.Detour
/// @par /// @par
/// ///
/// All points are projected onto the xz-plane, so the y-values are ignored. /// 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<float> verts, int nverts)
{ {
// TODO: Replace pnpoly with triArea2D tests? // TODO: Replace pnpoly with triArea2D tests?
int i, j; int i, j;
@ -286,7 +265,7 @@ namespace DotRecast.Detour
return c; return c;
} }
public static bool DistancePtPolyEdgesSqr(RcVec3f pt, float[] verts, int nverts, float[] ed, float[] et) public static bool DistancePtPolyEdgesSqr(RcVec3f pt, Span<float> verts, int nverts, Span<float> ed, Span<float> et)
{ {
// TODO: Replace pnpoly with triArea2D tests? // TODO: Replace pnpoly with triArea2D tests?
int i, j; int i, j;
@ -307,10 +286,10 @@ namespace DotRecast.Detour
return c; return c;
} }
public static float DistancePtSegSqr2D(RcVec3f pt, float[] verts, int p, int q, out float t) public static float DistancePtSegSqr2D(RcVec3f pt, Span<float> verts, int p, int q, out float t)
{ {
var vp = RcVecUtils.Create(verts, p); var vp = RcVec.Create(verts, p);
var vq = RcVecUtils.Create(verts, q); var vq = RcVec.Create(verts, q);
return DistancePtSegSqr2D(pt, vp, vq, out t); return DistancePtSegSqr2D(pt, vp, vq, out t);
} }
@ -342,7 +321,7 @@ namespace DotRecast.Detour
} }
public static bool IntersectSegmentPoly2D(RcVec3f p0, RcVec3f p1, public static bool IntersectSegmentPoly2D(RcVec3f p0, RcVec3f p1,
RcVec3f[] verts, int nverts, Span<RcVec3f> verts, int nverts,
out float tmin, out float tmax, out float tmin, out float tmax,
out int segMin, out int segMax) out int segMin, out int segMax)
{ {
@ -362,8 +341,8 @@ namespace DotRecast.Detour
RcVec3f vpi = verts[i]; RcVec3f vpi = verts[i];
var edge = RcVec3f.Subtract(vpi, vpj); var edge = RcVec3f.Subtract(vpi, vpj);
var diff = RcVec3f.Subtract(p0v, vpj); var diff = RcVec3f.Subtract(p0v, vpj);
float n = RcVecUtils.Perp2D(edge, diff); float n = RcVec.Perp2D(edge, diff);
float d = RcVecUtils.Perp2D(dir, edge); float d = RcVec.Perp2D(dir, edge);
if (MathF.Abs(d) < EPS) if (MathF.Abs(d) < EPS)
{ {
// S is nearly parallel to this edge // S is nearly parallel to this edge
@ -425,14 +404,14 @@ namespace DotRecast.Detour
RcVec3f u = RcVec3f.Subtract(aq, ap); RcVec3f u = RcVec3f.Subtract(aq, ap);
RcVec3f v = RcVec3f.Subtract(bq, bp); RcVec3f v = RcVec3f.Subtract(bq, bp);
RcVec3f w = RcVec3f.Subtract(ap, 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) if (MathF.Abs(d) < 1e-6f)
{ {
return false; return false;
} }
s = RcVecUtils.PerpXZ(v, w) / d; s = RcVec.PerpXZ(v, w) / d;
t = RcVecUtils.PerpXZ(u, w) / d; t = RcVec.PerpXZ(u, w) / d;
return true; return true;
} }

View File

@ -1,7 +1,14 @@
using System;
namespace DotRecast.Detour namespace DotRecast.Detour
{ {
/// Provides custom polygon query behavior.
/// Used by dtNavMeshQuery::queryPolygons.
/// @ingroup detour
public interface IDtPolyQuery 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<long> refs, int count);
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -21,13 +21,17 @@ using DotRecast.Core;
namespace DotRecast.Detour.Io namespace DotRecast.Detour.Io
{ {
using static DtDetour;
public class DtMeshDataReader public class DtMeshDataReader
{ {
public const int DT_POLY_DETAIL_SIZE = 10; 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) public DtMeshData Read(BinaryReader stream, int maxVertPerPoly)
{ {
RcByteBuffer buf = IOUtils.ToByteBuffer(stream); RcByteBuffer buf = RcIO.ToByteBuffer(stream);
return Read(buf, maxVertPerPoly, false); return Read(buf, maxVertPerPoly, false);
} }
@ -38,7 +42,7 @@ namespace DotRecast.Detour.Io
public DtMeshData Read32Bit(BinaryReader stream, int maxVertPerPoly) public DtMeshData Read32Bit(BinaryReader stream, int maxVertPerPoly)
{ {
RcByteBuffer buf = IOUtils.ToByteBuffer(stream); RcByteBuffer buf = RcIO.ToByteBuffer(stream);
return Read(buf, maxVertPerPoly, true); return Read(buf, maxVertPerPoly, true);
} }
@ -53,10 +57,10 @@ namespace DotRecast.Detour.Io
DtMeshHeader header = new DtMeshHeader(); DtMeshHeader header = new DtMeshHeader();
data.header = header; data.header = header;
header.magic = buf.GetInt(); header.magic = buf.GetInt();
if (header.magic != DtNavMesh.DT_NAVMESH_MAGIC) if (header.magic != DT_NAVMESH_MAGIC)
{ {
header.magic = IOUtils.SwapEndianness(header.magic); header.magic = RcIO.SwapEndianness(header.magic);
if (header.magic != DtNavMesh.DT_NAVMESH_MAGIC) if (header.magic != DT_NAVMESH_MAGIC)
{ {
throw new IOException("Invalid magic"); throw new IOException("Invalid magic");
} }
@ -65,16 +69,16 @@ namespace DotRecast.Detour.Io
} }
header.version = buf.GetInt(); 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 if (header.version < DT_NAVMESH_VERSION_RECAST4J_FIRST
|| header.version > DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_LAST) || header.version > DT_NAVMESH_VERSION_RECAST4J_LAST)
{ {
throw new IOException("Invalid version " + header.version); 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.x = buf.GetInt();
header.y = buf.GetInt(); header.y = buf.GetInt();
header.layer = buf.GetInt(); header.layer = buf.GetInt();
@ -116,8 +120,6 @@ namespace DotRecast.Detour.Io
return data; return data;
} }
public const int LINK_SIZEOF = 16;
public const int LINK_SIZEOF32BIT = 12;
public static int GetSizeofLink(bool is32Bit) public static int GetSizeofLink(bool is32Bit)
{ {
@ -141,7 +143,7 @@ namespace DotRecast.Detour.Io
for (int i = 0; i < polys.Length; i++) for (int i = 0; i < polys.Length; i++)
{ {
polys[i] = new DtPoly(i, maxVertPerPoly); 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 buf.GetInt(); // polys[i].firstLink
} }
@ -171,8 +173,8 @@ namespace DotRecast.Detour.Io
{ {
int vertBase = buf.GetInt(); int vertBase = buf.GetInt();
int triBase = buf.GetInt(); int triBase = buf.GetInt();
int vertCount = buf.Get() & 0xFF; byte vertCount = (byte)(buf.Get() & 0xFF);
int triCount = buf.Get() & 0xFF; byte triCount = (byte)(buf.Get() & 0xFF);
polys[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount); polys[i] = new DtPolyDetail(vertBase, triBase, vertCount, triCount);
if (cCompatibility) if (cCompatibility)
{ {
@ -200,29 +202,25 @@ namespace DotRecast.Detour.Io
for (int i = 0; i < nodes.Length; i++) for (int i = 0; i < nodes.Length; i++)
{ {
nodes[i] = new DtBVNode(); 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.X = buf.GetShort() & 0xFFFF;
{ nodes[i].bmin.Y = buf.GetShort() & 0xFFFF;
nodes[i].bmin[j] = buf.GetShort() & 0xFFFF; nodes[i].bmin.Z = buf.GetShort() & 0xFFFF;
}
for (int j = 0; j < 3; j++) nodes[i].bmax.X = buf.GetShort() & 0xFFFF;
{ nodes[i].bmax.Y = buf.GetShort() & 0xFFFF;
nodes[i].bmax[j] = buf.GetShort() & 0xFFFF; nodes[i].bmax.Z = buf.GetShort() & 0xFFFF;
}
} }
else else
{ {
for (int j = 0; j < 3; j++) nodes[i].bmin.X = buf.GetInt();
{ nodes[i].bmin.Y = buf.GetInt();
nodes[i].bmin[j] = buf.GetInt(); nodes[i].bmin.Z = buf.GetInt();
}
for (int j = 0; j < 3; j++) nodes[i].bmax.X = buf.GetInt();
{ nodes[i].bmax.Y = buf.GetInt();
nodes[i].bmax[j] = buf.GetInt(); nodes[i].bmax.Z = buf.GetInt();
}
} }
nodes[i].i = buf.GetInt(); nodes[i].i = buf.GetInt();

View File

@ -21,36 +21,38 @@ using DotRecast.Core;
namespace DotRecast.Detour.Io 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) public void Write(BinaryWriter stream, DtMeshData data, RcByteOrder order, bool cCompatibility)
{ {
DtMeshHeader header = data.header; DtMeshHeader header = data.header;
Write(stream, header.magic, order); RcIO.Write(stream, header.magic, order);
Write(stream, cCompatibility ? DtNavMesh.DT_NAVMESH_VERSION : DtNavMesh.DT_NAVMESH_VERSION_RECAST4J_LAST, order); RcIO.Write(stream, cCompatibility ? DT_NAVMESH_VERSION : DT_NAVMESH_VERSION_RECAST4J_LAST, order);
Write(stream, header.x, order); RcIO.Write(stream, header.x, order);
Write(stream, header.y, order); RcIO.Write(stream, header.y, order);
Write(stream, header.layer, order); RcIO.Write(stream, header.layer, order);
Write(stream, header.userId, order); RcIO.Write(stream, header.userId, order);
Write(stream, header.polyCount, order); RcIO.Write(stream, header.polyCount, order);
Write(stream, header.vertCount, order); RcIO.Write(stream, header.vertCount, order);
Write(stream, header.maxLinkCount, order); RcIO.Write(stream, header.maxLinkCount, order);
Write(stream, header.detailMeshCount, order); RcIO.Write(stream, header.detailMeshCount, order);
Write(stream, header.detailVertCount, order); RcIO.Write(stream, header.detailVertCount, order);
Write(stream, header.detailTriCount, order); RcIO.Write(stream, header.detailTriCount, order);
Write(stream, header.bvNodeCount, order); RcIO.Write(stream, header.bvNodeCount, order);
Write(stream, header.offMeshConCount, order); RcIO.Write(stream, header.offMeshConCount, order);
Write(stream, header.offMeshBase, order); RcIO.Write(stream, header.offMeshBase, order);
Write(stream, header.walkableHeight, order); RcIO.Write(stream, header.walkableHeight, order);
Write(stream, header.walkableRadius, order); RcIO.Write(stream, header.walkableRadius, order);
Write(stream, header.walkableClimb, order); RcIO.Write(stream, header.walkableClimb, order);
Write(stream, header.bmin.X, order); RcIO.Write(stream, header.bmin.X, order);
Write(stream, header.bmin.Y, order); RcIO.Write(stream, header.bmin.Y, order);
Write(stream, header.bmin.Z, order); RcIO.Write(stream, header.bmin.Z, order);
Write(stream, header.bmax.X, order); RcIO.Write(stream, header.bmax.X, order);
Write(stream, header.bmax.Y, order); RcIO.Write(stream, header.bmax.Y, order);
Write(stream, header.bmax.Z, order); RcIO.Write(stream, header.bmax.Z, order);
Write(stream, header.bvQuantFactor, order); RcIO.Write(stream, header.bvQuantFactor, order);
WriteVerts(stream, data.verts, header.vertCount, order); WriteVerts(stream, data.verts, header.vertCount, order);
WritePolys(stream, data, order, cCompatibility); WritePolys(stream, data, order, cCompatibility);
if (cCompatibility) if (cCompatibility)
@ -70,7 +72,7 @@ namespace DotRecast.Detour.Io
{ {
for (int i = 0; i < count * 3; i++) 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) if (cCompatibility)
{ {
Write(stream, 0xFFFF, order); RcIO.Write(stream, 0xFFFF, order);
} }
for (int j = 0; j < data.polys[i].verts.Length; j++) 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++) 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); RcIO.Write(stream, (short)data.polys[i].flags, order);
Write(stream, (byte)data.polys[i].vertCount); RcIO.Write(stream, (byte)data.polys[i].vertCount);
Write(stream, (byte)data.polys[i].areaAndtype); 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++) for (int i = 0; i < data.header.detailMeshCount; i++)
{ {
Write(stream, data.detailMeshes[i].vertBase, order); RcIO.Write(stream, data.detailMeshes[i].vertBase, order);
Write(stream, data.detailMeshes[i].triBase, order); RcIO.Write(stream, data.detailMeshes[i].triBase, order);
Write(stream, (byte)data.detailMeshes[i].vertCount); RcIO.Write(stream, (byte)data.detailMeshes[i].vertCount);
Write(stream, (byte)data.detailMeshes[i].triCount); RcIO.Write(stream, (byte)data.detailMeshes[i].triCount);
if (cCompatibility) 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++) 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) if (cCompatibility)
{ {
for (int j = 0; j < 3; j++) RcIO.Write(stream, (short)data.bvTree[i].bmin.X, order);
{ RcIO.Write(stream, (short)data.bvTree[i].bmin.Y, order);
Write(stream, (short)data.bvTree[i].bmin[j], order); RcIO.Write(stream, (short)data.bvTree[i].bmin.Z, order);
}
for (int j = 0; j < 3; j++) RcIO.Write(stream, (short)data.bvTree[i].bmax.X, order);
{ RcIO.Write(stream, (short)data.bvTree[i].bmax.Y, order);
Write(stream, (short)data.bvTree[i].bmax[j], order); RcIO.Write(stream, (short)data.bvTree[i].bmax.Z, order);
}
} }
else else
{ {
for (int j = 0; j < 3; j++) RcIO.Write(stream, data.bvTree[i].bmin.X, order);
{ RcIO.Write(stream, data.bvTree[i].bmin.Y, order);
Write(stream, data.bvTree[i].bmin[j], order); RcIO.Write(stream, data.bvTree[i].bmin.Z, 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);
} }
for (int j = 0; j < 3; j++) RcIO.Write(stream, data.bvTree[i].i, order);
{
Write(stream, data.bvTree[i].bmax[j], order);
}
}
Write(stream, data.bvTree[i].i, order);
} }
} }
@ -161,16 +159,16 @@ namespace DotRecast.Detour.Io
{ {
for (int j = 0; j < 2; j++) for (int j = 0; j < 2; j++)
{ {
Write(stream, data.offMeshCons[i].pos[j].X, order); RcIO.Write(stream, data.offMeshCons[i].pos[j].X, order);
Write(stream, data.offMeshCons[i].pos[j].Y, order); RcIO.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].Z, order);
} }
Write(stream, data.offMeshCons[i].rad, order); RcIO.Write(stream, data.offMeshCons[i].rad, order);
Write(stream, (short)data.offMeshCons[i].poly, order); RcIO.Write(stream, (short)data.offMeshCons[i].poly, order);
Write(stream, (byte)data.offMeshCons[i].flags); RcIO.Write(stream, (byte)data.offMeshCons[i].flags);
Write(stream, (byte)data.offMeshCons[i].side); RcIO.Write(stream, (byte)data.offMeshCons[i].side);
Write(stream, data.offMeshCons[i].userId, order); RcIO.Write(stream, data.offMeshCons[i].userId, order);
} }
} }
} }

View File

@ -22,6 +22,8 @@ using DotRecast.Core;
namespace DotRecast.Detour.Io namespace DotRecast.Detour.Io
{ {
using static DtDetour;
public class DtMeshSetReader public class DtMeshSetReader
{ {
private readonly DtMeshDataReader meshReader = new DtMeshDataReader(); private readonly DtMeshDataReader meshReader = new DtMeshDataReader();
@ -29,7 +31,7 @@ namespace DotRecast.Detour.Io
public DtNavMesh Read(BinaryReader @is, int maxVertPerPoly) 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) public DtNavMesh Read(RcByteBuffer bb, int maxVertPerPoly)
@ -39,7 +41,7 @@ namespace DotRecast.Detour.Io
public DtNavMesh Read32Bit(BinaryReader @is, int maxVertPerPoly) 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) public DtNavMesh Read32Bit(RcByteBuffer bb, int maxVertPerPoly)
@ -49,7 +51,7 @@ namespace DotRecast.Detour.Io
public DtNavMesh Read(BinaryReader @is) public DtNavMesh Read(BinaryReader @is)
{ {
return Read(IOUtils.ToByteBuffer(@is)); return Read(RcIO.ToByteBuffer(@is));
} }
public DtNavMesh Read(RcByteBuffer bb) public DtNavMesh Read(RcByteBuffer bb)
@ -66,7 +68,8 @@ namespace DotRecast.Detour.Io
} }
bool cCompatibility = header.version == NavMeshSetHeader.NAVMESHSET_VERSION; 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); ReadTiles(bb, is32Bit, ref header, cCompatibility, mesh);
return mesh; return mesh;
} }
@ -77,7 +80,7 @@ namespace DotRecast.Detour.Io
header.magic = bb.GetInt(); header.magic = bb.GetInt();
if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC) if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC)
{ {
header.magic = IOUtils.SwapEndianness(header.magic); header.magic = RcIO.SwapEndianness(header.magic);
if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC) if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC)
{ {
throw new IOException("Invalid magic " + header.magic); throw new IOException("Invalid magic " + header.magic);
@ -131,7 +134,7 @@ namespace DotRecast.Detour.Io
} }
DtMeshData data = meshReader.Read(bb, mesh.GetMaxVertsPerPoly(), is32Bit); 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 salt = ((refs >> (m_polyBits + m_tileBits)) & saltMask);
int it = ((refs >> m_polyBits) & tileMask); int it = ((refs >> m_polyBits) & tileMask);
int ip = refs & polyMask; int ip = refs & polyMask;
return DtNavMesh.EncodePolyId(salt, it, ip); return EncodePolyId(salt, it, ip);
} }
} }
} }

View File

@ -22,7 +22,7 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Io namespace DotRecast.Detour.Io
{ {
public class DtMeshSetWriter : DtWriter public class DtMeshSetWriter
{ {
private readonly DtMeshDataWriter writer = new DtMeshDataWriter(); private readonly DtMeshDataWriter writer = new DtMeshDataWriter();
private readonly DtNavMeshParamWriter paramWriter = new DtNavMeshParamWriter(); 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) private void WriteHeader(BinaryWriter stream, DtNavMesh mesh, RcByteOrder order, bool cCompatibility)
{ {
Write(stream, NavMeshSetHeader.NAVMESHSET_MAGIC, order); RcIO.Write(stream, NavMeshSetHeader.NAVMESHSET_MAGIC, order);
Write(stream, cCompatibility ? NavMeshSetHeader.NAVMESHSET_VERSION : NavMeshSetHeader.NAVMESHSET_VERSION_RECAST4J, order); RcIO.Write(stream, cCompatibility ? NavMeshSetHeader.NAVMESHSET_VERSION : NavMeshSetHeader.NAVMESHSET_VERSION_RECAST4J, order);
int numTiles = 0; int numTiles = 0;
for (int i = 0; i < mesh.GetMaxTiles(); ++i) for (int i = 0; i < mesh.GetMaxTiles(); ++i)
{ {
@ -49,11 +49,11 @@ namespace DotRecast.Detour.Io
numTiles++; numTiles++;
} }
Write(stream, numTiles, order); RcIO.Write(stream, numTiles, order);
paramWriter.Write(stream, mesh.GetParams(), order); paramWriter.Write(stream, mesh.GetParams(), order);
if (!cCompatibility) 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(); byte[] ba = msw.ToArray();
tileHeader.dataSize = ba.Length; tileHeader.dataSize = ba.Length;
Write(stream, tileHeader.tileRef, order); RcIO.Write(stream, tileHeader.tileRef, order);
Write(stream, tileHeader.dataSize, order); RcIO.Write(stream, tileHeader.dataSize, order);
if (cCompatibility) if (cCompatibility)
{ {
Write(stream, 0, order); // C struct padding RcIO.Write(stream, 0, order); // C struct padding
} }
stream.Write(ba); stream.Write(ba);

View File

@ -4,17 +4,17 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Io namespace DotRecast.Detour.Io
{ {
public class DtNavMeshParamWriter : DtWriter public class DtNavMeshParamWriter
{ {
public void Write(BinaryWriter stream, DtNavMeshParams option, RcByteOrder order) public void Write(BinaryWriter stream, DtNavMeshParams option, RcByteOrder order)
{ {
Write(stream, option.orig.X, order); RcIO.Write(stream, option.orig.X, order);
Write(stream, option.orig.Y, order); RcIO.Write(stream, option.orig.Y, order);
Write(stream, option.orig.Z, order); RcIO.Write(stream, option.orig.Z, order);
Write(stream, option.tileWidth, order); RcIO.Write(stream, option.tileWidth, order);
Write(stream, option.tileHeight, order); RcIO.Write(stream, option.tileHeight, order);
Write(stream, option.maxTiles, order); RcIO.Write(stream, option.maxTiles, order);
Write(stream, option.maxPolys, order); RcIO.Write(stream, option.maxPolys, order);
} }
} }
} }

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 DtNavMesh _navMesh;
private DtNavMeshQuery _navMeshQuery; private DtNavMeshQuery _navMeshQuery;
private readonly RcNavMeshBuildSettings _settings; private readonly RcNavMeshBuildSettings _settings;
private RcConfig _cfg;
private IList<RcBuilderResult> _recastResults; private IList<RcBuilderResult> _recastResults;
private bool _changed; private bool _changed;
@ -56,6 +57,11 @@ namespace DotRecast.Recast.Demo
return _geom; return _geom;
} }
public RcConfig GetRecastConfig()
{
return _cfg;
}
public IList<RcBuilderResult> GetRecastResults() public IList<RcBuilderResult> GetRecastResults()
{ {
return _recastResults; return _recastResults;
@ -86,9 +92,10 @@ namespace DotRecast.Recast.Demo
_changed = changed; _changed = changed;
} }
public void Update(DemoInputGeomProvider geom, IList<RcBuilderResult> recastResults, DtNavMesh navMesh) public void Update(DemoInputGeomProvider geom, RcConfig cfg, IList<RcBuilderResult> recastResults, DtNavMesh navMesh)
{ {
_geom = geom; _geom = geom;
_cfg = cfg;
_recastResults = recastResults; _recastResults = recastResults;
_navMesh = navMesh; _navMesh = navMesh;
SetQuery(navMesh); SetQuery(navMesh);

View File

@ -20,15 +20,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Serilog" Version="3.1.1" /> <PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/> <PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0"/> <PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0"/> <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" /> <PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageReference Include="Silk.NET" Version="2.21.0" /> <PackageReference Include="Silk.NET" Version="2.22.0" />
<PackageReference Include="Silk.NET.OpenGL.Extensions.ImGui" Version="2.21.0" /> <PackageReference Include="Silk.NET.OpenGL.Extensions.ImGui" Version="2.22.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,4 @@
using System; using System;
using DotRecast.Core; using DotRecast.Core;
namespace DotRecast.Recast.Demo.Draw; namespace DotRecast.Recast.Demo.Draw;
@ -9,19 +9,19 @@ public class ArrayBuffer<T>
private T[] _items; private T[] _items;
public int Count => _size; public int Count => _size;
public ArrayBuffer() public ArrayBuffer() : this(512) { }
public ArrayBuffer(int capacity)
{ {
if (capacity <= 0)
throw new ArgumentOutOfRangeException();
_size = 0; _size = 0;
_items = Array.Empty<T>(); _items = new T[capacity];
} }
public void Add(T item) public void Add(T item)
{ {
if (0 >= _items.Length)
{
_items = new T[256];
}
if (_items.Length <= _size) if (_items.Length <= _size)
{ {
var temp = new T[(int)(_size * 1.5)]; var temp = new T[(int)(_size * 1.5)];
@ -37,8 +37,8 @@ public class ArrayBuffer<T>
_size = 0; _size = 0;
} }
public T[] AsArray() public Span<T> AsArray()
{ {
return _items; return _items.AsSpan(0, _size);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 const int NUM_ARC_PTS = 8;
private static readonly float PAD = 0.05f; private const float PAD = 0.05f;
private static readonly float ARC_PTS_SCALE = (1.0f - PAD * 2) / NUM_ARC_PTS; 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) 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; plane[3] = pw;
} }
public bool FrustumTest(float[] bounds) public bool FrustumTest(Span<float> bounds)
{ {
foreach (float[] plane in frustumPlanes) foreach (float[] plane in frustumPlanes)
{ {
@ -748,6 +748,6 @@ public class DebugDraw
public bool FrustumTest(RcVec3f bmin, RcVec3f bmax) 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 });
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public static int GlhUnProjectf(float winx, float winy, float winz, float[] modelview, float[] projection, int[] viewport, ref RcVec3f objectCoordinate)
{ {
// Transformation matrices // Transformation matrices
float[] m = new float[16], A = new float[16]; Span<float> m = stackalloc float[16], A = stackalloc float[16];
float[] @in = new float[4], @out = new float[4]; Span<float> @in = stackalloc float[4], @out = stackalloc float[4];
// Calculation for inverting a matrix, compute projection x modelview // Calculation for inverting a matrix, compute projection x modelview
// and store in A[16] // and store in A[16]
MultiplyMatrices4by4OpenGL_FLOAT(A, projection, modelview); MultiplyMatrices4by4OpenGL_FLOAT(A, projection, modelview);
@ -92,7 +93,7 @@ public static class GLU
return 1; return 1;
} }
static void MultiplyMatrices4by4OpenGL_FLOAT(float[] result, float[] matrix1, float[] matrix2) static void MultiplyMatrices4by4OpenGL_FLOAT(Span<float> result, float[] matrix1, float[] matrix2)
{ {
result[0] = matrix1[0] * matrix2[0] + matrix1[4] * matrix2[1] + matrix1[8] * matrix2[2] + matrix1[12] * matrix2[3]; 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]; 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]; 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<float> resultvector, Span<float> matrix, Span<float> pvector)
{ {
resultvector[0] = matrix[0] * pvector[0] + matrix[4] * pvector[1] + matrix[8] * pvector[2] + matrix[12] * pvector[3]; 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]; 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 // This code comes directly from GLU except that it is for float
static int GlhInvertMatrixf2(float[] m, float[] @out) static int GlhInvertMatrixf2(Span<float> m, Span<float> @out)
{ {
float[][] wtmp = RcArrays.Of<float>(4, 8);
float m0, m1, m2, m3, s; float m0, m1, m2, m3, s;
float[] r0, r1, r2, r3; Span<float> r0 = stackalloc float[8];
r0 = wtmp[0]; Span<float> r1 = stackalloc float[8];
r1 = wtmp[1]; Span<float> r2 = stackalloc float[8];
r2 = wtmp[2]; Span<float> r3 = stackalloc float[8];
r3 = wtmp[3];
r0[0] = MAT(m, 0, 0); r0[0] = MAT(m, 0, 0);
r0[1] = MAT(m, 0, 1); r0[1] = MAT(m, 0, 1);
r0[2] = MAT(m, 0, 2); r0[2] = MAT(m, 0, 2);
@ -160,27 +159,28 @@ public static class GLU
/* choose pivot - or die */ /* choose pivot - or die */
if (MathF.Abs(r3[0]) > MathF.Abs(r2[0])) if (MathF.Abs(r3[0]) > MathF.Abs(r2[0]))
{ {
float[] r = r2; Span<float> r = r2;
r2 = r3; r2 = r3;
r3 = r; r3 = r;
} }
if (MathF.Abs(r2[0]) > MathF.Abs(r1[0])) if (MathF.Abs(r2[0]) > MathF.Abs(r1[0]))
{ {
float[] r = r2; Span<float> r = r2;
r2 = r1; r2 = r1;
r1 = r; r1 = r;
} }
if (MathF.Abs(r1[0]) > MathF.Abs(r0[0])) if (MathF.Abs(r1[0]) > MathF.Abs(r0[0]))
{ {
float[] r = r1; Span<float> r = r1;
r1 = r0; r1 = r0;
r0 = r; r0 = r;
} }
if (0.0 == r0[0]) if (0.0 == r0[0])
return 0; return 0;
/* eliminate first variable */ /* eliminate first variable */
m1 = r1[0] / r0[0]; m1 = r1[0] / r0[0];
m2 = r2[0] / r0[0]; m2 = r2[0] / r0[0];
@ -232,14 +232,14 @@ public static class GLU
/* choose pivot - or die */ /* choose pivot - or die */
if (MathF.Abs(r3[1]) > MathF.Abs(r2[1])) if (MathF.Abs(r3[1]) > MathF.Abs(r2[1]))
{ {
float[] r = r2; Span<float> r = r2;
r2 = r3; r2 = r3;
r3 = r; r3 = r;
} }
if (MathF.Abs(r2[1]) > MathF.Abs(r1[1])) if (MathF.Abs(r2[1]) > MathF.Abs(r1[1]))
{ {
float[] r = r2; Span<float> r = r2;
r2 = r1; r2 = r1;
r1 = r; r1 = r;
} }
@ -284,7 +284,7 @@ public static class GLU
/* choose pivot - or die */ /* choose pivot - or die */
if (MathF.Abs(r3[2]) > MathF.Abs(r2[2])) if (MathF.Abs(r3[2]) > MathF.Abs(r2[2]))
{ {
float[] r = r2; Span<float> r = r2;
r2 = r3; r2 = r3;
r3 = r; r3 = r;
} }
@ -358,12 +358,12 @@ public static class GLU
return 1; return 1;
} }
static float MAT(float[] m, int r, int c) static float MAT(Span<float> m, int r, int c)
{ {
return m[(c) * 4 + (r)]; return m[(c) * 4 + (r)];
} }
static void MAT(float[] m, int r, int c, float v) static void MAT(Span<float> m, int r, int c, float v)
{ {
m[(c) * 4 + (r)] = v; m[(c) * 4 + (r)] = v;
} }

View File

@ -1,3 +1,4 @@
using System.Numerics;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Demo.Draw; namespace DotRecast.Recast.Demo.Draw;

View File

@ -1,10 +1,11 @@
using System; using System;
using System.IO;
using Silk.NET.OpenGL; using Silk.NET.OpenGL;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Demo.Draw; namespace DotRecast.Recast.Demo.Draw;
// TODO use a lot of Memory, 2GB+
public class ModernOpenGLDraw : IOpenGLDraw public class ModernOpenGLDraw : IOpenGLDraw
{ {
private GL _gl; private GL _gl;
@ -19,8 +20,8 @@ public class ModernOpenGLDraw : IOpenGLDraw
private float fogEnd; private float fogEnd;
private bool fogEnabled; private bool fogEnabled;
private int uniformViewMatrix; private int uniformViewMatrix;
private readonly ArrayBuffer<OpenGLVertex> vertices = new(); private readonly ArrayBuffer<OpenGLVertex> vertices = new(512);
private readonly ArrayBuffer<int> elements = new(); private readonly ArrayBuffer<int> elements = new(512);
private GLCheckerTexture _texture; private GLCheckerTexture _texture;
private readonly float[] _viewMatrix = new float[16]; private readonly float[] _viewMatrix = new float[16];
private readonly float[] _projectionMatrix = new float[16]; private readonly float[] _projectionMatrix = new float[16];
@ -36,36 +37,42 @@ public class ModernOpenGLDraw : IOpenGLDraw
public unsafe void Init() public unsafe void Init()
{ {
string SHADER_VERSION = "#version 400\n"; const string SHADER_VERSION = "#version 400\n";
string vertex_shader = SHADER_VERSION + "uniform mat4 ProjMtx;\n" // const string vertex_shader = $@"
+ "uniform mat4 ViewMtx;\n" // {SHADER_VERSION}
+ "in vec3 Position;\n" // uniform mat4 ProjMtx;
+ "in vec2 TexCoord;\n" // uniform mat4 ViewMtx;
+ "in vec4 Color;\n" // in vec3 Position;
+ "out vec2 Frag_UV;\n" // in vec2 TexCoord;
+ "out vec4 Frag_Color;\n" // in vec4 Color;
+ "out float Frag_Depth;\n" // out vec2 Frag_UV;
+ "void main() {\n" // out vec4 Frag_Color;
+ " Frag_UV = TexCoord;\n" // out float Frag_Depth;
+ " Frag_Color = Color;\n" // void main() {{
+ " vec4 VSPosition = ViewMtx * vec4(Position, 1);\n" // Frag_UV = TexCoord;
+ " Frag_Depth = -VSPosition.z;\n" // Frag_Color = Color;
+ " gl_Position = ProjMtx * VSPosition;\n" // vec4 VSPosition = ViewMtx * vec4(Position, 1);
+ "}\n"; Frag_Depth = -VSPosition.z;
string fragment_shader = SHADER_VERSION + "precision mediump float;\n" // gl_Position = ProjMtx * VSPosition;
+ "uniform sampler2D Texture;\n" // }}
+ "uniform float UseTexture;\n" // ";
+ "uniform float EnableFog;\n" // const string fragment_shader = $@"
+ "uniform float FogStart;\n" // {SHADER_VERSION}
+ "uniform float FogEnd;\n" // precision mediump float;
+ "const vec4 FogColor = vec4(0.3f, 0.3f, 0.32f, 1.0f);\n" // uniform sampler2D Texture;
+ "in vec2 Frag_UV;\n" // uniform float UseTexture;
+ "in vec4 Frag_Color;\n" // uniform float EnableFog;
+ "in float Frag_Depth;\n" // uniform float FogStart;
+ "out vec4 Out_Color;\n" // uniform float FogEnd;
+ "void main(){\n" // const vec4 FogColor = vec4(0.3f, 0.3f, 0.32f, 1.0f);
+ " 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" // in vec2 Frag_UV;
+ "}\n"; 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(); program = _gl.CreateProgram();
uint vert_shdr = _gl.CreateShader(GLEnum.VertexShader); uint vert_shdr = _gl.CreateShader(GLEnum.VertexShader);
@ -150,6 +157,10 @@ public class ModernOpenGLDraw : IOpenGLDraw
_gl.BindVertexArray(0); _gl.BindVertexArray(0);
_gl.BindBuffer(GLEnum.ArrayBuffer, 0); _gl.BindBuffer(GLEnum.ArrayBuffer, 0);
_gl.BindBuffer(GLEnum.ElementArrayBuffer, 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() public void Clear()
@ -191,35 +202,18 @@ public class ModernOpenGLDraw : IOpenGLDraw
// GlBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_BUFFER, GL_STREAM_DRAW); // GlBufferData(GL_ARRAY_BUFFER, MAX_VERTEX_BUFFER, GL_STREAM_DRAW);
// GlBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_BUFFER, GL_STREAM_DRAW); // GlBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_ELEMENT_BUFFER, GL_STREAM_DRAW);
uint vboSize = (uint)vertices.Count * 24; _gl.BufferData<OpenGLVertex>(GLEnum.ArrayBuffer, vertices.AsArray(), GLEnum.DynamicDraw);
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
{
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())
{
System.Buffer.MemoryCopy(v, pVerts, vboSize, vboSize);
}
if (currentPrim == DebugDrawPrimitives.QUADS) 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) for (int i = 0; i < vertices.Count; i += 4)
{ {
bw.Write(i); elements.Add(i);
bw.Write(i + 1); elements.Add(i + 1);
bw.Write(i + 2); elements.Add(i + 2);
bw.Write(i); elements.Add(i);
bw.Write(i + 2); elements.Add(i + 2);
bw.Write(i + 3); elements.Add(i + 3);
} }
} }
else else
@ -228,16 +222,10 @@ public class ModernOpenGLDraw : IOpenGLDraw
{ {
elements.Add(i); elements.Add(i);
} }
fixed (void* e = elements.AsArray())
{
System.Buffer.MemoryCopy(e, pElems, eboSize, eboSize);
}
} }
_gl.UnmapBuffer(GLEnum.ElementArrayBuffer); _gl.BufferData<int>(GLEnum.ElementArrayBuffer, elements.AsArray(), GLEnum.DynamicDraw);
_gl.UnmapBuffer(GLEnum.ArrayBuffer);
}
if (_texture != null) if (_texture != null)
{ {
_texture.Bind(); _texture.Bind();
@ -271,15 +259,22 @@ public class ModernOpenGLDraw : IOpenGLDraw
_gl.BindBuffer(GLEnum.ArrayBuffer, 0); _gl.BindBuffer(GLEnum.ArrayBuffer, 0);
_gl.BindBuffer(GLEnum.ElementArrayBuffer, 0); _gl.BindBuffer(GLEnum.ElementArrayBuffer, 0);
vertices.Clear(); vertices.Clear();
elements.Clear();
_gl.LineWidth(1.0f); _gl.LineWidth(1.0f);
_gl.PointSize(1.0f); _gl.PointSize(1.0f);
} }
public void Vertex(float x, float y, float z, int color) public void Vertex(float x, float y, float z, int color)
{ {
vertices.Add(new OpenGLVertex(x, y, z, 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) public void Vertex(float[] pos, int color)
{ {
vertices.Add(new OpenGLVertex(pos, color)); vertices.Add(new OpenGLVertex(pos, color));

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.Detour;
using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Toolset.Builder;
using DotRecast.Recast.Toolset.Geom; using DotRecast.Recast.Toolset.Geom;
using static DotRecast.Recast.RcCommons; using static DotRecast.Recast.RcRecast;
namespace DotRecast.Recast.Demo.Draw; namespace DotRecast.Recast.Demo.Draw;
public class NavMeshRenderer public class NavMeshRenderer
{ {
private readonly RecastDebugDraw _debugDraw; 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) public NavMeshRenderer(RecastDebugDraw debugDraw)
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour; using DotRecast.Detour;
using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Toolset.Builder;
@ -27,11 +26,13 @@ using Silk.NET.OpenGL;
namespace DotRecast.Recast.Demo.Draw; namespace DotRecast.Recast.Demo.Draw;
using static DtDetour;
public class RecastDebugDraw : DebugDraw public class RecastDebugDraw : DebugDraw
{ {
public static readonly int DRAWNAVMESH_OFFMESHCONS = 0x01; public const int DU_DRAWNAVMESH_OFFMESHCONS = 0x01;
public static readonly int DRAWNAVMESH_CLOSEDLIST = 0x02; public const int DU_DRAWNAVMESH_CLOSEDLIST = 0x02;
public static readonly int DRAWNAVMESH_COLOR_TILES = 0x04; public const int DU_DRAWNAVMESH_COLOR_TILES = 0x04;
public RecastDebugDraw(GL gl) : base(gl) public RecastDebugDraw(GL gl) : base(gl)
{ {
@ -101,7 +102,7 @@ public class RecastDebugDraw : DebugDraw
public void DebugDrawNavMeshWithClosedList(DtNavMesh mesh, DtNavMeshQuery query, int flags) 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) for (int i = 0; i < mesh.GetMaxTiles(); ++i)
{ {
DtMeshTile tile = mesh.GetTile(i); DtMeshTile tile = mesh.GetTile(i);
@ -116,7 +117,7 @@ public class RecastDebugDraw : DebugDraw
{ {
long @base = mesh.GetPolyRefBase(tile); long @base = mesh.GetPolyRefBase(tile);
int tileNum = DtNavMesh.DecodePolyIdTile(@base); int tileNum = DecodePolyIdTile(@base);
int tileColor = DuIntToCol(tileNum, 128); int tileColor = DuIntToCol(tileNum, 128);
DepthMask(false); DepthMask(false);
Begin(DebugDrawPrimitives.TRIS); Begin(DebugDrawPrimitives.TRIS);
@ -135,7 +136,7 @@ public class RecastDebugDraw : DebugDraw
} }
else else
{ {
if ((flags & DRAWNAVMESH_COLOR_TILES) != 0) if ((flags & DU_DRAWNAVMESH_COLOR_TILES) != 0)
{ {
col = tileColor; col = tileColor;
} }
@ -163,7 +164,7 @@ public class RecastDebugDraw : DebugDraw
// Draw outer poly boundaries // Draw outer poly boundaries
DrawPolyBoundaries(tile, DuRGBA(0, 48, 64, 220), 2.5f, false); 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); Begin(DebugDrawPrimitives.LINES, 2.0f);
for (int i = 0; i < tile.data.header.polyCount; ++i) 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. // Check to see if start and end end-points have links.
bool startSet = false; bool startSet = false;
bool endSet = 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) if (tile.links[k].edge == 0)
{ {
@ -299,6 +300,7 @@ public class RecastDebugDraw : DebugDraw
Begin(DebugDrawPrimitives.LINES, linew); Begin(DebugDrawPrimitives.LINES, linew);
Span<RcVec3f> tv = stackalloc RcVec3f[3];
for (int i = 0; i < tile.data.header.polyCount; ++i) for (int i = 0; i < tile.data.header.polyCount; ++i)
{ {
DtPoly p = tile.data.polys[i]; DtPoly p = tile.data.polys[i];
@ -318,10 +320,10 @@ public class RecastDebugDraw : DebugDraw
continue; continue;
} }
if ((p.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) if ((p.neis[j] & DT_EXT_LINK) != 0)
{ {
bool con = false; 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) if (tile.links[k].edge == j)
{ {
@ -370,14 +372,14 @@ public class RecastDebugDraw : DebugDraw
for (int k = 0; k < pd.triCount; ++k) for (int k = 0; k < pd.triCount; ++k)
{ {
int t = (pd.triBase + k) * 4; int t = (pd.triBase + k) * 4;
RcVec3f[] tv = new RcVec3f[3];
for (int m = 0; m < 3; ++m) for (int m = 0; m < 3; ++m)
{ {
int v = tile.data.detailTris[t + m]; int v = tile.data.detailTris[t + m];
if (v < p.vertCount) if (v < p.vertCount)
{ {
tv[m] = new RcVec3f( 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] 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++) 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; continue;
if (((tile.data.detailTris[t + 3] >> (n * 2)) & 0x3) == 0) if (((tile.data.detailTris[t + 3] >> (n * 2)) & 0x3) == 0)
@ -463,9 +465,13 @@ public class RecastDebugDraw : DebugDraw
continue; continue;
} }
AppendBoxWire(tile.data.header.bmin.X + n.bmin[0] * cs, tile.data.header.bmin.Y + n.bmin[1] * cs, AppendBoxWire(
tile.data.header.bmin.Z + n.bmin[2] * cs, tile.data.header.bmin.X + n.bmax[0] * cs, tile.data.header.bmin.X + n.bmin.X * cs,
tile.data.header.bmin.Y + n.bmax[1] * cs, tile.data.header.bmin.Z + n.bmax[2] * 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)); DuRGBA(255, 255, 255, 128));
} }
@ -497,7 +503,7 @@ public class RecastDebugDraw : DebugDraw
{ {
color = DuRGBA(0, 192, 255, 64); 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); color = DuRGBA(0, 0, 0, 64);
} }
@ -669,7 +675,7 @@ public class RecastDebugDraw : DebugDraw
int v3 = c.rverts[j * 4 + 3]; int v3 = c.rverts[j * 4 + 3];
float off = 0; float off = 0;
int colv = color; int colv = color;
if ((v3 & RcConstants.RC_BORDER_VERTEX) != 0) if ((v3 & RcRecast.RC_BORDER_VERTEX) != 0)
{ {
colv = DuRGBA(255, 255, 255, a); colv = DuRGBA(255, 255, 255, a);
off = ch * 2; off = ch * 2;
@ -716,7 +722,7 @@ public class RecastDebugDraw : DebugDraw
int vb0 = c.verts[j * 4]; int vb0 = c.verts[j * 4];
int vb1 = c.verts[j * 4 + 1]; int vb1 = c.verts[j * 4 + 1];
int vb2 = c.verts[j * 4 + 2]; 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 fx = orig.X + va0 * cs;
float fy = orig.Y + (va1 + 1 + (i & 1)) * ch; float fy = orig.Y + (va1 + 1 + (i & 1)) * ch;
@ -747,7 +753,7 @@ public class RecastDebugDraw : DebugDraw
int v3 = c.verts[j * 4 + 3]; int v3 = c.verts[j * 4 + 3];
float off = 0; float off = 0;
int colv = color; int colv = color;
if ((v3 & RcConstants.RC_BORDER_VERTEX) != 0) if ((v3 & RcRecast.RC_BORDER_VERTEX) != 0)
{ {
colv = DuRGBA(255, 255, 255, a); colv = DuRGBA(255, 255, 255, a);
off = ch * 2; off = ch * 2;
@ -827,7 +833,7 @@ public class RecastDebugDraw : DebugDraw
{ {
fcol[0] = DuRGBA(64, 128, 160, 255); 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); fcol[0] = DuRGBA(64, 64, 64, 255);
} }
@ -949,7 +955,7 @@ public class RecastDebugDraw : DebugDraw
{ {
color = DuRGBA(0, 192, 255, 64); 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); color = DuRGBA(0, 0, 0, 64);
} }
@ -961,7 +967,7 @@ public class RecastDebugDraw : DebugDraw
int[] vi = new int[3]; int[] vi = new int[3];
for (int j = 2; j < nvp; ++j) 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; break;
} }
@ -992,7 +998,7 @@ public class RecastDebugDraw : DebugDraw
int p = i * nvp * 2; int p = i * nvp * 2;
for (int j = 0; j < nvp; ++j) 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; break;
} }
@ -1002,7 +1008,7 @@ public class RecastDebugDraw : DebugDraw
continue; 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[] vi = { mesh.polys[p + j], mesh.polys[p + nj] };
for (int k = 0; k < 2; ++k) for (int k = 0; k < 2; ++k)
@ -1026,7 +1032,7 @@ public class RecastDebugDraw : DebugDraw
int p = i * nvp * 2; int p = i * nvp * 2;
for (int j = 0; j < nvp; ++j) 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; break;
} }
@ -1036,7 +1042,7 @@ public class RecastDebugDraw : DebugDraw
continue; 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[] vi = { mesh.polys[p + j], mesh.polys[p + nj] };
int col = colb; int col = colb;
@ -1327,7 +1333,7 @@ public class RecastDebugDraw : DebugDraw
for (int side = 0; side < 8; ++side) 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) for (int i = 0; i < tile.data.header.polyCount; ++i)
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using DotRecast.Core; using DotRecast.Core;
using K4os.Compression.LZ4; using K4os.Compression.LZ4;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo; namespace DotRecast.Recast.Demo;
public static class KeyModState public static class KeyModState
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public class GeomLoadBeganEvent : IRecastDemoMessage public class GeomLoadBeganEvent : IRecastDemoMessage
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public interface IRecastDemoChannel public interface IRecastDemoChannel
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public class IRecastDemoMessage public class IRecastDemoMessage
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public class NavMeshBuildBeganEvent : IRecastDemoMessage public class NavMeshBuildBeganEvent : IRecastDemoMessage
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public class NavMeshLoadBeganEvent : IRecastDemoMessage public class NavMeshLoadBeganEvent : IRecastDemoMessage
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;
public class NavMeshSaveBeganEvent : IRecastDemoMessage public class NavMeshSaveBeganEvent : IRecastDemoMessage
{ {

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Demo.Messages; namespace DotRecast.Recast.Demo.Messages;

View File

@ -1,8 +1,8 @@
using System.IO; using System.IO;
using System.Threading;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Recast.Demo.Logging.Sinks; using DotRecast.Recast.Demo.Logging.Sinks;
using Serilog; using Serilog;
using Serilog.Enrichers;
namespace DotRecast.Recast.Demo; namespace DotRecast.Recast.Demo;
@ -10,6 +10,8 @@ public static class Program
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
Thread.CurrentThread.Name ??= "main";
InitializeWorkingDirectory(); InitializeWorkingDirectory();
InitializeLogger(); InitializeLogger();
StartDemo(); StartDemo();
@ -22,7 +24,6 @@ public static class Program
.MinimumLevel.Verbose() .MinimumLevel.Verbose()
.Enrich.WithThreadId() .Enrich.WithThreadId()
.Enrich.WithThreadName() .Enrich.WithThreadName()
.Enrich.WithProperty(ThreadNameEnricher.ThreadNamePropertyName, "main")
.WriteTo.Async(c => c.LogMessageBroker(outputTemplate: format)) .WriteTo.Async(c => c.LogMessageBroker(outputTemplate: format))
.WriteTo.Async(c => c.Console(outputTemplate: format)) .WriteTo.Async(c => c.Console(outputTemplate: format))
.WriteTo.Async(c => c.File( .WriteTo.Async(c => c.File(

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime;
using System.Runtime.InteropServices;
using DotRecast.Core; using DotRecast.Core;
using Serilog; using Serilog;
using Silk.NET.Input; using Silk.NET.Input;
@ -318,7 +320,7 @@ public class RecastDemo : IRecastDemoChannel
if (null != mesh) if (null != mesh)
{ {
_sample.Update(_sample.GetInputGeom(), ImmutableArray<RcBuilderResult>.Empty, mesh); _sample.Update(_sample.GetInputGeom(), _sample.GetRecastConfig(), ImmutableArray<RcBuilderResult>.Empty, mesh);
_toolsetView.SetEnabled(true); _toolsetView.SetEnabled(true);
} }
} }
@ -409,9 +411,17 @@ public class RecastDemo : IRecastDemoChannel
var workingDirectory = Directory.GetCurrentDirectory(); var workingDirectory = Directory.GetCurrentDirectory();
Logger.Information($"Working directory - {workingDirectory}"); 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($"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($"{vendor} {rendererGl}");
Logger.Information($"gl version({version}) lang version({glslString})"); Logger.Information($"gl version({version}) lang version({glslString})");
} }
@ -462,7 +472,7 @@ public class RecastDemo : IRecastDemoChannel
var settings = _sample.GetSettings(); var settings = _sample.GetSettings();
RcVec3f bmin = _sample.GetInputGeom().GetMeshBoundsMin(); RcVec3f bmin = _sample.GetInputGeom().GetMeshBoundsMin();
RcVec3f bmax = _sample.GetInputGeom().GetMeshBoundsMax(); 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.SetVoxels(gw, gh);
settingsView.SetTiles(tileNavMeshBuilder.GetTiles(_sample.GetInputGeom(), settings.cellSize, settings.tileSize)); settingsView.SetTiles(tileNavMeshBuilder.GetTiles(_sample.GetInputGeom(), settings.cellSize, settings.tileSize));
settingsView.SetMaxTiles(tileNavMeshBuilder.GetMaxTiles(_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); var geom = LoadInputMesh(args.FilePath);
_sample.Update(geom, ImmutableArray<RcBuilderResult>.Empty, null); _sample.Update(geom, null, ImmutableArray<RcBuilderResult>.Empty, null);
} }
private void OnNavMeshBuildBegan(NavMeshBuildBeganEvent args) private void OnNavMeshBuildBegan(NavMeshBuildBeganEvent args)
@ -709,7 +719,7 @@ public class RecastDemo : IRecastDemoChannel
return; return;
} }
_sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh); _sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh);
_sample.SetChanged(false); _sample.SetChanged(false);
settingsView.SetBuildTime((RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond); settingsView.SetBuildTime((RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond);
//settingsUI.SetBuildTelemetry(buildResult.Item1.Select(x => x.GetTelemetry()).ToList()); //settingsUI.SetBuildTelemetry(buildResult.Item1.Select(x => x.GetTelemetry()).ToList());

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Collections.Generic;
using System.Linq;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour; using DotRecast.Detour;
using DotRecast.Detour.Crowd; using DotRecast.Detour.Crowd;
using DotRecast.Recast.Toolset.Builder;
using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Draw;
using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset;
using DotRecast.Recast.Toolset.Tools; using DotRecast.Recast.Toolset.Tools;
@ -98,6 +95,11 @@ public class CrowdAgentProfilingSampleTool : ISampleTool
ImGui.SliderInt("Max Iterations", ref toolCfg.maxIterations, 0, 4000); ImGui.SliderInt("Max Iterations", ref toolCfg.maxIterations, 0, 4000);
ImGui.NewLine(); ImGui.NewLine();
ImGui.Text("Debug Draw");
ImGui.Separator();
ImGui.Checkbox("Show Agents", ref toolCfg.showAgents);
ImGui.NewLine();
if (ImGui.Button("Start Crowd Profiling")) if (ImGui.Button("Start Crowd Profiling"))
{ {
var settings = _sample.GetSettings(); var settings = _sample.GetSettings();
@ -118,11 +120,11 @@ public class CrowdAgentProfilingSampleTool : ISampleTool
ImGui.Text($"{rtt.Key}: {rtt.Micros} us"); ImGui.Text($"{rtt.Key}: {rtt.Micros} us");
} }
ImGui.Text($"Sampling Time: {_tool.GetCrowdUpdateSamplingTime()} ms"); ImGui.Text($"Sampling Time: {_tool.GetCrowdUpdateSamplingTime():0.00} ms");
ImGui.Text($"Current Update Time: {_tool.GetCrowdUpdateTime()} ms"); ImGui.Text($"Current Update Time: {_tool.GetCrowdUpdateTime():0.00} ms");
ImGui.Text($"Avg Update Time: {_tool.GetCrowdUpdateAvgTime()} ms"); ImGui.Text($"Avg Update Time: {_tool.GetCrowdUpdateAvgTime():0.00} ms");
ImGui.Text($"Max Update Time: {_tool.GetCrowdUpdateMaxTime()} ms"); ImGui.Text($"Max Update Time: {_tool.GetCrowdUpdateMaxTime():0.00} ms");
ImGui.Text($"Min Update Time: {_tool.GetCrowdUpdateMinTime()} ms"); ImGui.Text($"Min Update Time: {_tool.GetCrowdUpdateMinTime():0.00} ms");
} }
} }
@ -132,7 +134,7 @@ public class CrowdAgentProfilingSampleTool : ISampleTool
dd.DepthMask(false); dd.DepthMask(false);
var crowd = _tool.GetCrowd(); var crowd = _tool.GetCrowd();
if (crowd != null) if (crowd != null && _tool.GetToolConfig().showAgents)
{ {
foreach (DtCrowdAgent ag in crowd.GetActiveAgents()) foreach (DtCrowdAgent ag in crowd.GetActiveAgents())
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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(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))); dd.Vertex(trail.trail[v], trail.trail[v + 1] + 0.1f, trail.trail[v + 2], DuRGBA(0, 0, 0, (int)(128 * a)));
preva = a; preva = a;
prev = RcVecUtils.Create(trail.trail, v); prev = RcVec.Create(trail.trail, v);
} }
dd.End(); dd.End();
@ -251,10 +251,10 @@ public class CrowdSampleTool : ISampleTool
if (_showCorners) if (_showCorners)
{ {
if (0 < ag.corners.Count) if (0 < ag.ncorners)
{ {
dd.Begin(LINES, 2.0f); 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 va = j == 0 ? pos : ag.corners[j - 1].pos;
RcVec3f vb = ag.corners[j].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)); 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) & 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, v.Z, DuRGBA(192, 0, 0, 192));
dd.Vertex(v.X, v.Y + radius * 2, 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); 2.0f);
dd.Begin(LINES, 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; DtCrowdAgent nei = ag.neis[j].agent;
if (nei != null) if (nei != null)

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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.Collections;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour.Dynamic; using DotRecast.Detour.Dynamic;
using DotRecast.Detour.Dynamic.Io;
using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset;
using DotRecast.Recast.Toolset.Tools; using DotRecast.Recast.Toolset.Tools;
using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Draw;
@ -116,8 +117,17 @@ public class DynamicUpdateSampleTool : ISampleTool
if (mode == RcDynamicUpdateToolMode.BUILD) if (mode == RcDynamicUpdateToolMode.BUILD)
{ {
var loadVoxelPopupStrId = "Load Voxels Popup"; const string loadVoxelPopupStrId = "Load Voxels Popup";
bool isLoadVoxelPopup = true; bool isLoadVoxelPopup = true;
if (_sample.GetRecastResults() != null && _sample.GetRecastConfig() != null)
{
if (ImGui.Button("Import Voxels"))
{
Copy();
}
}
if (ImGui.Button("Load Voxels...")) if (ImGui.Button("Load Voxels..."))
{ {
ImGui.OpenPopup(loadVoxelPopupStrId); ImGui.OpenPopup(loadVoxelPopupStrId);
@ -135,7 +145,7 @@ public class DynamicUpdateSampleTool : ISampleTool
ImGui.EndPopup(); ImGui.EndPopup();
} }
var saveVoxelPopupStrId = "Save Voxels Popup"; const string saveVoxelPopupStrId = "Save Voxels Popup";
bool isSaveVoxelPopup = true; bool isSaveVoxelPopup = true;
var dynaMesh = _tool.GetDynamicNavMesh(); var dynaMesh = _tool.GetDynamicNavMesh();
@ -144,7 +154,7 @@ public class DynamicUpdateSampleTool : ISampleTool
ImGui.Checkbox("Compression", ref compression); ImGui.Checkbox("Compression", ref compression);
if (ImGui.Button("Save Voxels...")) if (ImGui.Button("Save Voxels..."))
{ {
ImGui.BeginPopup(saveVoxelPopupStrId); ImGui.OpenPopup(saveVoxelPopupStrId);
} }
if (ImGui.BeginPopupModal(saveVoxelPopupStrId, ref isSaveVoxelPopup, ImGuiWindowFlags.NoTitleBar)) 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"); var picker = ImFilePicker.GetFilePicker(saveVoxelPopupStrId, Path.Combine(Environment.CurrentDirectory), ".voxels");
if (picker.Draw()) if (picker.Draw())
{ {
if (string.IsNullOrEmpty(picker.SelectedFile))
Save(picker.SelectedFile); Save(picker.SelectedFile);
ImFilePicker.RemoveFilePicker(saveVoxelPopupStrId); ImFilePicker.RemoveFilePicker(saveVoxelPopupStrId);
} }
@ -411,7 +419,7 @@ public class DynamicUpdateSampleTool : ISampleTool
{ {
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond; buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond;
var dynaMesh = _tool.GetDynamicNavMesh(); var dynaMesh = _tool.GetDynamicNavMesh();
_sample.Update(null, dynaMesh.RecastResults(), dynaMesh.NavMesh()); _sample.Update(null, null, dynaMesh.RecastResults(), dynaMesh.NavMesh());
_sample.SetChanged(false); _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) private void Load(string filename)
{ {
@ -458,7 +475,7 @@ public class DynamicUpdateSampleTool : ISampleTool
} }
buildTime = (RcFrequency.Ticks - t) / TimeSpan.TicksPerMillisecond; 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) private void UpdateTo(DtDynamicNavMeshConfig config)

View File

@ -1,4 +1,4 @@
using System; using System;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Recast.Demo.Draw; 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(); RcVec3f normal = new RcVec3f();
for (int j = 0; j < 3; ++j) RcVec3f e0 = v1 - v0;
{ RcVec3f e1 = v2 - v0;
e0 = RcVecUtils.Subtract(vertices, v1, v0);
e1 = RcVecUtils.Subtract(vertices, v2, v0);
}
normal.X = e0.Y * e1.Z - e0.Z * e1.Y; normal.X = e0.Y * e1.Z - e0.Z * e1.Y;
normal.Y = e0.Z * e1.X - e0.X * e1.Z; 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 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 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); var trZ = new RcVec3f(box.halfEdges[0].Z, box.halfEdges[1].Z, box.halfEdges[2].Z);
float[] vertices = new float[8 * 3]; Span<float> vertices = stackalloc float[8 * 3];
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
vertices[i * 3 + 0] = RcVec3f.Dot(RcBoxGizmo.VERTS[i], trX) + box.center.X; 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); debugDraw.Begin(DebugDrawPrimitives.TRIS);
for (int i = 0; i < trimesh.triangles.Length; i += 3) for (int i = 0; i < trimesh.triangles.Length; i += 3)
{ {
int v0 = 3 * trimesh.triangles[i]; RcVec3f v0 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i]);
int v1 = 3 * trimesh.triangles[i + 1]; RcVec3f v1 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i + 1]);
int v2 = 3 * trimesh.triangles[i + 2]; RcVec3f v2 = RcVec.Create(trimesh.vertices, 3 * trimesh.triangles[i + 2]);
int col = GetColorByNormal(trimesh.vertices, v0, v1, v2); int col = GetColorByNormal(v0, v1, v2);
debugDraw.Vertex(trimesh.vertices[v0], trimesh.vertices[v0 + 1], trimesh.vertices[v0 + 2], col); debugDraw.Vertex(v0.X, v0.Y, v0.Z, col);
debugDraw.Vertex(trimesh.vertices[v1], trimesh.vertices[v1 + 1], trimesh.vertices[v1 + 2], col); debugDraw.Vertex(v1.X, v1.Y, v1.Z, col);
debugDraw.Vertex(trimesh.vertices[v2], trimesh.vertices[v2 + 1], trimesh.vertices[v2 + 2], col); debugDraw.Vertex(v2.X, v2.Y, v2.Z, col);
} }
debugDraw.End(); debugDraw.End();

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

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

View File

@ -1,4 +1,4 @@
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour.TileCache; using DotRecast.Detour.TileCache;
using DotRecast.Detour.TileCache.Io.Compress; using DotRecast.Detour.TileCache.Io.Compress;
@ -32,7 +32,7 @@ public class ObstacleSampleTool : ISampleTool
var buildResult = _tool.Build(geom, settings, RcByteOrder.LITTLE_ENDIAN, true); var buildResult = _tool.Build(geom, settings, RcByteOrder.LITTLE_ENDIAN, true);
if (buildResult.Success) if (buildResult.Success)
{ {
_sample.Update(_sample.GetInputGeom(), buildResult.RecastBuilderResults, buildResult.NavMesh); _sample.Update(_sample.GetInputGeom(), buildResult.Cfg, buildResult.RecastBuilderResults, buildResult.NavMesh);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -57,7 +57,8 @@ public class TestNavmeshSampleTool : ISampleTool
private bool m_hitResult; private bool m_hitResult;
private float m_distanceToWall; private float m_distanceToWall;
private List<DtStraightPath> m_straightPath; private DtStraightPath[] m_straightPath;
private int m_straightPathCount;
private List<long> m_polys; private List<long> m_polys;
private List<long> m_parent; private List<long> m_parent;
private float m_neighbourhoodRadius; private float m_neighbourhoodRadius;
@ -77,6 +78,8 @@ public class TestNavmeshSampleTool : ISampleTool
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
new float[] { 1f, 1f, 1f, 1f, 2f, 1.5f } new float[] { 1f, 1f, 1f, 1f, 2f, 1.5f }
); );
m_straightPath = new DtStraightPath[MAX_POLYS];
m_straightPathCount = 0;
} }
public void Layout() public void Layout()
@ -137,22 +140,22 @@ public class TestNavmeshSampleTool : ISampleTool
ImGui.Text("Common"); ImGui.Text("Common");
ImGui.Separator(); ImGui.Separator();
ImGui.Text("Include Flags"); ImGui.Text("+ Include Flags");
ImGui.Separator(); ImGui.Separator();
ImGui.CheckboxFlags("Walk", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); ImGui.CheckboxFlags("+ Walk", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK);
ImGui.CheckboxFlags("Swim", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); ImGui.CheckboxFlags("+ Swim", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM);
ImGui.CheckboxFlags("Door", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); ImGui.CheckboxFlags("+ Door", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR);
ImGui.CheckboxFlags("Jump", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); ImGui.CheckboxFlags("+ Jump", ref _includeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP);
ImGui.NewLine(); ImGui.NewLine();
m_filter.SetIncludeFlags(_includeFlags); m_filter.SetIncludeFlags(_includeFlags);
ImGui.Text("Exclude Flags"); ImGui.Text("- Exclude Flags");
ImGui.Separator(); ImGui.Separator();
ImGui.CheckboxFlags("Walk", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK); ImGui.CheckboxFlags("- Walk", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_WALK);
ImGui.CheckboxFlags("Swim", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM); ImGui.CheckboxFlags("- Swim", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_SWIM);
ImGui.CheckboxFlags("Door", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR); ImGui.CheckboxFlags("- Door", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_DOOR);
ImGui.CheckboxFlags("Jump", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP); ImGui.CheckboxFlags("- Jump", ref _excludeFlags, SampleAreaModifications.SAMPLE_POLYFLAGS_JUMP);
ImGui.NewLine(); ImGui.NewLine();
m_filter.SetExcludeFlags(_excludeFlags); m_filter.SetExcludeFlags(_excludeFlags);
@ -284,7 +287,7 @@ public class TestNavmeshSampleTool : ISampleTool
int spathCol = DuRGBA(64, 16, 0, 220); int spathCol = DuRGBA(64, 16, 0, 220);
int offMeshCol = DuRGBA(128, 96, 0, 220); int offMeshCol = DuRGBA(128, 96, 0, 220);
dd.Begin(LINES, 2.0f); 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 straightPathItem = m_straightPath[i];
DtStraightPath straightPathItem2 = m_straightPath[i + 1]; DtStraightPath straightPathItem2 = m_straightPath[i + 1];
@ -304,7 +307,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.End(); dd.End();
dd.Begin(POINTS, 6.0f); 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]; DtStraightPath straightPathItem = m_straightPath[i];
int col; int col;
@ -349,7 +352,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.DepthMask(false); dd.DepthMask(false);
int spathCol = m_hitResult ? DuRGBA(64, 16, 0, 220) : DuRGBA(240, 240, 240, 220); int spathCol = m_hitResult ? DuRGBA(64, 16, 0, 220) : DuRGBA(240, 240, 240, 220);
dd.Begin(LINES, 2.0f); 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 straightPathItem = m_straightPath[i];
DtStraightPath straightPathItem2 = m_straightPath[i + 1]; DtStraightPath straightPathItem2 = m_straightPath[i + 1];
@ -359,7 +362,7 @@ public class TestNavmeshSampleTool : ISampleTool
dd.End(); dd.End();
dd.Begin(POINTS, 4.0f); 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]; DtStraightPath straightPathItem = m_straightPath[i];
dd.Vertex(straightPathItem.pos.X, straightPathItem.pos.Y + 0.4f, straightPathItem.pos.Z, spathCol); 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) if (m_polys != null)
{ {
var segmentVerts = new List<RcSegmentVert>(); const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
var segmentRefs = new List<long>(); Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
for (int i = 0; i < m_polys.Count; i++) for (int i = 0; i < m_polys.Count; i++)
{ {
@ -488,18 +492,20 @@ public class TestNavmeshSampleTool : ISampleTool
dd.DepthMask(true); dd.DepthMask(true);
if (_sample.GetNavMeshQuery() != null) if (_sample.GetNavMeshQuery() != null)
{ {
int nsegs = 0;
var result = _sample var result = _sample
.GetNavMeshQuery() .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()) if (result.Succeeded())
{ {
dd.Begin(LINES, 2.0f); 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 v0 = s.vmin;
var s3 = s.vmax; var s3 = s.vmax;
// Skip too distant segments. // Skip too distant segments.
var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg); var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg);
if (distSqr > RcMath.Sqr(m_neighbourhoodRadius)) if (distSqr > RcMath.Sqr(m_neighbourhoodRadius))
@ -508,12 +514,13 @@ public class TestNavmeshSampleTool : ISampleTool
} }
RcVec3f delta = RcVec3f.Subtract(s3, s.vmin); 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); RcVec3f norm = new RcVec3f(delta.Z, 0, -delta.X);
norm = RcVec3f.Normalize(norm); 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. // Skip backfacing segments.
if (segmentRefs[j] != 0) if (refs[j] != 0)
{ {
int col = DuRGBA(255, 255, 255, 32); int col = DuRGBA(255, 255, 255, 32);
dd.Vertex(s.vmin.X, s.vmin.Y + agentClimb, s.vmin.Z, col); 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) if (_mode == RcTestNavmeshToolMode.PATHFIND_FOLLOW)
{ {
_tool.FindFollowPath(navMesh, navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast, _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) else if (_mode == RcTestNavmeshToolMode.PATHFIND_STRAIGHT)
{ {
_tool.FindStraightPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast, _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) else if (_mode == RcTestNavmeshToolMode.PATHFIND_SLICED)
{ {
m_polys?.Clear(); 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); m_pathFindStatus = _tool.InitSlicedFindPath(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _enableRaycast);
} }
else if (_mode == RcTestNavmeshToolMode.RAYCAST) else if (_mode == RcTestNavmeshToolMode.RAYCAST)
{ {
_tool.Raycast(navQuery, m_startRef, m_endRef, m_spos, m_epos, m_filter, _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) else if (_mode == RcTestNavmeshToolMode.DISTANCE_TO_WALL)
{ {
@ -712,7 +719,7 @@ public class TestNavmeshSampleTool : ISampleTool
if (m_pathFindStatus.InProgress()) 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);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Draw;
using DotRecast.Recast.Toolset; using DotRecast.Recast.Toolset;

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@ -69,7 +69,7 @@ public class ImFilePicker
ImGui.Text("Current Folder: " + CurrentFolder); ImGui.Text("Current Folder: " + CurrentFolder);
bool result = false; bool result = false;
if (ImGui.BeginChildFrame(1, new Vector2(1024, 400))) if (ImGui.BeginChild(1, new Vector2(1024, 400)))
{ {
var di = new DirectoryInfo(CurrentFolder); var di = new DirectoryInfo(CurrentFolder);
if (di.Exists) if (di.Exists)
@ -111,7 +111,7 @@ public class ImFilePicker
} }
} }
ImGui.EndChildFrame(); ImGui.EndChild();
if (ImGui.Button("Cancel")) if (ImGui.Button("Cancel"))

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; 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); _isHovered = ImGui.IsWindowHovered(ImGuiHoveredFlags.RectOnly | ImGuiHoveredFlags.RootAndChildWindows);

View File

@ -1,4 +1,4 @@
using DotRecast.Core; using DotRecast.Core;
using ImGuiNET; using ImGuiNET;
namespace DotRecast.Recast.Demo.UI; namespace DotRecast.Recast.Demo.UI;

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Demo.UI.ViewModels; namespace DotRecast.Recast.Demo.UI.ViewModels;
public class LogMessageItem public class LogMessageItem
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Detour; using DotRecast.Detour;
@ -7,6 +7,7 @@ namespace DotRecast.Recast.Toolset.Builder
public class NavMeshBuildResult public class NavMeshBuildResult
{ {
public readonly bool Success; public readonly bool Success;
public readonly RcConfig Cfg;
public readonly IList<RcBuilderResult> RecastBuilderResults; public readonly IList<RcBuilderResult> RecastBuilderResults;
public readonly DtNavMesh NavMesh; public readonly DtNavMesh NavMesh;
@ -17,11 +18,22 @@ namespace DotRecast.Recast.Toolset.Builder
NavMesh = null; NavMesh = null;
} }
public NavMeshBuildResult(IList<RcBuilderResult> recastBuilderResults, DtNavMesh navMesh) // for solo
public NavMeshBuildResult(RcConfig cfg, IList<RcBuilderResult> recastBuilderResults, DtNavMesh navMesh)
{ {
Success = true; Success = true;
Cfg = cfg;
RecastBuilderResults = recastBuilderResults; RecastBuilderResults = recastBuilderResults;
NavMesh = navMesh; NavMesh = navMesh;
} }
// for tiles
public NavMeshBuildResult(RcConfig cfg, IList<RcBuilderResult> recastBuilderResults)
{
Success = true;
Cfg = cfg;
RecastBuilderResults = recastBuilderResults;
NavMesh = null;
}
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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_JUMP_AUTO = 0x6;
public const int SAMPLE_POLYAREA_TYPE_WALKABLE = 0x3f; public const int SAMPLE_POLYAREA_TYPE_WALKABLE = 0x3f;
public static readonly int SAMPLE_POLYFLAGS_WALK = 0x01; // Ability to walk (ground, grass, road) public const int SAMPLE_POLYFLAGS_WALK = 0x01; // Ability to walk (ground, grass, road)
public static readonly int SAMPLE_POLYFLAGS_SWIM = 0x02; // Ability to swim (water). public const int SAMPLE_POLYFLAGS_SWIM = 0x02; // Ability to swim (water).
public static readonly int SAMPLE_POLYFLAGS_DOOR = 0x04; // Ability to move through doors. public const int SAMPLE_POLYFLAGS_DOOR = 0x04; // Ability to move through doors.
public static readonly int SAMPLE_POLYFLAGS_JUMP = 0x08; // Ability to jump. public const int SAMPLE_POLYFLAGS_JUMP = 0x08; // Ability to jump.
public static readonly int SAMPLE_POLYFLAGS_DISABLED = 0x10; // Disabled polygon public const int SAMPLE_POLYFLAGS_DISABLED = 0x10; // Disabled polygon
public static readonly int SAMPLE_POLYFLAGS_ALL = 0xffff; // All abilities. 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_WALKABLE = new RcAreaModification(SAMPLE_POLYAREA_TYPE_WALKABLE);
public static readonly RcAreaModification SAMPLE_AREAMOD_GROUND = new RcAreaModification(SAMPLE_POLYAREA_TYPE_GROUND); public static readonly RcAreaModification SAMPLE_AREAMOD_GROUND = new RcAreaModification(SAMPLE_POLYAREA_TYPE_GROUND);

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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); 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) 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) private RcBuilderResult BuildRecastResult(DemoInputGeomProvider geom, RcConfig cfg, bool keepInterResults)

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 filterLowHangingObstacles, bool filterLedgeSpans, bool filterWalkableLowHeightSpans,
bool keepInterResults, bool buildAll) bool keepInterResults, bool buildAll)
{ {
List<RcBuilderResult> results = BuildRecastResult( NavMeshBuildResult result = BuildRecastResult(
geom, geom,
tileSize, tileSize,
partitionType, partitionType,
@ -71,12 +72,12 @@ namespace DotRecast.Recast.Toolset.Builder
keepInterResults, buildAll 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); var tileNavMesh = BuildNavMesh(geom, tileMeshData, cellSize, tileSize, vertsPerPoly);
return new NavMeshBuildResult(results, tileNavMesh); return new NavMeshBuildResult(result.Cfg, result.RecastBuilderResults, tileNavMesh);
} }
public List<RcBuilderResult> BuildRecastResult(IInputGeomProvider geom, public NavMeshBuildResult BuildRecastResult(IInputGeomProvider geom,
int tileSize, int tileSize,
RcPartition partitionType, RcPartition partitionType,
float cellSize, float cellHeight, float cellSize, float cellHeight,
@ -101,7 +102,8 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
RcBuilder rcBuilder = new RcBuilder(); 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<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly) public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly)
@ -115,8 +117,9 @@ namespace DotRecast.Recast.Toolset.Builder
navMeshParams.maxTiles = GetMaxTiles(geom, cellSize, tileSize); navMeshParams.maxTiles = GetMaxTiles(geom, cellSize, tileSize);
navMeshParams.maxPolys = GetMaxPolysPerTile(geom, cellSize, tileSize); navMeshParams.maxPolys = GetMaxPolysPerTile(geom, cellSize, tileSize);
DtNavMesh navMesh = new DtNavMesh(navMeshParams, vertsPerPoly); DtNavMesh navMesh = new DtNavMesh();
meshData.ForEach(md => navMesh.AddTile(md, 0, 0)); navMesh.Init(navMeshParams, vertsPerPoly);
meshData.ForEach(md => navMesh.AddTile(md, 0, 0, out _));
return navMesh; return navMesh;
} }
@ -158,7 +161,7 @@ namespace DotRecast.Recast.Toolset.Builder
private int GetTileBits(IInputGeomProvider geom, float cellSize, int tileSize) 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 tw = (gw + tileSize - 1) / tileSize;
int th = (gh + tileSize - 1) / tileSize; int th = (gh + tileSize - 1) / tileSize;
int tileBits = Math.Min(DtUtils.Ilog2(DtUtils.NextPow2(tw * th)), 14); 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) 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 tw = (gw + tileSize - 1) / tileSize;
int th = (gh + tileSize - 1) / tileSize; int th = (gh + tileSize - 1) / tileSize;
return new int[] { tw, th }; return new int[] { tw, th };

View File

@ -1,4 +1,4 @@
using DotRecast.Detour; using DotRecast.Detour;
using DotRecast.Detour.TileCache; using DotRecast.Detour.TileCache;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Toolset.Builder;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public static DemoInputGeomProvider LoadFile(string objFilePath)
{ {
byte[] chunk = RcResources.Load(objFilePath); byte[] chunk = RcIO.ReadFileIfFound(objFilePath);
var context = RcObjImporter.LoadContext(chunk); var context = RcObjImporter.LoadContext(chunk);
return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces); return new DemoInputGeomProvider(context.vertexPositions, context.meshFaces);
} }
@ -57,12 +57,12 @@ namespace DotRecast.Recast.Toolset.Geom
this.faces = faces; this.faces = faces;
normals = new float[faces.Length]; normals = new float[faces.Length];
CalculateNormals(); CalculateNormals();
bmin = RcVecUtils.Create(vertices); bmin = new RcVec3f(vertices);
bmax = RcVecUtils.Create(vertices); bmax = new RcVec3f(vertices);
for (int i = 1; i < vertices.Length / 3; i++) for (int i = 1; i < vertices.Length / 3; i++)
{ {
bmin = RcVecUtils.Min(bmin, vertices, i * 3); bmin = RcVec3f.Min(bmin, RcVec.Create(vertices, i * 3));
bmax = RcVecUtils.Max(bmax, vertices, i * 3); bmax = RcVec3f.Max(bmax, RcVec.Create(vertices, i * 3));
} }
_mesh = new RcTriMesh(vertices, faces); _mesh = new RcTriMesh(vertices, faces);
@ -87,11 +87,11 @@ namespace DotRecast.Recast.Toolset.Geom
{ {
for (int i = 0; i < faces.Length; i += 3) for (int i = 0; i < faces.Length; i += 3)
{ {
int v0 = faces[i] * 3; RcVec3f v0 = RcVec.Create(vertices, faces[i] * 3);
int v1 = faces[i + 1] * 3; RcVec3f v1 = RcVec.Create(vertices, faces[i + 1] * 3);
int v2 = faces[i + 2] * 3; RcVec3f v2 = RcVec.Create(vertices, faces[i + 2] * 3);
var e0 = RcVecUtils.Subtract(vertices, v1, v0); RcVec3f e0 = v1 - v0;
var e1 = RcVecUtils.Subtract(vertices, v2, v0); RcVec3f e1 = v2 - v0;
normals[i] = e0.Y * e1.Z - e0.Z * e1.Y; normals[i] = e0.Y * e1.Z - e0.Z * e1.Y;
normals[i + 1] = e0.Z * e1.X - e0.X * e1.Z; 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.X = src.X + (dst.X - src.X) * btmax;
q.Y = src.Z + (dst.Z - src.Z) * btmax; q.Y = src.Z + (dst.Z - src.Z) * btmax;
List<RcChunkyTriMeshNode> chunks = _mesh.chunkyTriMesh.GetChunksOverlappingSegment(p, q); List<RcChunkyTriMeshNode> chunks = RcChunkyTriMeshs.GetChunksOverlappingSegment(_mesh.chunkyTriMesh, p, q);
if (0 == chunks.Count) if (0 == chunks.Count)
{ {
return false; return false;

View File

@ -19,7 +19,7 @@ namespace DotRecast.Recast.Toolset.Gizmos
0.5f * (start.Z + end.Z) 0.5f * (start.Z + end.Z)
}; };
RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z); RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
RcVec3f[] normals = new RcVec3f[3]; Span<RcVec3f> normals = stackalloc RcVec3f[3];
normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z); normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
normals[1] = RcVec3f.Normalize(normals[1]); normals[1] = RcVec3f.Normalize(normals[1]);
normals[0] = GetSideVector(axis); normals[0] = GetSideVector(axis);

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Gizmos namespace DotRecast.Recast.Toolset.Gizmos
{ {
public class RcCompositeGizmo : IRcGizmoMeshFilter public class RcCompositeGizmo : IRcGizmoMeshFilter
{ {

View File

@ -19,7 +19,7 @@ namespace DotRecast.Recast.Toolset.Gizmos
0.5f * (start.Z + end.Z) 0.5f * (start.Z + end.Z)
); );
RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z); RcVec3f axis = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
RcVec3f[] normals = new RcVec3f[3]; Span<RcVec3f> normals = stackalloc RcVec3f[3];
normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z); normals[1] = new RcVec3f(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
normals[1] = RcVec3f.Normalize(normals[1]); normals[1] = RcVec3f.Normalize(normals[1]);
normals[0] = GetSideVector(axis); normals[0] = GetSideVector(axis);

View File

@ -1,4 +1,4 @@
using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Recast.Toolset.Gizmos; using DotRecast.Recast.Toolset.Gizmos;
namespace DotRecast.Recast.Toolset.Gizmos namespace DotRecast.Recast.Toolset.Gizmos

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Toolset.Gizmos namespace DotRecast.Recast.Toolset.Gizmos
{ {

View File

@ -4,8 +4,8 @@ namespace DotRecast.Recast.Toolset.Gizmos
{ {
public static class RcGizmoHelper public static class RcGizmoHelper
{ {
private static readonly int SEGMENTS = 16; private const int SEGMENTS = 16;
private static readonly int RINGS = 8; private const int RINGS = 8;
private static float[] sphericalVertices; private static float[] sphericalVertices;

View File

@ -1,7 +1,6 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper; using static DotRecast.Recast.Toolset.Gizmos.RcGizmoHelper;
namespace DotRecast.Recast.Toolset.Gizmos namespace DotRecast.Recast.Toolset.Gizmos
{ {
public class RcSphereGizmo : IRcGizmoMeshFilter public class RcSphereGizmo : IRcGizmoMeshFilter

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Gizmos namespace DotRecast.Recast.Toolset.Gizmos
{ {
public class RcTrimeshGizmo : IRcGizmoMeshFilter public class RcTrimeshGizmo : IRcGizmoMeshFilter
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset namespace DotRecast.Recast.Toolset
{ {
public interface IRcToolable public interface IRcToolable
{ {

View File

@ -1,8 +1,5 @@
using System;
namespace DotRecast.Recast.Toolset namespace DotRecast.Recast.Toolset
{ {
[Serializable]
public class RcNavMeshBuildSettings public class RcNavMeshBuildSettings
{ {
public float cellSize = 0.3f; public float cellSize = 0.3f;
@ -35,7 +32,7 @@ namespace DotRecast.Recast.Toolset
public bool tiled = false; public bool tiled = false;
public int tileSize = 32; public int tileSize = 32;
public bool keepInterResults = false; public bool keepInterResults = true; // full memory
public bool buildAll = true; public bool buildAll = true;
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@ -26,12 +26,12 @@ namespace DotRecast.Recast.Toolset.Tools
private readonly List<DtPolyPoint> _polyPoints; private readonly List<DtPolyPoint> _polyPoints;
private const int SamplingCount = 500; private const int SamplingCount = 500;
private long _samplingUpdateTime; private double _samplingUpdateTime;
private readonly RcCyclicBuffer<long> _updateTimes; private readonly RcCyclicBuffer<long> _updateTimes;
private long _curUpdateTime; private double _curUpdateTime;
private long _avgUpdateTime; private double _avgUpdateTime;
private long _minUpdateTime; private double _minUpdateTime;
private long _maxUpdateTime; private double _maxUpdateTime;
public RcCrowdAgentProfilingTool() public RcCrowdAgentProfilingTool()
{ {
@ -269,11 +269,11 @@ namespace DotRecast.Recast.Toolset.Tools
_updateTimes.PushBack(currentTime); _updateTimes.PushBack(currentTime);
// for benchmark // for benchmark
_samplingUpdateTime = _updateTimes.Sum() / TimeSpan.TicksPerMillisecond; _samplingUpdateTime = _updateTimes.Sum() / (double)TimeSpan.TicksPerMillisecond;
_curUpdateTime = currentTime / TimeSpan.TicksPerMillisecond; _curUpdateTime = currentTime / (double)TimeSpan.TicksPerMillisecond;
_avgUpdateTime = (long)(_updateTimes.Average() / TimeSpan.TicksPerMillisecond); _avgUpdateTime = (_updateTimes.Average() / (double)TimeSpan.TicksPerMillisecond);
_minUpdateTime = _updateTimes.Min() / TimeSpan.TicksPerMillisecond; _minUpdateTime = _updateTimes.Min() / (double)TimeSpan.TicksPerMillisecond;
_maxUpdateTime = _updateTimes.Max() / TimeSpan.TicksPerMillisecond; _maxUpdateTime = _updateTimes.Max() / (double)TimeSpan.TicksPerMillisecond;
} }
private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData) 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; return _samplingUpdateTime;
} }
public long GetCrowdUpdateTime() public double GetCrowdUpdateTime()
{ {
return _curUpdateTime; return _curUpdateTime;
} }
public long GetCrowdUpdateAvgTime() public double GetCrowdUpdateAvgTime()
{ {
return _avgUpdateTime; return _avgUpdateTime;
} }
public long GetCrowdUpdateMinTime() public double GetCrowdUpdateMinTime()
{ {
return _minUpdateTime; return _minUpdateTime;
} }
public long GetCrowdUpdateMaxTime() public double GetCrowdUpdateMaxTime()
{ {
return _maxUpdateTime; return _maxUpdateTime;
} }

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {
public class RcCrowdAgentProfilingToolConfig public class RcCrowdAgentProfilingToolConfig
{ {
@ -12,5 +12,7 @@
public float percentTravellers = 15f; public float percentTravellers = 15f;
public int pathQueueSize = 32; public int pathQueueSize = 32;
public int maxIterations = 300; public int maxIterations = 300;
public bool showAgents = true;
} }
} }

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {
public class RcCrowdAgentTrail public class RcCrowdAgentTrail
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {
public enum RcCrowdAgentType public enum RcCrowdAgentType
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -297,7 +297,7 @@ namespace DotRecast.Recast.Toolset.Tools
RcVec3f vel = RcVec3f.Subtract(tgt, pos); RcVec3f vel = RcVec3f.Subtract(tgt, pos);
vel.Y = 0.0f; vel.Y = 0.0f;
vel = RcVec3f.Normalize(vel); vel = RcVec3f.Normalize(vel);
return vel.Scale(speed); return vel * speed;
} }
public long GetCrowdUpdateTime() public long GetCrowdUpdateTime()

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {
public enum RcDynamicColliderShape public enum RcDynamicColliderShape
{ {

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -141,6 +141,16 @@ namespace DotRecast.Recast.Toolset.Tools
return colliderWithGizmo; return colliderWithGizmo;
} }
public DtDynamicNavMesh Copy(RcConfig cfg, IList<RcBuilderResult> 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) public DtDynamicNavMesh Load(string filename, IRcCompressor compressor)
{ {
using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); 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) public void Save(string filename, bool compression, IRcCompressor compressor)
{ {
DtVoxelFile voxelFile = DtVoxelFile.From(dynaMesh); 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); using var bw = new BinaryWriter(fs);
DtVoxelFileWriter writer = new DtVoxelFileWriter(compressor); DtVoxelFileWriter writer = new DtVoxelFileWriter(compressor);
writer.Write(bw, voxelFile, compression); writer.Write(bw, voxelFile, compression);

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Detour.Extras.Jumplink; using DotRecast.Detour.Extras.Jumplink;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
@ -116,7 +116,7 @@ namespace DotRecast.Recast.Toolset.Tools
{ {
RcVec3f p = link.startSamples[i].p; RcVec3f p = link.startSamples[i].p;
RcVec3f q = link.endSamples[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); geom.AddOffMeshConnection(p, q, agentRadius, false, area, flags);
prev = p; prev = p;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Linq; using System.Linq;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
@ -42,7 +42,7 @@ namespace DotRecast.Recast.Toolset.Tools
// Init cache // Init cache
var bmin = geom.GetMeshBoundsMin(); var bmin = geom.GetMeshBoundsMin();
var bmax = geom.GetMeshBoundsMax(); 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 ts = setting.tileSize;
int tw = (gw + ts - 1) / ts; int tw = (gw + ts - 1) / ts;
int th = (gh + ts - 1) / ts; int th = (gh + ts - 1) / ts;
@ -78,7 +78,7 @@ namespace DotRecast.Recast.Toolset.Tools
_tc.BuildNavMeshTile(refs); _tc.BuildNavMeshTile(refs);
} }
return new NavMeshBuildResult(RcImmutableArray<RcBuilderResult>.Empty, _tc.GetNavMesh()); return new NavMeshBuildResult(cfg, RcImmutableArray<RcBuilderResult>.Empty, _tc.GetNavMesh());
} }
public void ClearAllTempObstacles() public void ClearAllTempObstacles()
@ -141,7 +141,8 @@ namespace DotRecast.Recast.Toolset.Tools
navMeshParams.maxTiles = 256; // .. navMeshParams.maxTiles = 256; // ..
navMeshParams.maxPolys = 16384; 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 comp = _comp.Create(cCompatibility ? 0 : 1);
var storageParams = new DtTileCacheStorageParams(order, cCompatibility); var storageParams = new DtTileCacheStorageParams(order, cCompatibility);
DtTileCache tc = new DtTileCache(option, storageParams, navMesh, comp, _proc); DtTileCache tc = new DtTileCache(option, storageParams, navMesh, comp, _proc);

View File

@ -1,4 +1,4 @@
using System; using System;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
using DotRecast.Recast.Toolset.Builder; using DotRecast.Recast.Toolset.Builder;
@ -34,7 +34,7 @@ namespace DotRecast.Recast.Toolset.Tools
RcOffMeshConnection nearestConnection = null; RcOffMeshConnection nearestConnection = null;
foreach (RcOffMeshConnection offMeshCon in geom.GetOffMeshConnections()) 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) if (d < nearestDist && Math.Sqrt(d) < settings.agentRadius)
{ {
nearestDist = d; nearestDist = d;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -11,7 +11,6 @@ namespace DotRecast.Recast.Toolset.Tools
public const int MAX_POLYS = 256; public const int MAX_POLYS = 256;
public const int MAX_SMOOTH = 2048; public const int MAX_SMOOTH = 2048;
public RcTestNavMeshTool() 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, public DtStatus FindFollowPath(DtNavMesh navMesh, DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast,
ref List<long> pathIterPolys, ref List<RcVec3f> smoothPath) ref List<long> pathIterPolys, int pathIterPolyCount, ref List<RcVec3f> smoothPath)
{ {
if (startRef == 0 || endRef == 0) if (startRef == 0 || endRef == 0)
{ {
@ -36,6 +35,8 @@ namespace DotRecast.Recast.Toolset.Tools
smoothPath ??= new List<RcVec3f>(); smoothPath ??= new List<RcVec3f>();
pathIterPolys.Clear(); pathIterPolys.Clear();
pathIterPolyCount = 0;
smoothPath.Clear(); smoothPath.Clear();
var opt = new DtFindPathOption(enableRaycast ? DtFindPathOptions.DT_FINDPATH_ANY_ANGLE : 0, float.MaxValue); 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) if (0 >= pathIterPolys.Count)
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
pathIterPolyCount = pathIterPolys.Count;
// Iterate over the path to find smooth path on the detail mesh surface. // Iterate over the path to find smooth path on the detail mesh surface.
navQuery.ClosestPointOnPoly(startRef, startPt, out var iterPos, out var _); navQuery.ClosestPointOnPoly(startRef, startPt, out var iterPos, out var _);
navQuery.ClosestPointOnPoly(pathIterPolys[pathIterPolys.Count - 1], endPt, out var targetPos, out var _); navQuery.ClosestPointOnPoly(pathIterPolys[pathIterPolys.Count - 1], endPt, out var targetPos, out var _);
float STEP_SIZE = 0.5f; const float STEP_SIZE = 0.5f;
float SLOP = 0.01f; const float SLOP = 0.01f;
smoothPath.Clear(); smoothPath.Clear();
smoothPath.Add(iterPos); smoothPath.Add(iterPos);
var visited = new List<long>();
Span<long> visited = stackalloc long[16];
int nvisited = 0;
// Move towards target a small advancement at a time until target reached or // Move towards target a small advancement at a time until target reached or
// when ran out of memory to store the path. // 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. // Find location to steer towards.
if (!DtPathUtils.GetSteerTarget(navQuery, iterPos, targetPos, SLOP, 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; break;
} }
@ -85,15 +91,15 @@ namespace DotRecast.Recast.Toolset.Tools
len = STEP_SIZE / len; len = STEP_SIZE / len;
} }
RcVec3f moveTgt = RcVecUtils.Mad(iterPos, delta, len); RcVec3f moveTgt = RcVec.Mad(iterPos, delta, len);
// Move // 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; iterPos = result;
pathIterPolys = DtPathUtils.MergeCorridorStartMoved(pathIterPolys, pathIterPolys.Count, MAX_POLYS, visited); pathIterPolyCount = DtPathUtils.MergeCorridorStartMoved(ref pathIterPolys, pathIterPolyCount, MAX_POLYS, visited, nvisited);
pathIterPolys = DtPathUtils.FixupShortcuts(pathIterPolys, navQuery); pathIterPolyCount = DtPathUtils.FixupShortcuts(ref pathIterPolys, pathIterPolyCount, navQuery);
var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h); var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h);
if (status.Succeeded()) if (status.Succeeded())
@ -123,7 +129,7 @@ namespace DotRecast.Recast.Toolset.Tools
long prevRef = 0; long prevRef = 0;
long polyRef = pathIterPolys[0]; long polyRef = pathIterPolys[0];
int npos = 0; int npos = 0;
while (npos < pathIterPolys.Count && polyRef != steerPosRef) while (npos < pathIterPolyCount && polyRef != steerPosRef)
{ {
prevRef = polyRef; prevRef = polyRef;
polyRef = pathIterPolys[npos]; polyRef = pathIterPolys[npos];
@ -131,6 +137,7 @@ namespace DotRecast.Recast.Toolset.Tools
} }
pathIterPolys = pathIterPolys.GetRange(npos, pathIterPolys.Count - npos); pathIterPolys = pathIterPolys.GetRange(npos, pathIterPolys.Count - npos);
pathIterPolyCount -= npos;
// Handle the connection. // Handle the connection.
var status2 = navMesh.GetOffMeshConnectionPolyEndPoints(prevRef, polyRef, ref startPos, ref endPos); 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, public DtStatus FindStraightPath(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPt, RcVec3f endPt, IDtQueryFilter filter, bool enableRaycast,
ref List<long> polys, ref List<DtStraightPath> straightPath, int straightPathOptions) ref List<long> polys, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath, int straightPathOptions)
{ {
straightPathCount = 0;
if (startRef == 0 || endRef == 0) if (startRef == 0 || endRef == 0)
{ {
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
} }
polys ??= new List<long>(); polys ??= new List<long>();
straightPath ??= new List<DtStraightPath>();
polys.Clear(); polys.Clear();
straightPath.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; 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, public DtStatus UpdateSlicedFindPath(DtNavMeshQuery navQuery, int maxIter, long endRef, RcVec3f startPos, RcVec3f endPos,
ref List<long> path, ref List<DtStraightPath> straightPath) ref List<long> path, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath)
{ {
straightPathCount = 0;
var status = navQuery.UpdateSlicedFindPath(maxIter, out _); var status = navQuery.UpdateSlicedFindPath(maxIter, out _);
if (!status.Succeeded()) if (!status.Succeeded())
@ -224,7 +232,6 @@ namespace DotRecast.Recast.Toolset.Tools
navQuery.FinalizeSlicedFindPath(ref path); navQuery.FinalizeSlicedFindPath(ref path);
straightPath?.Clear();
if (path != null) if (path != null)
{ {
// In case of partial path, make sure the end point is clamped to the last polygon. // 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<DtStraightPath>(MAX_POLYS); navQuery.FindStraightPath(startPos, epos, path, path.Count, straightPath, out straightPathCount, maxStraightPath, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS);
navQuery.FindStraightPath(startPos, epos, path, ref straightPath, MAX_POLYS, DtStraightPathOptions.DT_STRAIGHTPATH_ALL_CROSSINGS);
} }
return DtStatus.DT_SUCCESS; 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, public DtStatus Raycast(DtNavMeshQuery navQuery, long startRef, long endRef, RcVec3f startPos, RcVec3f endPos, IDtQueryFilter filter,
ref List<long> polys, ref List<DtStraightPath> straightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult) ref List<long> polys, Span<DtStraightPath> straightPath, out int straightPathCount, int maxStraightPath, ref RcVec3f hitPos, ref RcVec3f hitNormal, ref bool hitResult)
{ {
straightPathCount = 0;
if (startRef == 0 || endRef == 0) if (startRef == 0 || endRef == 0)
{ {
polys?.Clear(); polys?.Clear();
straightPath?.Clear();
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
} }
@ -267,7 +272,7 @@ namespace DotRecast.Recast.Toolset.Tools
// results ... // results ...
polys = path; polys = path;
if (t > 1) if (t >= 1)
{ {
// No hit // No hit
hitPos = endPos; hitPos = endPos;
@ -291,10 +296,8 @@ namespace DotRecast.Recast.Toolset.Tools
} }
} }
straightPath ??= new List<DtStraightPath>(); straightPath[straightPathCount++] = new DtStraightPath(startPos, 0, 0);
straightPath.Clear(); straightPath[straightPathCount++] = new DtStraightPath(hitPos, 0, 0);
straightPath.Add(new DtStraightPath(startPos, 0, 0));
straightPath.Add(new DtStraightPath(hitPos, 0, 0));
return status; return status;
} }
@ -377,7 +380,7 @@ namespace DotRecast.Recast.Toolset.Tools
float nx = (epos.Z - spos.Z) * 0.25f; float nx = (epos.Z - spos.Z) * 0.25f;
float nz = -(epos.X - spos.X) * 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].X = spos.X + nx * 1.2f;
tempQueryPoly[0].Y = spos.Y + agentHeight / 2; tempQueryPoly[0].Y = spos.Y + agentHeight / 2;
tempQueryPoly[0].Z = spos.Z + nz * 1.2f; tempQueryPoly[0].Z = spos.Z + nz * 1.2f;

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
namespace DotRecast.Recast.Toolset.Tools namespace DotRecast.Recast.Toolset.Tools
{ {

View File

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -23,7 +23,7 @@ namespace DotRecast.Recast.Toolset.Tools
var bmin = geom.GetMeshBoundsMin(); var bmin = geom.GetMeshBoundsMin();
var bmax = geom.GetMeshBoundsMax(); var bmax = geom.GetMeshBoundsMax();
int gw = 0, gh = 0; 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 ts = settings.tileSize;
int tw = (gw + ts - 1) / ts; int tw = (gw + ts - 1) / ts;
@ -47,7 +47,7 @@ namespace DotRecast.Recast.Toolset.Tools
var bmin = geom.GetMeshBoundsMin(); var bmin = geom.GetMeshBoundsMin();
var bmax = geom.GetMeshBoundsMax(); var bmax = geom.GetMeshBoundsMax();
int gw = 0, gh = 0; 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 ts = settings.tileSize;
int tw = (gw + ts - 1) / ts; int tw = (gw + ts - 1) / ts;
@ -68,8 +68,8 @@ namespace DotRecast.Recast.Toolset.Tools
tileTriCount = 0; // ... tileTriCount = 0; // ...
tileMemUsage = 0; // ... tileMemUsage = 0; // ...
var availableTileCount = navMesh.GetAvailableTileCount(); var availableTile = navMesh.IsAvailableTileCount();
if (0 >= availableTileCount) if (!availableTile)
{ {
return false; return false;
} }

View File

@ -0,0 +1,8 @@
namespace DotRecast.Recast
{
public static class EdgeValues
{
public const int EV_UNDEF = -1;
public const int EV_HULL = -2;
}
}

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {
public class RcChunkyTriMesh public class RcChunkyTriMesh
{ {
private List<RcChunkyTriMeshNode> nodes; public List<RcChunkyTriMeshNode> nodes;
private int ntris; public int ntris;
private int maxTrisPerChunk; public 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<RcChunkyTriMeshNode> 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<RcChunkyTriMeshNode>(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<RcChunkyTriMeshNode> GetChunksOverlappingRect(float[] bmin, float[] bmax)
{
// Traverse tree
List<RcChunkyTriMeshNode> ids = new List<RcChunkyTriMeshNode>();
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<RcChunkyTriMeshNode> GetChunksOverlappingSegment(RcVec2f p, RcVec2f q)
{
// Traverse tree
List<RcChunkyTriMeshNode> ids = new List<RcChunkyTriMeshNode>();
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;
}
} }
} }

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {

View File

@ -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<RcChunkyTriMeshNode>(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<RcChunkyTriMeshNode> GetChunksOverlappingRect(RcChunkyTriMesh cm, RcVec2f bmin, RcVec2f bmax)
{
// Traverse tree
List<RcChunkyTriMeshNode> ids = new List<RcChunkyTriMeshNode>();
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<RcChunkyTriMeshNode> GetChunksOverlappingSegment(RcChunkyTriMesh cm, RcVec2f p, RcVec2f q)
{
// Traverse tree
List<RcChunkyTriMeshNode> ids = new List<RcChunkyTriMeshNode>();
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<RcChunkyTriMeshNode> 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;
}
}
}

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System.Collections.Generic;
using DotRecast.Core.Numerics;
namespace DotRecast.Recast.Geom namespace DotRecast.Recast.Geom
{ {
@ -32,7 +33,8 @@ namespace DotRecast.Recast.Geom
{ {
this.vertices = vertices; this.vertices = vertices;
this.faces = faces; 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() public int[] GetTris()
@ -45,9 +47,9 @@ namespace DotRecast.Recast.Geom
return vertices; return vertices;
} }
public List<RcChunkyTriMeshNode> GetChunksOverlappingRect(float[] bmin, float[] bmax) public List<RcChunkyTriMeshNode> GetChunksOverlappingRect(RcVec2f bmin, RcVec2f bmax)
{ {
return chunkyTriMesh.GetChunksOverlappingRect(bmin, bmax); return RcChunkyTriMeshs.GetChunksOverlappingRect(chunkyTriMesh, bmin, bmax);
} }
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public static SimpleInputGeomProvider LoadFile(string objFilePath)
{ {
byte[] chunk = RcResources.Load(objFilePath); byte[] chunk = RcIO.ReadFileIfFound(objFilePath);
var context = RcObjImporter.LoadContext(chunk); var context = RcObjImporter.LoadContext(chunk);
return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces); return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces);
} }
@ -77,12 +77,12 @@ namespace DotRecast.Recast.Geom
this.faces = faces; this.faces = faces;
normals = new float[faces.Length]; normals = new float[faces.Length];
CalculateNormals(); CalculateNormals();
bmin = RcVecUtils.Create(vertices); bmin = new RcVec3f(vertices);
bmax = RcVecUtils.Create(vertices); bmax = new RcVec3f(vertices);
for (int i = 1; i < vertices.Length / 3; i++) for (int i = 1; i < vertices.Length / 3; i++)
{ {
bmin = RcVecUtils.Min(bmin, vertices, i * 3); bmin = RcVec3f.Min(bmin, RcVec.Create(vertices, i * 3));
bmax = RcVecUtils.Max(bmax, vertices, i * 3); bmax = RcVec3f.Max(bmax, RcVec.Create(vertices, i * 3));
} }
_mesh = new RcTriMesh(vertices, faces); _mesh = new RcTriMesh(vertices, faces);

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public interface IRcBuilderProgressListener public interface IRcBuilderProgressListener
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,8 +25,7 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants; using static RcRecast;
using static RcCommons;
public static class RcAreas public static class RcAreas
{ {
@ -457,12 +456,12 @@ namespace DotRecast.Recast
int zStride = xSize; // For readability int zStride = xSize; // For readability
// Compute the bounding box of the polygon // Compute the bounding box of the polygon
RcVec3f bmin = RcVecUtils.Create(verts); RcVec3f bmin = new RcVec3f(verts);
RcVec3f bmax = RcVecUtils.Create(verts); RcVec3f bmax = new RcVec3f(verts);
for (int i = 3; i < verts.Length; i += 3) for (int i = 3; i < verts.Length; i += 3)
{ {
bmin = RcVecUtils.Min(bmin, verts, i); bmin = RcVec3f.Min(bmin, RcVec.Create(verts, i));
bmax = RcVecUtils.Max(bmax, verts, i); bmax = RcVec3f.Max(bmax, RcVec.Create(verts, i));
} }
bmin.Y = minY; bmin.Y = minY;
@ -753,19 +752,19 @@ namespace DotRecast.Recast
int vertIndexB = vertIndex; int vertIndexB = vertIndex;
int vertIndexC = (vertIndex + 1) % numVerts; int vertIndexC = (vertIndex + 1) % numVerts;
RcVec3f vertA = RcVecUtils.Create(verts, vertIndexA * 3); RcVec3f vertA = RcVec.Create(verts, vertIndexA * 3);
RcVec3f vertB = RcVecUtils.Create(verts, vertIndexB * 3); RcVec3f vertB = RcVec.Create(verts, vertIndexB * 3);
RcVec3f vertC = RcVecUtils.Create(verts, vertIndexC * 3); RcVec3f vertC = RcVec.Create(verts, vertIndexC * 3);
// From A to B on the x/z plane // From A to B on the x/z plane
RcVec3f prevSegmentDir = RcVec3f.Subtract(vertB, vertA); RcVec3f prevSegmentDir = RcVec3f.Subtract(vertB, vertA);
prevSegmentDir.Y = 0; // Squash onto x/z plane 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 // From B to C on the x/z plane
RcVec3f currSegmentDir = RcVec3f.Subtract(vertC, vertB); RcVec3f currSegmentDir = RcVec3f.Subtract(vertC, vertB);
currSegmentDir.Y = 0; // Squash onto x/z plane 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 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 // 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; 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. // 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; float scale = 1.0f / cornerMiterSqMag;
cornerMiterX *= scale; cornerMiterX *= scale;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public static class RcAxis public static class RcAxis
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
/// Contour build flags. /// Contour build flags.
/// @see rcBuildContours /// @see rcBuildContours

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -30,7 +30,7 @@ using DotRecast.Recast.Geom;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcCommons; using static RcRecast;
using static RcAreas; using static RcAreas;
public class RcBuilder public class RcBuilder

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -92,7 +92,7 @@ namespace DotRecast.Recast
} }
else 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);
} }
} }
} }

View File

@ -1,4 +1,4 @@
using DotRecast.Core; using DotRecast.Core;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcCompactSpanBuilder public class RcCompactSpanBuilder
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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;
using System.Linq; using System.Linq;
using DotRecast.Core; using DotRecast.Core;
using static DotRecast.Recast.RcConstants;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcCommons; using static RcRecast;
public static class RcCompacts 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 /// @name Compact Heightfield Functions

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

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

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Recast
{ {
/** Represents a simple, non-overlapping contour in field space. */ /// Represents a simple, non-overlapping contour in field space.
public class RcContour public class RcContour
{ {
/** Simplified contour vertex and connection data. [Size: 4 * #nverts] */ public int[] verts; //< Simplified contour vertex and connection data. [Size: 4 * #nverts]
public int[] verts; public int nverts; //< The number of vertices in the simplified contour.
public int[] rverts; //< Raw contour vertex and connection data. [Size: 4 * #nrverts]
/** The number of vertices in the simplified contour. */ public int nrverts; //< The number of vertices in the raw contour.
public int nverts; public int reg; //< The region id of the contour.
public int area; //< The area id of the contour.
/** 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;
} }
} }

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcContourHole public class RcContourHole
{ {

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcContourRegion public class RcContourRegion
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,8 +24,8 @@ using DotRecast.Core;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants;
using static RcCommons; using static RcRecast;
public static class RcContours public static class RcContours
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
// Struct to keep track of entries in the region table that have been changed. // Struct to keep track of entries in the region table that have been changed.
public readonly struct RcDirtyEntry public readonly struct RcDirtyEntry

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using static DotRecast.Recast.RcConstants;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcRecast;
public static class RcFilledVolumeRasterization public static class RcFilledVolumeRasterization
{ {
private const float EPSILON = 0.00001f; 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) 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]; 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) if (axis.Y * axis.Y > EPSILON)
{ {
RcVec3f[] rectangleOnStartPlane = new RcVec3f[4]; Span<RcVec3f> rectangleOnStartPlane = stackalloc RcVec3f[4];
RcVec3f[] rectangleOnEndPlane = new RcVec3f[4]; Span<RcVec3f> rectangleOnEndPlane = stackalloc RcVec3f[4];
float ds = RcVec3f.Dot(axis, start); float ds = RcVec3f.Dot(axis, start);
float de = RcVec3f.Dot(axis, end); float de = RcVec3f.Dot(axis, end);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
@ -326,7 +326,7 @@ namespace DotRecast.Recast
return s; 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<RcVec3f> rectangleOnPlane)
{ {
int j = (i + 1) % 4; int j = (i + 1) % 4;
// Ray against sphere intersection // Ray against sphere intersection
@ -507,8 +507,8 @@ namespace DotRecast.Recast
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
int vi = i * 3; int vi = i * 3;
if (vertices[vi] >= rectangle[0] && vertices[vi] < rectangle[2] && vertices[vi + 2] >= rectangle[1] if (vertices[vi] >= rectangle[0] && vertices[vi] < rectangle[2] &&
&& vertices[vi + 2] < rectangle[3]) vertices[vi + 2] >= rectangle[1] && vertices[vi + 2] < rectangle[3])
{ {
yMin = Math.Min(yMin, vertices[vi + 1]); yMin = Math.Min(yMin, vertices[vi + 1]);
yMax = Math.Max(yMax, vertices[vi + 1]); yMax = Math.Max(yMax, vertices[vi + 1]);
@ -525,7 +525,7 @@ namespace DotRecast.Recast
{ {
if (MathF.Abs(planes[j][1]) > EPSILON) 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 t = (planes[j][3] - dotNormalPoint) / planes[j][1];
float y = point.Y + t; float y = point.Y + t;
bool valid = true; bool valid = true;
@ -729,15 +729,15 @@ namespace DotRecast.Recast
private static bool RayTriangleIntersection(RcVec3f point, int plane, float[][] planes, out float y) private static bool RayTriangleIntersection(RcVec3f point, int plane, float[][] planes, out float y)
{ {
y = 0.0f; y = 0.0f;
float t = (planes[plane][3] - RcVecUtils.Dot(planes[plane], point)) / planes[plane][1]; float t = (planes[plane][3] - RcVec3f.Dot(new RcVec3f(planes[plane]), point)) / planes[plane][1];
float[] s = { point.X, point.Y + t, point.Z }; RcVec3f s = new RcVec3f(point.X, point.Y + t, point.Z);
float u = RcVecUtils.Dot(s, planes[plane + 1]) - planes[plane + 1][3]; float u = RcVec3f.Dot(s, new RcVec3f(planes[plane + 1])) - planes[plane + 1][3];
if (u < 0.0f || u > 1.0f) if (u < 0.0f || u > 1.0f)
{ {
return false; 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) if (v < 0.0f)
{ {
return false; return false;
@ -749,7 +749,7 @@ namespace DotRecast.Recast
return false; return false;
} }
y = s[1]; y = s.Y;
return true; return true;
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -23,8 +23,8 @@ using DotRecast.Core;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants;
using static RcCommons; using static RcRecast;
public static class RcFilters public static class RcFilters
{ {

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcHeightPatch public class RcHeightPatch
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,23 +1,23 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcLayerRegion public class RcLayerRegion
{ {
public int id; public readonly int index;
public int layerId; // Layer ID public List<int> layers;
public bool @base; // Flag indicating if the region is the base of merged regions. public List<int> neis;
public int ymin, ymax; public int ymin, ymax;
public List<int> layers; // Layer count public byte layerId; // Layer ID
public List<int> neis; // Neighbour count public bool @base; // Flag indicating if the region is the base of merged regions.
public RcLayerRegion(int i) public RcLayerRegion(int i)
{ {
id = i; index = i;
ymin = 0xFFFF;
layerId = 0xff;
layers = new List<int>(); layers = new List<int>();
neis = new List<int>(); neis = new List<int>();
ymin = 0xFFFF;
layerId = 0xff;
} }
}; };
} }

View File

@ -1,9 +1,9 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcLayerSweepSpan public class RcLayerSweepSpan
{ {
public int ns; // number samples public int ns; // number samples
public int id; // region id public byte id; // region id
public int nei; // neighbour id public byte nei; // neighbour id
}; };
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,15 +25,10 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants; using static RcRecast;
using static RcCommons;
public static class RcLayers public static class RcLayers
{ {
const int RC_MAX_LAYERS = RcConstants.RC_NOT_CONNECTED;
const int RC_MAX_NEIS = 16;
private static void AddUnique(List<int> a, int v) private static void AddUnique(List<int> a, int v)
{ {
if (!a.Contains(v)) if (!a.Contains(v))
@ -61,7 +56,6 @@ namespace DotRecast.Recast
/// @name Layer, Contour, Polymesh, and Detail Mesh Functions /// @name Layer, Contour, Polymesh, and Detail Mesh Functions
/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail
/// @{ /// @{
/// Builds a layer set from the specified compact heightfield. /// Builds a layer set from the specified compact heightfield.
/// @ingroup recast /// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] ctx The build context to use during the operation.
@ -80,10 +74,10 @@ namespace DotRecast.Recast
int w = chf.width; int w = chf.width;
int h = chf.height; int h = chf.height;
int[] srcReg = new int[chf.spanCount]; Span<byte> srcReg = stackalloc byte[chf.spanCount];
Array.Fill(srcReg, 0xFF); srcReg.Fill(0xFF);
int nsweeps = chf.width; // Math.Max(chf.width, chf.height); int nsweeps = chf.width;
RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps]; RcLayerSweepSpan[] sweeps = new RcLayerSweepSpan[nsweeps];
for (int i = 0; i < sweeps.Length; i++) for (int i = 0; i < sweeps.Length; i++)
{ {
@ -91,15 +85,15 @@ namespace DotRecast.Recast
} }
// Partition walkable area into monotone regions. // Partition walkable area into monotone regions.
int[] prevCount = new int[256]; Span<int> prevCount = stackalloc int[256];
int regId = 0; byte regId = 0;
// Sweep one line at a time. // Sweep one line at a time.
for (int y = borderSize; y < h - borderSize; ++y) for (int y = borderSize; y < h - borderSize; ++y)
{ {
// Collect spans from this row. // Collect spans from this row.
Array.Fill(prevCount, 0); prevCount.Fill(0);
int sweepId = 0; byte sweepId = 0;
for (int x = borderSize; x < w - borderSize; ++x) for (int x = borderSize; x < w - borderSize; ++x)
{ {
@ -111,7 +105,7 @@ namespace DotRecast.Recast
if (chf.areas[i] == RC_NULL_AREA) if (chf.areas[i] == RC_NULL_AREA)
continue; continue;
int sid = 0xFF; byte sid = 0xFF;
// -x // -x
if (GetCon(ref s, 0) != RC_NOT_CONNECTED) if (GetCon(ref s, 0) != RC_NOT_CONNECTED)
@ -136,7 +130,7 @@ namespace DotRecast.Recast
int ax = x + GetDirOffsetX(3); int ax = x + GetDirOffsetX(3);
int ay = y + GetDirOffsetY(3); int ay = y + GetDirOffsetY(3);
int ai = chf.cells[ax + ay * w].index + GetCon(ref s, 3); int ai = chf.cells[ax + ay * w].index + GetCon(ref s, 3);
int nr = srcReg[ai]; byte nr = srcReg[ai];
if (nr != 0xff) if (nr != 0xff)
{ {
// Set neighbour when first valid neighbour is encoutered. // Set neighbour when first valid neighbour is encoutered.
@ -266,9 +260,11 @@ namespace DotRecast.Recast
} }
// Create 2D layers from regions. // Create 2D layers from regions.
int layerId = 0; byte layerId = 0;
List<int> stack = new List<int>(); const int MAX_STACK = 64;
Span<byte> stack = stackalloc byte[MAX_STACK];
int nstack = 0;
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
{ {
@ -281,14 +277,16 @@ namespace DotRecast.Recast
root.layerId = layerId; root.layerId = layerId;
root.@base = true; root.@base = true;
stack.Add(i); nstack = 0;
stack[nstack++] = ((byte)i);
while (stack.Count != 0) while (0 != nstack)
{ {
// Pop front // Pop front
int pop = stack[0]; // TODO : 여기에 stack 처럼 작동하게 했는데, 스택인지는 모르겠음 RcLayerRegion reg = regs[stack[0]];
stack.RemoveAt(0); nstack--;
RcLayerRegion reg = regs[pop]; for (int j = 0; j < nstack; ++j)
stack[j] = stack[j + 1];
foreach (int nei in reg.neis) foreach (int nei in reg.neis)
{ {
@ -307,8 +305,10 @@ namespace DotRecast.Recast
if ((ymax - ymin) >= 255) if ((ymax - ymin) >= 255)
continue; continue;
if (nstack < MAX_STACK)
{
// Deepen // Deepen
stack.Add(nei); stack[nstack++] = (byte)nei;
// Mark layer id // Mark layer id
regn.layerId = layerId; regn.layerId = layerId;
@ -322,6 +322,7 @@ namespace DotRecast.Recast
root.ymax = Math.Max(root.ymax, regn.ymax); root.ymax = Math.Max(root.ymax, regn.ymax);
} }
} }
}
layerId++; layerId++;
} }
@ -335,7 +336,7 @@ namespace DotRecast.Recast
if (!ri.@base) if (!ri.@base)
continue; continue;
int newId = ri.layerId; byte newId = ri.layerId;
for (;;) for (;;)
{ {
@ -411,7 +412,7 @@ namespace DotRecast.Recast
} }
// Compact layerIds // Compact layerIds
int[] remap = new int[256]; Span<byte> remap = stackalloc byte[256];
// Find number of unique layers. // Find number of unique layers.
layerId = 0; layerId = 0;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public readonly struct RcLevelStackEntry public readonly struct RcLevelStackEntry
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -22,178 +22,57 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using static DotRecast.Recast.RcConstants;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcCommons; using static RcRecast;
using static RcVec;
using static EdgeValues;
public static class RcMeshDetails public static class RcMeshDetails
{ {
public const int MAX_VERTS = 127; public const int RC_UNSET_HEIGHT = RC_SPAN_MAX_HEIGHT;
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;
private static float Vdot2(float[] a, float[] b) public static bool CircumCircle(RcVec3f p1, RcVec3f p2, RcVec3f p3, ref RcVec3f c, out float r)
{
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)
{ {
const float EPS = 1e-6f; const float EPS = 1e-6f;
// Calculate the circle relative to p1, to avoid some precision issues. // Calculate the circle relative to p1, to avoid some precision issues.
var v1 = new RcVec3f(); var v1 = new RcVec3f();
var v2 = RcVecUtils.Subtract(verts, p2, p1); var v2 = p2 - p1;
var v3 = RcVecUtils.Subtract(verts, p3, p1); var v3 = p3 - p1;
float cp = Vcross2(v1, v2, v3); float cp = Cross2(v1, v2, v3);
if (MathF.Abs(cp) > EPS) if (MathF.Abs(cp) > EPS)
{ {
float v1Sq = Vdot2(v1, v1); float v1Sq = Dot2(v1, v1);
float v2Sq = Vdot2(v2, v2); float v2Sq = Dot2(v2, v2);
float v3Sq = Vdot2(v3, v3); 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.X = (v1Sq * (v2.Z - v3.Z) + v2Sq * (v3.Z - v1.Z) + v3Sq * (v1.Z - v2.Z)) / (2 * cp);
c.Y = 0; c.Y = 0;
c.Z = (v1Sq * (v3.X - v2.X) + v2Sq * (v1.X - v3.X) + v3Sq * (v2.X - v1.X)) / (2 * cp); c.Z = (v1Sq * (v3.X - v2.X) + v2Sq * (v1.X - v3.X) + v3Sq * (v2.X - v1.X)) / (2 * cp);
r.Exchange(Vdist2(c, v1)); r = Dist2(c, v1);
c = RcVecUtils.Add(c, verts, p1); c = c + p1;
return true; return true;
} }
c = RcVecUtils.Create(verts, p1); c = p1;
r.Exchange(0f); r = 0f;
return false; 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 v0 = c - a;
var v1 = RcVecUtils.Subtract(verts, b, a); var v1 = b - a;
var v2 = RcVecUtils.Subtract(p, verts, a); var v2 = p - a;
float dot00 = Vdot2(v0, v0); float dot00 = Dot2(v0, v0);
float dot01 = Vdot2(v0, v1); float dot01 = Dot2(v0, v1);
float dot02 = Vdot2(v0, v2); float dot02 = Dot2(v0, v2);
float dot11 = Vdot2(v1, v1); float dot11 = Dot2(v1, v1);
float dot12 = Vdot2(v1, v2); float dot12 = Dot2(v1, v2);
// Compute barycentric coordinates // Compute barycentric coordinates
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
@ -204,14 +83,14 @@ namespace DotRecast.Recast
const float EPS = 1e-4f; const float EPS = 1e-4f;
if (u >= -EPS && v >= -EPS && (u + v) <= 1 + EPS) 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 MathF.Abs(y - p.Y);
} }
return float.MaxValue; 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 pqx = verts[q + 0] - verts[p + 0];
float pqy = verts[q + 1] - verts[p + 1]; float pqy = verts[q + 1] - verts[p + 1];
@ -242,7 +121,7 @@ namespace DotRecast.Recast
return dx * dx + dy * dy + dz * dz; 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 pqx = poly[q + 0] - poly[p + 0];
float pqz = poly[q + 2] - poly[p + 2]; float pqz = poly[q + 2] - poly[p + 2];
@ -270,7 +149,7 @@ namespace DotRecast.Recast
return dx * dx + dz * dz; 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 pqx = poly[q + 0] - poly[p + 0];
float pqz = poly[q + 2] - poly[p + 2]; float pqz = poly[q + 2] - poly[p + 2];
@ -298,15 +177,15 @@ namespace DotRecast.Recast
return dx * dx + dz * dz; return dx * dx + dz * dz;
} }
private static float DistToTriMesh(RcVec3f p, float[] verts, int nverts, List<int> tris, int ntris) public static float DistToTriMesh(RcVec3f p, float[] verts, int nverts, List<int> tris, int ntris)
{ {
float dmin = float.MaxValue; float dmin = float.MaxValue;
for (int i = 0; i < ntris; ++i) for (int i = 0; i < ntris; ++i)
{ {
int va = tris[i * 4 + 0] * 3; RcVec3f va = RcVec.Create(verts, tris[i * 4 + 0] * 3);
int vb = tris[i * 4 + 1] * 3; RcVec3f vb = RcVec.Create(verts, tris[i * 4 + 1] * 3);
int vc = tris[i * 4 + 2] * 3; RcVec3f vc = RcVec.Create(verts, tris[i * 4 + 2] * 3);
float d = DistPtTri(p, verts, va, vb, vc); float d = DistPtTri(p, va, vb, vc);
if (d < dmin) if (d < dmin)
{ {
dmin = d; dmin = d;
@ -321,7 +200,7 @@ namespace DotRecast.Recast
return dmin; 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; float dmin = float.MaxValue;
int i, j; int i, j;
@ -342,7 +221,7 @@ namespace DotRecast.Recast
return c ? -dmin : dmin; 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) RcHeightPatch hp)
{ {
int ix = (int)MathF.Floor(fx * ics + 0.01f); int ix = (int)MathF.Floor(fx * ics + 0.01f);
@ -425,7 +304,7 @@ namespace DotRecast.Recast
return h; return h;
} }
private static int FindEdge(List<int> edges, int s, int t) public static int FindEdge(List<int> edges, int s, int t)
{ {
for (int i = 0; i < edges.Count / 4; i++) for (int i = 0; i < edges.Count / 4; i++)
{ {
@ -439,7 +318,7 @@ namespace DotRecast.Recast
return EV_UNDEF; return EV_UNDEF;
} }
private static void AddEdge(RcContext ctx, List<int> edges, int maxEdges, int s, int t, int l, int r) public static void AddEdge(RcContext ctx, List<int> edges, int maxEdges, int s, int t, int l, int r)
{ {
if (edges.Count / 4 >= maxEdges) if (edges.Count / 4 >= maxEdges)
{ {
@ -457,7 +336,7 @@ namespace DotRecast.Recast
} }
} }
private static void UpdateLeftFace(List<int> edges, int e, int s, int t, int f) public static void UpdateLeftFace(List<int> edges, int e, int s, int t, int f)
{ {
if (edges[e + 0] == s && edges[e + 1] == t && edges[e + 2] == EV_UNDEF) 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 a1 = Cross2(verts, a, b, d);
float a2 = Vcross2(verts, a, b, c); float a2 = Cross2(verts, a, b, c);
if (a1 * a2 < 0.0f) if (a1 * a2 < 0.0f)
{ {
float a3 = Vcross2(verts, c, d, a); float a3 = Cross2(verts, c, d, a);
float a4 = a3 + a2 - a1; float a4 = a3 + a2 - a1;
if (a3 * a4 < 0.0f) if (a3 * a4 < 0.0f)
{ {
@ -486,7 +365,7 @@ namespace DotRecast.Recast
return false; return false;
} }
private static bool OverlapEdges(float[] pts, List<int> edges, int s1, int t1) public static bool OverlapEdges(float[] pts, List<int> edges, int s1, int t1)
{ {
for (int i = 0; i < edges.Count / 4; ++i) for (int i = 0; i < edges.Count / 4; ++i)
{ {
@ -507,7 +386,7 @@ namespace DotRecast.Recast
return false; return false;
} }
static int CompleteFacet(RcContext ctx, float[] pts, int npts, List<int> edges, int maxEdges, int nfaces, int e) public static int CompleteFacet(RcContext ctx, float[] pts, int npts, List<int> edges, int maxEdges, int nfaces, int e)
{ {
const float EPS = 1e-5f; const float EPS = 1e-5f;
@ -534,7 +413,7 @@ namespace DotRecast.Recast
// Find best point on left of edge. // Find best point on left of edge.
int pt = npts; int pt = npts;
RcVec3f c = new RcVec3f(); RcVec3f c = new RcVec3f();
RcAtomicFloat r = new RcAtomicFloat(-1f); float r = -1f;
for (int u = 0; u < npts; ++u) for (int u = 0; u < npts; ++u)
{ {
if (u == s || u == t) if (u == s || u == t)
@ -542,28 +421,32 @@ namespace DotRecast.Recast
continue; 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. // The circle is not updated yet, do it now.
pt = u; pt = u;
CircumCircle(pts, s * 3, t * 3, u * 3, ref c, r); CircumCircle(vs, vt, vu, ref c, out r);
continue; continue;
} }
float d = Vdist2(c, pts, u * 3); float d = Dist2(c, vu);
float tol = 0.001f; float tol = 0.001f;
if (d > r.Get() * (1 + tol)) if (d > r * (1 + tol))
{ {
// Outside current circumcircle, skip. // Outside current circumcircle, skip.
continue; continue;
} }
else if (d < r.Get() * (1 - tol)) else if (d < r * (1 - tol))
{ {
// Inside safe circumcircle, update circle. // Inside safe circumcircle, update circle.
pt = u; pt = u;
CircumCircle(pts, s * 3, t * 3, u * 3, ref c, r); CircumCircle(vs, vt, vu, ref c, out r);
} }
else else
{ {
@ -581,7 +464,7 @@ namespace DotRecast.Recast
// Edge is valid. // Edge is valid.
pt = u; 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; return nfaces;
} }
private static void DelaunayHull(RcContext ctx, int npts, float[] pts, int nhull, int[] hull, List<int> tris) public static void DelaunayHull(RcContext ctx, int npts, float[] pts, int nhull, int[] hull, List<int> tris)
{ {
int nfaces = 0; int nfaces = 0;
int maxEdges = npts * 10; int maxEdges = npts * 10;
@ -720,7 +603,7 @@ namespace DotRecast.Recast
} }
// Calculate minimum extend of the polygon. // 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; float minDist = float.MaxValue;
for (int i = 0; i < nverts; i++) for (int i = 0; i < nverts; i++)
@ -746,7 +629,7 @@ namespace DotRecast.Recast
return MathF.Sqrt(minDist); return MathF.Sqrt(minDist);
} }
private static void TriangulateHull(int nverts, float[] verts, int nhull, int[] hull, int nin, List<int> tris) public static void TriangulateHull(int nverts, float[] verts, int nhull, int[] hull, int nin, List<int> tris)
{ {
int start = 0, left = 1, right = nhull - 1; int start = 0, left = 1, right = nhull - 1;
@ -766,7 +649,7 @@ namespace DotRecast.Recast
int pv = hull[pi] * 3; int pv = hull[pi] * 3;
int cv = hull[i] * 3; int cv = hull[i] * 3;
int nv = hull[ni] * 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) if (d < dmin)
{ {
start = i; start = i;
@ -796,8 +679,8 @@ namespace DotRecast.Recast
int nvleft = hull[nleft] * 3; int nvleft = hull[nleft] * 3;
int cvright = hull[right] * 3; int cvright = hull[right] * 3;
int nvright = hull[nright] * 3; int nvright = hull[nright] * 3;
float dleft = Vdist2(verts, cvleft, nvleft) + Vdist2(verts, nvleft, cvright); float dleft = Dist2(verts, cvleft, nvleft) + Dist2(verts, nvleft, cvright);
float dright = Vdist2(verts, cvright, nvright) + Vdist2(verts, cvleft, nvright); float dright = Dist2(verts, cvright, nvright) + Dist2(verts, cvleft, nvright);
if (dleft < dright) 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; 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; return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
} }
static int BuildPolyDetail(RcContext ctx, float[] @in, int nin, float sampleDist, float sampleMaxError, public static int BuildPolyDetail(RcContext ctx, float[] @in, int nin,
int heightSearchRadius, RcCompactHeightfield chf, RcHeightPatch hp, float[] verts, List<int> tris) float sampleDist, float sampleMaxError,
int heightSearchRadius, RcCompactHeightfield chf,
RcHeightPatch hp, float[] verts,
ref List<int> tris, ref List<int> edges, ref List<int> samples)
{ {
List<int> samples = new List<int>(512); 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).
int nverts = 0; const int MAX_VERTS_PER_EDGE = 32;
float[] edge = new float[(MAX_VERTS_PER_EDGE + 1) * 3]; float[] edge = new float[(MAX_VERTS_PER_EDGE + 1) * 3];
int[] hull = new int[MAX_VERTS]; int[] hull = new int[MAX_VERTS];
int nhull = 0; int nhull = 0;
nverts = nin; int nverts = nin;
for (int i = 0; i < nin; ++i) 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(); tris.Clear();
float cs = chf.cs; float cs = chf.cs;
@ -957,7 +844,7 @@ namespace DotRecast.Recast
{ {
for (int k = nidx - 2; k > 0; --k) 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; hull[nhull++] = nverts;
nverts++; nverts++;
} }
@ -966,7 +853,7 @@ namespace DotRecast.Recast
{ {
for (int k = 1; k < nidx - 1; ++k) 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; hull[nhull++] = nverts;
nverts++; nverts++;
} }
@ -997,12 +884,12 @@ namespace DotRecast.Recast
if (sampleDist > 0) if (sampleDist > 0)
{ {
// Create sample locations in a grid. // Create sample locations in a grid.
RcVec3f bmin = RcVecUtils.Create(@in); RcVec3f bmin = new RcVec3f(@in);
RcVec3f bmax = RcVecUtils.Create(@in); RcVec3f bmax = new RcVec3f(@in);
for (int i = 1; i < nin; ++i) for (int i = 1; i < nin; ++i)
{ {
bmin = RcVecUtils.Min(bmin, @in, i * 3); bmin = RcVec3f.Min(bmin, RcVec.Create(@in, i * 3));
bmax = RcVecUtils.Max(bmax, @in, i * 3); bmax = RcVec3f.Max(bmax, RcVec.Create(@in, i * 3));
} }
int x0 = (int)MathF.Floor(bmin.X / sampleDist); int x0 = (int)MathF.Floor(bmin.X / sampleDist);
@ -1105,7 +992,7 @@ namespace DotRecast.Recast
return nverts; 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. // All internal sampled points come after the hull so we can early out for those.
if (a >= nhull || b >= nhull) if (a >= nhull || b >= nhull)
@ -1121,7 +1008,7 @@ namespace DotRecast.Recast
} }
// Find edges that lie on hull and mark them as such. // Find edges that lie on hull and mark them as such.
static void SetTriFlags(List<int> tris, int nhull, int[] hull) public static void SetTriFlags(List<int> tris, int nhull, int[] hull)
{ {
// Matches DT_DETAIL_EDGE_BOUNDARY // Matches DT_DETAIL_EDGE_BOUNDARY
const int DETAIL_EDGE_BOUNDARY = 0x1; 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<int> array) int[] verts, int bs, RcHeightPatch hp, List<int> array)
{ {
// Note: Reads to the compact heightfield are offset by border size (bs) // 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; hp.data[cx - hp.xmin + (cy - hp.ymin) * hp.width] = cs2.y;
} }
const int RETRACT_SIZE = 256;
static void Push3(List<int> queue, int v1, int v2, int v3) public static void Push3(List<int> queue, int v1, int v2, int v3)
{ {
queue.Add(v1); queue.Add(v1);
queue.Add(v2); queue.Add(v2);
queue.Add(v3); queue.Add(v3);
} }
static void GetHeightData(RcContext ctx, RcCompactHeightfield chf, int[] meshpolys, int poly, int npoly, int[] verts, public static void GetHeightData(RcContext ctx, RcCompactHeightfield chf,
int bs, RcHeightPatch hp, int region) int[] meshpolys, int poly, int npoly,
int[] verts, int bs,
ref RcHeightPatch hp, ref List<int> queue,
int region)
{ {
// Note: Reads to the compact heightfield are offset by border size (bs) // Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices. // since border size offset is already removed from the polymesh vertices.
List<int> queue = new List<int>(512); queue.Clear();
// Set all heights to RC_UNSET_HEIGHT.
Array.Fill(hp.data, RC_UNSET_HEIGHT, 0, (hp.width * hp.height) - (0)); Array.Fill(hp.data, RC_UNSET_HEIGHT, 0, (hp.width * hp.height) - (0));
bool empty = true; bool empty = true;
@ -1370,6 +1260,7 @@ namespace DotRecast.Recast
SeedArrayWithPolyCenter(ctx, chf, meshpolys, poly, npoly, verts, bs, hp, queue); SeedArrayWithPolyCenter(ctx, chf, meshpolys, poly, npoly, verts, bs, hp, queue);
} }
const int RETRACT_SIZE = 256;
int head = 0; int head = 0;
// We assume the seed is centered in the polygon, so a BFS to collect // 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 borderSize = mesh.borderSize;
int heightSearchRadius = (int)Math.Max(1, MathF.Ceiling(mesh.maxEdgeError)); int heightSearchRadius = (int)Math.Max(1, MathF.Ceiling(mesh.maxEdgeError));
List<int> edges = new List<int>(64);
List<int> tris = new List<int>(512); List<int> tris = new List<int>(512);
List<int> arr = new List<int>(512);
List<int> samples = new List<int>(512);
float[] verts = new float[256 * 3]; float[] verts = new float[256 * 3];
RcHeightPatch hp = new RcHeightPatch(); RcHeightPatch hp = new RcHeightPatch();
int nPolyVerts = 0; int nPolyVerts = 0;
@ -1526,18 +1420,20 @@ namespace DotRecast.Recast
hp.ymin = bounds[i * 4 + 2]; hp.ymin = bounds[i * 4 + 2];
hp.width = bounds[i * 4 + 1] - bounds[i * 4 + 0]; hp.width = bounds[i * 4 + 1] - bounds[i * 4 + 0];
hp.height = bounds[i * 4 + 3] - bounds[i * 4 + 2]; 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. // Build detail mesh.
int nverts = BuildPolyDetail(ctx, poly, npoly, sampleDist, sampleMaxError, heightSearchRadius, chf, hp, int nverts = BuildPolyDetail(ctx, poly, npoly,
verts, tris); sampleDist, sampleMaxError,
heightSearchRadius, chf, hp,
verts, ref tris,
ref edges, ref samples);
// Move detail verts to world space. // Move detail verts to world space.
for (int j = 0; j < nverts; ++j) for (int j = 0; j < nverts; ++j)
{ {
verts[j * 3 + 0] += orig.X; verts[j * 3 + 0] += orig.X;
verts[j * 3 + 1] += orig.Y + chf.ch; // Is this offset necessary? See 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; verts[j * 3 + 2] += orig.Z;
} }
@ -1614,7 +1510,7 @@ namespace DotRecast.Recast
} }
/// @see rcAllocPolyMeshDetail, rcPolyMeshDetail /// @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); 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) 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++; mesh.nverts++;
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,7 +24,7 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants; using static RcRecast;
public static class RcMeshs public static class RcMeshs
{ {
@ -715,8 +715,13 @@ namespace DotRecast.Recast
int nv = CountPolyVerts(mesh.polys, p, nvp); int nv = CountPolyVerts(mesh.polys, p, nvp);
bool hasRem = false; bool hasRem = false;
for (int j = 0; j < nv; ++j) for (int j = 0; j < nv; ++j)
{
if (mesh.polys[p + j] == rem) if (mesh.polys[p + j] == rem)
{
hasRem = true; hasRem = true;
}
}
if (hasRem) if (hasRem)
{ {
// Collect edges which does not touch the removed vertex. // Collect edges which does not touch the removed vertex.
@ -956,7 +961,7 @@ namespace DotRecast.Recast
mesh.npolys++; mesh.npolys++;
if (mesh.npolys > maxTris) 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 // Triangulate contour
for (int j = 0; j < cont.nverts; ++j) for (int j = 0; j < cont.nverts; ++j)
indices[j] = j; indices[j] = j;
int ntris = Triangulate(cont.nverts, cont.verts, indices, tris); int ntris = Triangulate(cont.nverts, cont.verts, indices, tris);
if (ntris <= 0) if (ntris <= 0)
{ {
// Bad triangulation, should not happen. // 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; ntris = -ntris;
} }
@ -1198,14 +1215,12 @@ namespace DotRecast.Recast
if (mesh.nverts > MAX_MESH_VERTS_POLY) if (mesh.nverts > MAX_MESH_VERTS_POLY)
{ {
throw new Exception("rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts throw new Exception($"BuildPolyMesh: The resulting mesh has too many vertices {mesh.nverts} (max {MAX_MESH_VERTS_POLY}). Data can be corrupted.");
+ " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
} }
if (mesh.npolys > MAX_MESH_VERTS_POLY) if (mesh.npolys > MAX_MESH_VERTS_POLY)
{ {
throw new Exception("rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys throw new Exception($"BuildPolyMesh: The resulting mesh has too many polygons {mesh.npolys} (max {MAX_MESH_VERTS_POLY}). Data can be corrupted.");
+ " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
} }
return mesh; return mesh;

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public enum RcPartition public enum RcPartition
{ {

View File

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
Recast4J Copyright (c) 2015 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; 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) private static bool Raycast(RcPolyMesh poly, RcPolyMeshDetail meshDetail, RcVec3f sp, RcVec3f sq, out float hitTime)
{ {
hitTime = 0; hitTime = 0;
Span<RcVec3f> tempVs = stackalloc RcVec3f[3];
if (meshDetail != null) if (meshDetail != null)
{ {
for (int i = 0; i < meshDetail.nmeshes; ++i) for (int i = 0; i < meshDetail.nmeshes; ++i)
@ -57,7 +59,7 @@ namespace DotRecast.Recast
int tris = btris * 4; int tris = btris * 4;
for (int j = 0; j < ntris; ++j) for (int j = 0; j < ntris; ++j)
{ {
RcVec3f[] vs = new RcVec3f[3]; Span<RcVec3f> vs = tempVs;
for (int k = 0; k < 3; ++k) for (int k = 0; k < 3; ++k)
{ {
vs[k].X = meshDetail.verts[verts + meshDetail.tris[tris + j * 4 + k] * 3]; vs[k].X = meshDetail.verts[verts + meshDetail.tris[tris + j * 4 + k] * 3];

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using static DotRecast.Recast.RcConstants;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcRecast;
public static class RcRasterizations public static class RcRasterizations
{ {
/// Check whether two bounding boxes overlap /// Check whether two bounding boxes overlap
@ -207,7 +209,7 @@ namespace DotRecast.Recast
/// @param[out] outVerts2Count The number of resulting polygon 2 vertices /// @param[out] outVerts2Count The number of resulting polygon 2 vertices
/// @param[in] axisOffset THe offset along the specified axis /// @param[in] axisOffset THe offset along the specified axis
/// @param[in] axis The separating axis /// @param[in] axis The separating axis
private static void DividePoly(float[] inVerts, int inVertsOffset, int inVertsCount, private static void DividePoly(Span<float> inVerts, int inVertsOffset, int inVertsCount,
int outVerts1, out int outVerts1Count, int outVerts1, out int outVerts1Count,
int outVerts2, out int outVerts2Count, int outVerts2, out int outVerts2Count,
float axisOffset, int axis) 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 + 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 + 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; 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++; poly1Vert++;
poly2Vert++; poly2Vert++;
@ -239,12 +241,12 @@ namespace DotRecast.Recast
// since these were already added above // since these were already added above
if (inVertAxisDelta[inVertA] > 0) 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++; poly1Vert++;
} }
else if (inVertAxisDelta[inVertA] < 0) 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++; 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 // add the i'th point to the right polygon. Addition is done even for points on the dividing line
if (inVertAxisDelta[inVertA] >= 0) 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++; poly1Vert++;
if (inVertAxisDelta[inVertA] != 0) 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++; poly2Vert++;
} }
} }
@ -293,13 +295,13 @@ namespace DotRecast.Recast
int flagMergeThreshold) int flagMergeThreshold)
{ {
// Calculate the bounding box of the triangle. // Calculate the bounding box of the triangle.
RcVec3f triBBMin = RcVecUtils.Create(verts, v0 * 3); RcVec3f triBBMin = RcVec.Create(verts, v0 * 3);
triBBMin = RcVecUtils.Min(triBBMin, verts, v1 * 3); triBBMin = RcVec3f.Min(triBBMin, RcVec.Create(verts, v1 * 3));
triBBMin = RcVecUtils.Min(triBBMin, verts, v2 * 3); triBBMin = RcVec3f.Min(triBBMin, RcVec.Create(verts, v2 * 3));
RcVec3f triBBMax = RcVecUtils.Create(verts, v0 * 3); RcVec3f triBBMax = RcVec.Create(verts, v0 * 3);
triBBMax = RcVecUtils.Max(triBBMax, verts, v1 * 3); triBBMax = RcVec3f.Max(triBBMax, RcVec.Create(verts, v1 * 3));
triBBMax = RcVecUtils.Max(triBBMax, verts, v2 * 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 the triangle does not touch the bounding box of the heightfield, skip the triangle.
if (!OverlapBounds(triBBMin, triBBMax, heightfieldBBMin, heightfieldBBMax)) if (!OverlapBounds(triBBMin, triBBMax, heightfieldBBMin, heightfieldBBMax))
@ -320,15 +322,15 @@ namespace DotRecast.Recast
z1 = Math.Clamp(z1, 0, h - 1); z1 = Math.Clamp(z1, 0, h - 1);
// Clip the triangle into all grid cells it touches. // Clip the triangle into all grid cells it touches.
float[] buf = new float[7 * 3 * 4]; Span<float> buf = stackalloc float[7 * 3 * 4];
int @in = 0; int @in = 0;
int inRow = 7 * 3; int inRow = 7 * 3;
int p1 = inRow + 7 * 3; int p1 = inRow + 7 * 3;
int p2 = p1 + 7 * 3; int p2 = p1 + 7 * 3;
RcVecUtils.Copy(buf, 0, verts, v0 * 3); RcVec.Copy(buf, 0, verts, v0 * 3);
RcVecUtils.Copy(buf, 3, verts, v1 * 3); RcVec.Copy(buf, 3, verts, v1 * 3);
RcVecUtils.Copy(buf, 6, verts, v2 * 3); RcVec.Copy(buf, 6, verts, v2 * 3);
int nvRow; int nvRow;
int nvIn = 3; int nvIn = 3;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,10 +24,81 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants; public static class RcRecast
public static class RcCommons
{ {
/// 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[] DirOffsetX = { -1, 0, 1, 0, };
private static readonly int[] DirOffsetY = { 0, 1, 0, -1 }; private static readonly int[] DirOffsetY = { 0, 1, 0, -1 };
private static readonly int[] DirForOffset = { 3, 0, -1, 2, 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) for (int i = 0; i < nt; ++i)
{ {
int tri = i * 3; 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. // Check if the face is walkable.
if (norm.Y > walkableThr) if (norm.Y > walkableThr)
areas[i] = areaMod.Apply(areas[i]); areas[i] = areaMod.Apply(areas[i]);
@ -136,10 +210,10 @@ namespace DotRecast.Recast
return areas; 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 e0 = v1 - v0;
var e1 = RcVecUtils.Subtract(verts, v2 * 3, v0 * 3); var e1 = v2 - v0;
norm = RcVec3f.Cross(e0, e1); norm = RcVec3f.Cross(e0, e1);
norm = RcVec3f.Normalize(norm); norm = RcVec3f.Normalize(norm);
} }
@ -162,7 +236,10 @@ namespace DotRecast.Recast
for (int i = 0; i < nt; ++i) for (int i = 0; i < nt; ++i)
{ {
int tri = i * 3; 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. // Check if the face is walkable.
if (norm.Y <= walkableThr) if (norm.Y <= walkableThr)
areas[i] = RC_NULL_AREA; areas[i] = RC_NULL_AREA;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,8 +25,8 @@ using DotRecast.Core;
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
using static RcConstants;
using static RcCommons; using static RcRecast;
public static class RcRegions public static class RcRegions
{ {
@ -1665,8 +1665,8 @@ namespace DotRecast.Recast
ctx.StartTimer(RcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED); ctx.StartTimer(RcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED);
int LOG_NB_STACKS = 3; const int LOG_NB_STACKS = 3;
int NB_STACKS = 1 << LOG_NB_STACKS; const int NB_STACKS = 1 << LOG_NB_STACKS;
List<List<RcLevelStackEntry>> lvlStacks = new List<List<RcLevelStackEntry>>(); List<List<RcLevelStackEntry>> lvlStacks = new List<List<RcLevelStackEntry>>();
for (int i = 0; i < NB_STACKS; ++i) for (int i = 0; i < NB_STACKS; ++i)
{ {

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 namespace DotRecast.Recast
{ {
/** Represents a span in a heightfield. */ /// Represents a span in a heightfield.
/// @see rcHeightfield
public class RcSpan public class RcSpan
{ {
/** The lower limit of the span. [Limit: &lt; smax] */ public int smin; //< The lower limit of the span. [Limit: < #smax]
public int smin; public int smax; //< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT]
public int area; //< The area id assigned to the span.
/** The upper limit of the span. [Limit: &lt;= SPAN_MAX_HEIGHT] */ public RcSpan next; //< The next span higher up in column.
public int smax;
/** The area id assigned to the span. */
public int area;
/** The next span higher up in column. */
public RcSpan next;
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org 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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -28,7 +28,7 @@ namespace DotRecast.Recast
public RcSpanPool() 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) for (int i = 0; i < items.Length; ++i)
{ {
items[i] = new RcSpan(); items[i] = new RcSpan();

View File

@ -1,4 +1,4 @@
namespace DotRecast.Recast namespace DotRecast.Recast
{ {
public class RcSweepSpan public class RcSweepSpan
{ {

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
namespace DotRecast.Recast namespace DotRecast.Recast
@ -44,18 +45,18 @@ namespace DotRecast.Recast
float[] verts = geom.GetVerts(); float[] verts = geom.GetVerts();
if (cfg.UseTiles) if (cfg.UseTiles)
{ {
float[] tbmin = new float[2]; RcVec2f tbmin;
float[] tbmax = new float[2]; RcVec2f tbmax;
tbmin[0] = builderCfg.bmin.X; tbmin.X = builderCfg.bmin.X;
tbmin[1] = builderCfg.bmin.Z; tbmin.Y = builderCfg.bmin.Z;
tbmax[0] = builderCfg.bmax.X; tbmax.X = builderCfg.bmax.X;
tbmax[1] = builderCfg.bmax.Z; tbmax.Y = builderCfg.bmax.Z;
List<RcChunkyTriMeshNode> nodes = geom.GetChunksOverlappingRect(tbmin, tbmax); List<RcChunkyTriMeshNode> nodes = geom.GetChunksOverlappingRect(tbmin, tbmax);
foreach (RcChunkyTriMeshNode node in nodes) foreach (RcChunkyTriMeshNode node in nodes)
{ {
int[] tris = node.tris; int[] tris = node.tris;
int ntris = tris.Length / 3; 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); RcRasterizations.RasterizeTriangles(ctx, verts, tris, m_triareas, ntris, solid, cfg.WalkableClimb);
} }
} }
@ -63,7 +64,7 @@ namespace DotRecast.Recast
{ {
int[] tris = geom.GetTris(); int[] tris = geom.GetTris();
int ntris = tris.Length / 3; 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); RcRasterizations.RasterizeTriangles(ctx, verts, tris, m_triareas, ntris, solid, cfg.WalkableClimb);
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"name": "com.rnd.dotrecast", "name": "com.rnd.dotrecast",
"displayName": "DotRecast", "displayName": "DotRecast",
"version": "0.3.0" "version": "0.4.0"
} }

View File

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

View File

@ -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<Node> _sq;
private RcBinaryMinHeap<Node> _bmHeap;
private PriorityQueue<Node, Node> _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<Node>.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();
}
}
}

View File

@ -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<long> hashTable = stackalloc long[HashTableSize];
_consumer.Consume(hashTable[0]);
}
[Benchmark]
[SkipLocalsInit]
public void Stackalloc_Long_SkipLocalsInit()
{
Span<long> hashTable = stackalloc long[HashTableSize];
_consumer.Consume(hashTable[0]);
}
[Benchmark]
public void New_Long()
{
Span<long> hashTable = new long[HashTableSize];
_consumer.Consume(hashTable[0]);
}
[Benchmark]
[SkipLocalsInit]
public void New_Long_SkipLocalsInit()
{
Span<long> hashTable = new long[HashTableSize];
_consumer.Consume(hashTable[0]);
}
}

View File

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

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\DotRecast.Core\DotRecast.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Sockets; using System.Net.Sockets;

View File

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

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using DotRecast.Core.Buffers; using DotRecast.Core.Buffers;

View File

@ -1,4 +1,4 @@
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Core.Test; namespace DotRecast.Core.Test;

View File

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

View File

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

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml;
using DotRecast.Core.Buffers; using DotRecast.Core.Buffers;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Serialization;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Core.Test; namespace DotRecast.Core.Test;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;

View File

@ -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<long> src = stackalloc long[] { 1, 2, 3 };
Span<long> 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<long> src = stackalloc long[] { 1, 2 };
Span<long> 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<ArgumentException>(() =>
{
Span<long> src = stackalloc long[] { 1, 2, 3, 4, 5 };
Span<long> 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<long> src = stackalloc long[] { 1, 2, 3, 4, 5 };
Span<long> 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<long> src = stackalloc long[] { 5, 4, 3, 2, 1 };
Span<long> 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<ArgumentOutOfRangeException>(() =>
{
Span<long> src = stackalloc long[] { 5, 4, 3, 2, 1 };
Span<long> dst = stackalloc long[] { 0, 0, 0 };
//
RcSpans.Copy(src, 3, dst, 0, 3);
});
// Test for src (index + length) over
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Span<long> src = stackalloc long[] { 5, 4, 3, 2, 1 };
Span<long> dst = stackalloc long[] { 0, 0, 0 };
//
RcSpans.Copy(src, 5, dst, 0, 1);
});
// Test for dst (idx + length) over
Assert.Throws<ArgumentException>(() =>
{
Span<long> src = stackalloc long[] { 5, 4, 3, 2, 1 };
Span<long> dst = stackalloc long[] { 0, 0, 0 };
//
RcSpans.Copy(src, 0, dst, 1, 3);
});
// Test for dst (idx + length) over
Assert.Throws<ArgumentException>(() =>
{
Span<long> src = stackalloc long[] { 5, 4, 3, 2, 1 };
Span<long> 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<long>() { 3, 1, 1 };
Span<long> 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<long>() { 2, 1, 1 };
Span<long> 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<long>() { 3, 2, 1 };
Span<long> src = stackalloc long[] { 3, 2, 1 };
RcSpans.Move(src, 0, 0, 3);
Assert.That(src.ToArray(), Is.EqualTo(expected));
}
// length over
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Span<long> src = stackalloc long[] { 3, 2, 1 };
RcSpans.Move(src, 0, 0, 4);
});
// source index over
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Span<long> src = stackalloc long[] { 3, 2, 1 };
RcSpans.Move(src, 3, 0, 1);
});
// destination index over
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Span<long> src = stackalloc long[] { 3, 2, 1 };
RcSpans.Move(src, 0, 3, 1);
});
}
}

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
using NUnit.Framework; using NUnit.Framework;

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Numerics; using System.Numerics;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using NUnit.Framework; using NUnit.Framework;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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; namespace DotRecast.Detour.Crowd.Test;
public class AbstractCrowdTest public class AbstractCrowdTest
{ {
protected readonly long[] startRefs = protected readonly long[] startRefs =
@ -63,8 +62,9 @@ public class AbstractCrowdTest
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
nmd = new RecastTestMeshBuilder().GetMeshData(); nmd = TestMeshDataFactory.Create();
navmesh = new DtNavMesh(nmd, 6, 0); navmesh = new DtNavMesh();
navmesh.Init(nmd, 6, 0);
query = new DtNavMeshQuery(navmesh); query = new DtNavMeshQuery(navmesh);
DtCrowdConfig config = new DtCrowdConfig(0.6f); DtCrowdConfig config = new DtCrowdConfig(0.6f);
crowd = new DtCrowd(config, navmesh); crowd = new DtCrowd(config, navmesh);
@ -153,7 +153,7 @@ public class AbstractCrowdTest
RcVec3f vel = RcVec3f.Subtract(tgt, pos); RcVec3f vel = RcVec3f.Subtract(tgt, pos);
vel.Y = 0.0f; vel.Y = 0.0f;
vel = RcVec3f.Normalize(vel); vel = RcVec3f.Normalize(vel);
vel = vel.Scale(speed); vel = vel * speed;
return vel; return vel;
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

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

View File

@ -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<DtStraightPath> 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<DtNavMeshQuery>(It.IsAny<DtNavMesh>());
mockQuery.Setup(q => q.FindStraightPath(
It.IsAny<RcVec3f>(),
It.IsAny<RcVec3f>(),
It.IsAny<List<long>>(),
ref It.Ref<List<DtStraightPath>>.IsAny,
It.IsAny<int>(),
It.IsAny<int>())
)
.Callback((RcVec3f startPos, RcVec3f endPos, List<long> path,
ref List<DtStraightPath> refStraightPath, int maxStraightPath, int options) =>
{
refStraightPath = straightPath;
})
.Returns(() => DtStatus.DT_SUCCESS);
var path = new List<DtStraightPath>();
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<DtStraightPath> 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<DtNavMeshQuery>(It.IsAny<DtNavMesh>());
mockQuery.Setup(q => q.FindStraightPath(
It.IsAny<RcVec3f>(),
It.IsAny<RcVec3f>(),
It.IsAny<List<long>>(),
ref It.Ref<List<DtStraightPath>>.IsAny,
It.IsAny<int>(),
It.IsAny<int>())
).Callback((RcVec3f startPos, RcVec3f endPos, List<long> path,
ref List<DtStraightPath> refStraightPath, int maxStraightPath, int options) =>
{
refStraightPath = straightPath;
})
.Returns(() => DtStatus.DT_SUCCESS);
var path = new List<DtStraightPath>();
corridor.FindCorners(ref path, int.MaxValue, mockQuery.Object, filter);
Assert.That(path.Count, Is.EqualTo(2));
Assert.That(path, Is.EqualTo(new List<DtStraightPath> { straightPath[2], straightPath[3] }));
}
}

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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; namespace DotRecast.Detour.Crowd.Test;
public class RecastTestMeshBuilder public class TestMeshDataFactory
{ {
private readonly DtMeshData meshData; private const float m_cellSize = 0.3f;
public const float m_cellSize = 0.3f; private const float m_cellHeight = 0.2f;
public const float m_cellHeight = 0.2f; private const float m_agentHeight = 2.0f;
public const float m_agentHeight = 2.0f; private const float m_agentRadius = 0.6f;
public const float m_agentRadius = 0.6f; private const float m_agentMaxClimb = 0.9f;
public const float m_agentMaxClimb = 0.9f; private const float m_agentMaxSlope = 45.0f;
public const float m_agentMaxSlope = 45.0f; private const int m_regionMinSize = 8;
public const int m_regionMinSize = 8; private const int m_regionMergeSize = 20;
public const int m_regionMergeSize = 20; private const float m_edgeMaxLen = 12.0f;
public const float m_edgeMaxLen = 12.0f; private const float m_edgeMaxError = 1.3f;
public const float m_edgeMaxError = 1.3f; private const int m_vertsPerPoly = 6;
public const int m_vertsPerPoly = 6; private const float m_detailSampleDist = 6.0f;
public const float m_detailSampleDist = 6.0f; private const float m_detailSampleMaxError = 1.0f;
public const float m_detailSampleMaxError = 1.0f;
public RecastTestMeshBuilder() public static DtMeshData Create()
: 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)
{ {
} 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( RcConfig cfg = new RcConfig(
partitionType, partition,
cellSize, cellHeight, cellSize, cellHeight,
agentMaxSlope, agentHeight, agentRadius, agentMaxClimb, agentMaxSlope, agentHeight, agentRadius, agentMaxClimb,
regionMinSize, regionMergeSize, regionMinSize, regionMergeSize,
@ -73,31 +70,31 @@ public class RecastTestMeshBuilder
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false);
RcPolyMesh m_pmesh = rcResult.Mesh; RcPolyMesh pmesh = rcResult.Mesh;
for (int i = 0; i < m_pmesh.npolys; ++i) 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(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = m_pmesh.verts; option.verts = pmesh.verts;
option.vertCount = m_pmesh.nverts; option.vertCount = pmesh.nverts;
option.polys = m_pmesh.polys; option.polys = pmesh.polys;
option.polyAreas = m_pmesh.areas; option.polyAreas = pmesh.areas;
option.polyFlags = m_pmesh.flags; option.polyFlags = pmesh.flags;
option.polyCount = m_pmesh.npolys; option.polyCount = pmesh.npolys;
option.nvp = m_pmesh.nvp; option.nvp = pmesh.nvp;
option.detailMeshes = m_dmesh.meshes; option.detailMeshes = dmesh.meshes;
option.detailVerts = m_dmesh.verts; option.detailVerts = dmesh.verts;
option.detailVertsCount = m_dmesh.nverts; option.detailVertsCount = dmesh.nverts;
option.detailTris = m_dmesh.tris; option.detailTris = dmesh.tris;
option.detailTriCount = m_dmesh.ntris; option.detailTriCount = dmesh.ntris;
option.walkableHeight = agentHeight; option.walkableHeight = agentHeight;
option.walkableRadius = agentRadius; option.walkableRadius = agentRadius;
option.walkableClimb = agentMaxClimb; option.walkableClimb = agentMaxClimb;
option.bmin = m_pmesh.bmin; option.bmin = pmesh.bmin;
option.bmax = m_pmesh.bmax; option.bmax = pmesh.bmax;
option.cs = cellSize; option.cs = cellSize;
option.ch = cellHeight; option.ch = cellHeight;
option.buildBvTree = true; option.buildBvTree = true;
@ -120,11 +117,8 @@ public class RecastTestMeshBuilder
option.offMeshConUserID = new int[1]; option.offMeshConUserID = new int[1];
option.offMeshConUserID[0] = 0x4567; option.offMeshConUserID[0] = 0x4567;
option.offMeshConCount = 1; option.offMeshConCount = 1;
meshData = DtNavMeshBuilder.CreateNavMeshData(option); var meshData = DtNavMeshBuilder.CreateNavMeshData(option);
}
public DtMeshData GetMeshData()
{
return meshData; return meshData;
} }
} }

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -6,6 +7,7 @@ using DotRecast.Core.Numerics;
using DotRecast.Detour.Dynamic.Colliders; using DotRecast.Detour.Dynamic.Colliders;
using DotRecast.Detour.Dynamic.Io; using DotRecast.Detour.Dynamic.Io;
using DotRecast.Detour.Dynamic.Test.Io; using DotRecast.Detour.Dynamic.Test.Io;
using DotRecast.Detour.Io;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Dynamic.Test; namespace DotRecast.Detour.Dynamic.Test;
@ -21,7 +23,7 @@ public class DynamicNavMeshTest
[Test] [Test]
public void E2eTest() 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 ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
@ -78,4 +80,101 @@ public class DynamicNavMeshTest
// path length should be back to the initial value // path length should be back to the initial value
Assert.That(path.Count, Is.EqualTo(16)); 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<long> path = new List<long>();
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<long>();
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<long>();
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));
}
}
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -31,7 +31,7 @@ public class VoxelFileReaderTest
[Test] [Test]
public void ShouldReadSingleTileFile() public void ShouldReadSingleTileFile()
{ {
byte[] bytes = RcResources.Load("test.voxels"); byte[] bytes = RcIO.ReadFileIfFound("test.voxels");
using var ms = new MemoryStream(bytes); using var ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
@ -57,7 +57,7 @@ public class VoxelFileReaderTest
[Test] [Test]
public void ShouldReadMultiTileFile() 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 ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -32,7 +32,7 @@ public class VoxelFileReaderWriterTest
[TestCase(true)] [TestCase(true)]
public void ShouldReadSingleTileFile(bool compression) 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 ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
@ -60,7 +60,7 @@ public class VoxelFileReaderWriterTest
[TestCase(true)] [TestCase(true)]
public void ShouldReadMultiTileFile(bool compression) 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 ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -93,7 +93,7 @@ public class VoxelQueryTest
private DtDynamicNavMesh CreateDynaMesh() 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 ms = new MemoryStream(bytes);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -21,7 +22,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public abstract class AbstractDetourTest public abstract class AbstractDetourTest
{ {
protected static readonly long[] startRefs = protected static readonly long[] startRefs =
@ -64,6 +64,8 @@ public abstract class AbstractDetourTest
protected DtNavMesh CreateNavMesh() protected DtNavMesh CreateNavMesh()
{ {
return new DtNavMesh(new RecastTestMeshBuilder().GetMeshData(), 6, 0); var mesh = new DtNavMesh();
mesh.Init(TestMeshDataFactory.Create(), 6, 0);
return mesh;
} }
} }

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,4 +1,4 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
using NUnit.Framework; using NUnit.Framework;

View File

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

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -21,10 +22,12 @@ using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class FindNearestPolyTest : AbstractDetourTest 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 = private static readonly RcVec3f[] POLY_POS =
{ {
@ -44,11 +47,11 @@ public class FindNearestPolyTest : AbstractDetourTest
{ {
RcVec3f startPos = startPoss[i]; RcVec3f startPos = startPoss[i];
var status = query.FindNearestPoly(startPos, extents, filter, out var nearestRef, out var nearestPt, out var _); var status = query.FindNearestPoly(startPos, extents, filter, out var nearestRef, out var nearestPt, out var _);
Assert.That(status.Succeeded(), Is.True); Assert.That(status.Succeeded(), Is.True, $"index({i})");
Assert.That(nearestRef, Is.EqualTo(POLY_REFS[i])); Assert.That(nearestRef, Is.EqualTo(POLY_REFS[i]), $"index({i})");
Assert.That(nearestPt.X, Is.EqualTo(POLY_POS[i].X).Within(0.001f)); 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)); 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)); Assert.That(nearestPt.Z, Is.EqualTo(POLY_POS[i].Z).Within(0.001f), $"index({i})");
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class FindPathTest : AbstractDetourTest public class FindPathTest : AbstractDetourTest
{ {
private static readonly DtStatus[] STATUSES = private static readonly DtStatus[] STATUSES =
@ -184,6 +185,7 @@ public class FindPathTest : AbstractDetourTest
{ {
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
var path = new List<long>(); var path = new List<long>();
Span<DtStraightPath> straightPath = stackalloc DtStraightPath[256];
for (int i = 0; i < STRAIGHT_PATHS.Length; i++) for (int i = 0; i < STRAIGHT_PATHS.Length; i++)
{ {
// startRefs.Length; i++) { // startRefs.Length; i++) {
@ -192,9 +194,8 @@ public class FindPathTest : AbstractDetourTest
var startPos = startPoss[i]; var startPos = startPoss[i];
var endPos = endPoss[i]; var endPos = endPoss[i];
var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption);
var straightPath = new List<DtStraightPath>(); query.FindStraightPath(startPos, endPos, path, path.Count, straightPath, out var nstraightPath, 256, 0);
query.FindStraightPath(startPos, endPos, path, ref straightPath, int.MaxValue, 0); Assert.That(nstraightPath, Is.EqualTo(STRAIGHT_PATHS[i].Length));
Assert.That(straightPath.Count, Is.EqualTo(STRAIGHT_PATHS[i].Length));
for (int j = 0; j < STRAIGHT_PATHS[i].Length; j++) for (int j = 0; j < STRAIGHT_PATHS[i].Length; j++)
{ {
Assert.That(straightPath[j].refs, Is.EqualTo(STRAIGHT_PATHS[i][j].refs)); Assert.That(straightPath[j].refs, Is.EqualTo(STRAIGHT_PATHS[i][j].refs));

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class GetPolyWallSegmentsTest : AbstractDetourTest public class GetPolyWallSegmentsTest : AbstractDetourTest
{ {
private static readonly RcSegmentVert[][] VERTICES = private static readonly RcSegmentVert[][] VERTICES =
@ -82,28 +83,30 @@ public class GetPolyWallSegmentsTest : AbstractDetourTest
[Test] [Test]
public void TestFindDistanceToWall() public void TestFindDistanceToWall()
{ {
var segmentVerts = new List<RcSegmentVert>(); const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
var segmentRefs = new List<long>(); Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
int nsegs = 0;
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
for (int i = 0; i < startRefs.Length; i++) for (int i = 0; i < startRefs.Length; i++)
{ {
var result = query.GetPolyWallSegments(startRefs[i], true, filter, ref segmentVerts, ref segmentRefs); var result = query.GetPolyWallSegments(startRefs[i], filter, segs, refs, ref nsegs, MAX_SEGS);
Assert.That(segmentVerts.Count, Is.EqualTo(VERTICES[i].Length)); Assert.That(nsegs, Is.EqualTo(VERTICES[i].Length));
Assert.That(segmentRefs.Count, Is.EqualTo(REFS[i].Length)); Assert.That(nsegs, Is.EqualTo(REFS[i].Length));
for (int v = 0; v < VERTICES[i].Length / 6; v++) 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(segs[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(segs[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(segs[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(segs[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(segs[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].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f));
} }
for (int v = 0; v < REFS[i].Length; v++) 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]));
} }
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -32,8 +33,7 @@ public class MeshDataReaderWriterTest
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
RecastTestMeshBuilder rcBuilder = new RecastTestMeshBuilder(); meshData = TestMeshDataFactory.Create();
meshData = rcBuilder.GetMeshData();
} }
[Test] [Test]
@ -117,11 +117,8 @@ public class MeshDataReaderWriterTest
for (int i = 0; i < meshData.header.bvNodeCount; i++) for (int i = 0; i < meshData.header.bvNodeCount; i++)
{ {
Assert.That(readData.bvTree[i].i, Is.EqualTo(meshData.bvTree[i].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, Is.EqualTo(meshData.bvTree[i].bmin));
{ Assert.That(readData.bvTree[i].bmax, Is.EqualTo(meshData.bvTree[i].bmax));
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]));
}
} }
for (int i = 0; i < meshData.header.offMeshConCount; i++) for (int i = 0; i < meshData.header.offMeshConCount; i++)

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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; namespace DotRecast.Detour.Test.Io;
public class MeshSetReaderTest public class MeshSetReaderTest
{ {
private readonly DtMeshSetReader reader = new DtMeshSetReader(); private readonly DtMeshSetReader reader = new DtMeshSetReader();
@ -32,27 +32,35 @@ public class MeshSetReaderTest
[Test] [Test]
public void TestNavmesh() 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 ms = new MemoryStream(@is);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
DtNavMesh mesh = reader.Read(br, 6); DtNavMesh mesh = reader.Read(br, 6);
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> 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.polys.Length, Is.EqualTo(7));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(22 * 3)); 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.polys.Length, Is.EqualTo(7));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(26 * 3)); 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.polys.Length, Is.EqualTo(1));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(4 * 3)); 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.polys.Length, Is.EqualTo(8));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(24 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(24 * 3));
} }
@ -60,7 +68,7 @@ public class MeshSetReaderTest
[Test] [Test]
public void TestDungeon() 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 ms = new MemoryStream(@is);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
@ -68,20 +76,28 @@ public class MeshSetReaderTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); 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.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 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.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }
@ -89,7 +105,7 @@ public class MeshSetReaderTest
[Test] [Test]
public void TestDungeon32Bit() 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 ms = new MemoryStream(@is);
using var br = new BinaryReader(ms); using var br = new BinaryReader(ms);
@ -97,20 +113,28 @@ public class MeshSetReaderTest
Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128)); Assert.That(mesh.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); 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.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 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.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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; namespace DotRecast.Detour.Test.Io;
public class MeshSetReaderWriterTest public class MeshSetReaderWriterTest
{ {
private readonly DtMeshSetWriter writer = new DtMeshSetWriter(); private readonly DtMeshSetWriter writer = new DtMeshSetWriter();
@ -66,11 +66,12 @@ public class MeshSetReaderWriterTest
header.option.maxTiles = m_maxTiles; header.option.maxTiles = m_maxTiles;
header.option.maxPolys = m_maxPolysPerTile; header.option.maxPolys = m_maxPolysPerTile;
header.numTiles = 0; header.numTiles = 0;
DtNavMesh mesh = new DtNavMesh(header.option, 6); DtNavMesh mesh = new DtNavMesh();
mesh.Init(header.option, 6);
RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax(); 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 y = 0; y < th; ++y)
{ {
for (int x = 0; x < tw; ++x) for (int x = 0; x < tw; ++x)
@ -92,7 +93,7 @@ public class MeshSetReaderWriterTest
if (data != null) if (data != null)
{ {
mesh.RemoveTile(mesh.GetTileRefAt(x, y, 0)); 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.GetMaxTiles(), Is.EqualTo(128));
Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000)); Assert.That(mesh.GetParams().maxPolys, Is.EqualTo(0x8000));
Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f)); Assert.That(mesh.GetParams().tileWidth, Is.EqualTo(9.6f).Within(0.001f));
List<DtMeshTile> 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(7 * 3)); 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.polys.Length, Is.EqualTo(2));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(9 * 3)); 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.polys.Length, Is.EqualTo(3));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(6 * 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.polys.Length, Is.EqualTo(5));
Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3)); Assert.That(tiles[0].data.verts.Length, Is.EqualTo(17 * 3));
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class MoveAlongSurfaceTest : AbstractDetourTest public class MoveAlongSurfaceTest : AbstractDetourTest
{ {
private static readonly long[][] VISITED = private static readonly long[][] VISITED =
@ -69,20 +70,21 @@ public class MoveAlongSurfaceTest : AbstractDetourTest
public void TestMoveAlongSurface() public void TestMoveAlongSurface()
{ {
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
var visited = new List<long>(); const int MAX_VISITED = 32;
Span<long> visited = stackalloc long[MAX_VISITED];
for (int i = 0; i < startRefs.Length; i++) for (int i = 0; i < startRefs.Length; i++)
{ {
long startRef = startRefs[i]; long startRef = startRefs[i];
RcVec3f startPos = startPoss[i]; RcVec3f startPos = startPoss[i];
RcVec3f endPos = endPoss[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(status.Succeeded(), Is.True);
Assert.That(result.X, Is.EqualTo(POSITION[i].X).Within(0.01f)); 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.Y, Is.EqualTo(POSITION[i].Y).Within(0.01f));
Assert.That(result.Z, Is.EqualTo(POSITION[i].Z).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++) for (int j = 0; j < 3; j++)
{ {
Assert.That(visited[j], Is.EqualTo(VISITED[i][j])); Assert.That(visited[j], Is.EqualTo(VISITED[i][j]));

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -21,6 +22,7 @@ using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
using static DtDetour;
public class NavMeshBuilderTest public class NavMeshBuilderTest
{ {
@ -29,7 +31,7 @@ public class NavMeshBuilderTest
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
nmd = new RecastTestMeshBuilder().GetMeshData(); nmd = TestMeshDataFactory.Create();
} }
[Test] [Test]
@ -52,12 +54,12 @@ public class NavMeshBuilderTest
for (int i = 0; i < 2; i++) 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].rad, Is.EqualTo(0.1f));
Assert.That(nmd.offMeshCons[0].poly, Is.EqualTo(118)); 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].side, Is.EqualTo(0xFF));
Assert.That(nmd.offMeshCons[0].userId, Is.EqualTo(0x4567)); Assert.That(nmd.offMeshCons[0].userId, Is.EqualTo(0x4567));
Assert.That(nmd.polys[118].vertCount, Is.EqualTo(2)); Assert.That(nmd.polys[118].vertCount, Is.EqualTo(2));

View File

@ -1,6 +1,6 @@
/* /*
recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,5 +1,6 @@
/* /*
recast4j Copyright (c) 2015-2021 Piotr Piastucki piotr@jtilia.org 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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class RandomPointTest : AbstractDetourTest public class RandomPointTest : AbstractDetourTest
{ {
[Test] [Test]
[Repeat(10)]
public void TestRandom() public void TestRandom()
{ {
RcRand f = new RcRand(1); RcRand f = new RcRand(1);
IDtQueryFilter filter = new DtQueryDefaultFilter(); 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); var status = query.FindRandomPoint(filter, f, out var randomRef, out var randomPt);
Assert.That(status.Succeeded(), Is.True); 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 >= bmin[1], Is.True);
Assert.That(randomPt.Z <= bmax[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] [Test]
@ -103,7 +108,7 @@ public class RandomPointTest : AbstractDetourTest
var status = query.FindRandomPointWithinCircle(randomRef, randomPt, radius, filter, f, out var nextRandomRef, out var nextRandomPt); var status = query.FindRandomPointWithinCircle(randomRef, randomPt, radius, filter, f, out var nextRandomRef, out var nextRandomPt);
Assert.That(status.Failed(), Is.False); Assert.That(status.Failed(), Is.False);
float distance = RcVecUtils.Dist2D(randomPt, nextRandomPt); float distance = RcVec.Dist2D(randomPt, nextRandomPt);
Assert.That(distance <= radius, Is.True); Assert.That(distance <= radius, Is.True);
randomRef = nextRandomRef; randomRef = nextRandomRef;

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using DotRecast.Core.Numerics;
using DotRecast.Recast; using DotRecast.Recast;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
namespace DotRecast.Detour.Test; 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_cellSize = 0.3f;
private const float m_cellHeight = 0.2f; private const float m_cellHeight = 0.2f;
private const float m_agentHeight = 2.0f; 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_detailSampleDist = 6.0f;
private const float m_detailSampleMaxError = 1.0f; private const float m_detailSampleMaxError = 1.0f;
public RecastTestMeshBuilder() public static DtMeshData Create()
: 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)
{ {
} 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( RcConfig cfg = new RcConfig(
partition, partition,
cellSize, cellHeight, cellSize, cellHeight,
@ -73,31 +69,31 @@ public class RecastTestMeshBuilder
RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax()); RcBuilderConfig bcfg = new RcBuilderConfig(cfg, geom.GetMeshBoundsMin(), geom.GetMeshBoundsMax());
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false); RcBuilderResult rcResult = rcBuilder.Build(geom, bcfg, false);
RcPolyMesh m_pmesh = rcResult.Mesh; RcPolyMesh pmesh = rcResult.Mesh;
for (int i = 0; i < m_pmesh.npolys; ++i) 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(); DtNavMeshCreateParams option = new DtNavMeshCreateParams();
option.verts = m_pmesh.verts; option.verts = pmesh.verts;
option.vertCount = m_pmesh.nverts; option.vertCount = pmesh.nverts;
option.polys = m_pmesh.polys; option.polys = pmesh.polys;
option.polyAreas = m_pmesh.areas; option.polyAreas = pmesh.areas;
option.polyFlags = m_pmesh.flags; option.polyFlags = pmesh.flags;
option.polyCount = m_pmesh.npolys; option.polyCount = pmesh.npolys;
option.nvp = m_pmesh.nvp; option.nvp = pmesh.nvp;
option.detailMeshes = m_dmesh.meshes; option.detailMeshes = dmesh.meshes;
option.detailVerts = m_dmesh.verts; option.detailVerts = dmesh.verts;
option.detailVertsCount = m_dmesh.nverts; option.detailVertsCount = dmesh.nverts;
option.detailTris = m_dmesh.tris; option.detailTris = dmesh.tris;
option.detailTriCount = m_dmesh.ntris; option.detailTriCount = dmesh.ntris;
option.walkableHeight = agentHeight; option.walkableHeight = agentHeight;
option.walkableRadius = agentRadius; option.walkableRadius = agentRadius;
option.walkableClimb = agentMaxClimb; option.walkableClimb = agentMaxClimb;
option.bmin = m_pmesh.bmin; option.bmin = pmesh.bmin;
option.bmax = m_pmesh.bmax; option.bmax = pmesh.bmax;
option.cs = cellSize; option.cs = cellSize;
option.ch = cellHeight; option.ch = cellHeight;
option.buildBvTree = true; option.buildBvTree = true;
@ -120,11 +116,8 @@ public class RecastTestMeshBuilder
option.offMeshConUserID = new int[1]; option.offMeshConUserID = new int[1];
option.offMeshConUserID[0] = 0x4567; option.offMeshConUserID[0] = 0x4567;
option.offMeshConCount = 1; option.offMeshConCount = 1;
meshData = DtNavMeshBuilder.CreateNavMeshData(option); var meshData = DtNavMeshBuilder.CreateNavMeshData(option);
}
public DtMeshData GetMeshData()
{
return meshData; return meshData;
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 System.Collections.Generic;
using DotRecast.Core.Numerics;
using DotRecast.Recast; using DotRecast.Recast;
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
@ -64,7 +64,8 @@ public class TestTiledNavMeshBuilder
navMeshParams.tileHeight = tileSize * cellSize; navMeshParams.tileHeight = tileSize * cellSize;
navMeshParams.maxTiles = 128; navMeshParams.maxTiles = 128;
navMeshParams.maxPolys = 32768; navMeshParams.maxPolys = 32768;
navMesh = new DtNavMesh(navMeshParams, 6); navMesh = new DtNavMesh();
navMesh.Init(navMeshParams, 6);
// Build all tiles // Build all tiles
RcConfig cfg = new RcConfig(true, tileSize, tileSize, RcConfig.CalcBorder(agentRadius, cellSize), RcConfig cfg = new RcConfig(true, tileSize, tileSize, RcConfig.CalcBorder(agentRadius, cellSize),
@ -119,7 +120,7 @@ public class TestTiledNavMeshBuilder
option.tileX = result.TileX; option.tileX = result.TileX;
option.tileZ = result.TileZ; option.tileZ = result.TileZ;
option.buildBvTree = true; option.buildBvTree = true;
navMesh.AddTile(DtNavMeshBuilder.CreateNavMeshData(option), 0, 0); navMesh.AddTile(Detour.DtNavMeshBuilder.CreateNavMeshData(option), 0, 0, out _);
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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) public DtTileCache GetTileCache(IInputGeomProvider geom, RcByteOrder order, bool cCompatibility)
{ {
DtTileCacheParams option = new DtTileCacheParams(); 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.ch = m_cellHeight;
option.cs = m_cellSize; option.cs = m_cellSize;
option.orig = geom.GetMeshBoundsMin(); option.orig = geom.GetMeshBoundsMin();
@ -64,7 +64,8 @@ public class AbstractTileCacheTest
navMeshParams.maxTiles = 256; navMeshParams.maxTiles = 256;
navMeshParams.maxPolys = 16384; 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 comp = DtTileCacheCompressorFactory.Shared.Create(cCompatibility ? 0 : 1);
var storageParams = new DtTileCacheStorageParams(order, cCompatibility); var storageParams = new DtTileCacheStorageParams(order, cCompatibility);
var process = new TestTileCacheMeshProcess(); var process = new TestTileCacheMeshProcess();

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -35,7 +35,7 @@ public class TileCacheReaderTest
[Test] [Test]
public void TestNavmesh() 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); using var br = new BinaryReader(ms);
DtTileCache tc = reader.Read(br, 6, null); DtTileCache tc = reader.Read(br, 6, null);
Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256)); Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256));
@ -133,7 +133,7 @@ public class TileCacheReaderTest
[Test] [Test]
public void TestDungeon() 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); using var br = new BinaryReader(ms);
DtTileCache tc = reader.Read(br, 6, null); DtTileCache tc = reader.Read(br, 6, null);
Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256)); Assert.That(tc.GetNavMesh().GetMaxTiles(), Is.EqualTo(256));

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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; namespace DotRecast.Detour.TileCache.Test;
public class TempObstaclesTest : AbstractTileCacheTest public class TempObstaclesTest : AbstractTileCacheTest
{ {
[Test] [Test]
@ -43,21 +42,29 @@ public class TempObstaclesTest : AbstractTileCacheTest
tc.BuildNavMeshTile(refs); tc.BuildNavMeshTile(refs);
} }
List<DtMeshTile> 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]; DtMeshTile tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
long o = tc.AddObstacle(new RcVec3f(-1.815208f, 9.998184f, -20.307983f), 1f, 2f); long o = tc.AddObstacle(new RcVec3f(-1.815208f, 9.998184f, -20.307983f), 1f, 2f);
bool upToDate = tc.Update(); bool upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.vertCount, Is.EqualTo(22));
Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11));
tc.RemoveObstacle(o); tc.RemoveObstacle(o);
upToDate = tc.Update(); upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
@ -77,24 +84,32 @@ public class TempObstaclesTest : AbstractTileCacheTest
tc.BuildNavMeshTile(refs); tc.BuildNavMeshTile(refs);
} }
List<DtMeshTile> 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]; DtMeshTile tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));
long o = tc.AddBoxObstacle( long o = tc.AddBoxObstacle(
new RcVec3f(-2.315208f, 9.998184f, -20.807983f), new RcVec3f(-2.315208f, 9.998184f, -20.807983f),
new RcVec3f(-1.315208f, 11.998184f, -19.807983f) new RcVec3f(-1.315208f, 11.998184f, -19.807983f)
); );
bool upToDate = tc.Update(); bool upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(22)); Assert.That(tile.data.header.vertCount, Is.EqualTo(22));
Assert.That(tile.data.header.polyCount, Is.EqualTo(11)); Assert.That(tile.data.header.polyCount, Is.EqualTo(11));
tc.RemoveObstacle(o); tc.RemoveObstacle(o);
upToDate = tc.Update(); upToDate = tc.Update();
Assert.That(upToDate, Is.True); Assert.That(upToDate, Is.True);
tiles = tc.GetNavMesh().GetTilesAt(1, 4);
nneis = tc.GetNavMesh().GetTilesAt(1, 4, tiles, MAX_NEIS);
tile = tiles[0]; tile = tiles[0];
Assert.That(tile.data.header.vertCount, Is.EqualTo(16)); Assert.That(tile.data.header.vertCount, Is.EqualTo(16));
Assert.That(tile.data.header.polyCount, Is.EqualTo(6)); Assert.That(tile.data.header.polyCount, Is.EqualTo(6));

View File

@ -1,4 +1,4 @@
using DotRecast.Recast.Geom; using DotRecast.Recast.Geom;
namespace DotRecast.Detour.TileCache.Test; namespace DotRecast.Detour.TileCache.Test;

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax(); 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<byte[]> Build(RcByteOrder order, bool cCompatibility, int threads) public List<byte[]> Build(RcByteOrder order, bool cCompatibility, int threads)

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using DotRecast.Core; using DotRecast.Core;
@ -29,7 +30,6 @@ using NUnit.Framework;
namespace DotRecast.Detour.TileCache.Test; namespace DotRecast.Detour.TileCache.Test;
public class TileCacheFindPathTest : AbstractTileCacheTest public class TileCacheFindPathTest : AbstractTileCacheTest
{ {
private readonly RcVec3f start = new RcVec3f(39.44734f, 9.998177f, -0.784811f); private readonly RcVec3f start = new RcVec3f(39.44734f, 9.998177f, -0.784811f);
@ -39,7 +39,7 @@ public class TileCacheFindPathTest : AbstractTileCacheTest
public TileCacheFindPathTest() 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); using var br = new BinaryReader(msr);
DtTileCache tcC = new DtTileCacheReader(DtTileCacheCompressorFactory.Shared).Read(br, 6, new TestTileCacheMeshProcess()); DtTileCache tcC = new DtTileCacheReader(DtTileCacheCompressorFactory.Shared).Read(br, 6, new TestTileCacheMeshProcess());
navmesh = tcC.GetNavMesh(); navmesh = tcC.GetNavMesh();
@ -56,11 +56,11 @@ public class TileCacheFindPathTest : AbstractTileCacheTest
var path = new List<long>(); var path = new List<long>();
var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption); var status = query.FindPath(startRef, endRef, startPos, endPos, filter, ref path, DtFindPathOption.NoOption);
int maxStraightPath = 256; const int maxStraightPath = 256;
int options = 0; int options = 0;
var pathStr = new List<DtStraightPath>(); Span<DtStraightPath> pathStr = stackalloc DtStraightPath[maxStraightPath];
query.FindStraightPath(startPos, endPos, path, ref pathStr, maxStraightPath, options); query.FindStraightPath(startPos, endPos, path, path.Count, pathStr, out var npathStr, maxStraightPath, options);
Assert.That(pathStr.Count, Is.EqualTo(8)); Assert.That(npathStr, Is.EqualTo(8));
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 2009-2010 Mikko Mononen memon@inside.org Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,4 +1,4 @@
using DotRecast.Detour.TileCache.Io.Compress; using DotRecast.Detour.TileCache.Io.Compress;
using DotRecast.Detour.TileCache.Test.Io; using DotRecast.Detour.TileCache.Test.Io;
using NUnit.Framework; using NUnit.Framework;

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.70" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.1.0" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0"> <PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -24,9 +25,6 @@ using NUnit.Framework;
namespace DotRecast.Recast.Test; namespace DotRecast.Recast.Test;
using static RcConstants;
public class RecastLayersTest public class RecastLayersTest
{ {
private const float m_cellSize = 0.3f; private const float m_cellSize = 0.3f;

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -25,10 +26,9 @@ using NUnit.Framework;
namespace DotRecast.Recast.Test; namespace DotRecast.Recast.Test;
using static RcConstants; using static RcRecast;
using static RcAreas; using static RcAreas;
public class RecastSoloMeshTest public class RecastSoloMeshTest
{ {
private const float m_cellSize = 0.3f; 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. // 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 // If your input data is multiple meshes, you can transform them here, calculate
// the are type for each of the meshes and rasterize them. // 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); RcRasterizations.RasterizeTriangles(m_ctx, verts, tris, m_triareas, ntris, m_solid, cfg.WalkableClimb);
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -21,8 +22,7 @@ using DotRecast.Core;
namespace DotRecast.Recast.Test; namespace DotRecast.Recast.Test;
using static RcConstants; using static RcRecast;
public class RecastTest public class RecastTest
{ {
@ -39,18 +39,18 @@ public class RecastTest
RcContext ctx = new RcContext(); RcContext ctx = new RcContext();
{ {
int[] areas = { 42 }; 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"); Assert.That(areas[0], Is.EqualTo(RC_NULL_AREA), "Sets area ID of unwalkable triangle to RC_NULL_AREA");
} }
{ {
int[] areas = { 42 }; 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"); Assert.That(areas[0], Is.EqualTo(42), "Does not modify walkable triangle aread ID's");
} }
{ {
int[] areas = { 42 }; int[] areas = { 42 };
walkableSlopeAngle = 0; 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."); Assert.That(areas[0], Is.EqualTo(RC_NULL_AREA), "Slopes equal to the max slope are considered unwalkable.");
} }
} }

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -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 This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

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

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
</Project>

View File

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