diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..614fb8a8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,26 @@ +root = true + +############################### +# Core EditorConfig Options # +############################### + +# All files +[*] +indent_style = space +charset = utf-8 + +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct,proto,json}] +indent_size = 2 + +[*.cs] +indent_size = 4 +dotnet_sort_system_directives_first = true + +# Wrapping preferences +csharp_preserve_single_line_statements = false +csharp_preserve_single_line_blocks = true + +# ReSharper properties +resharper_csharp_wrap_lines = false +resharper_csharp_space_before_trailing_comment = true +resharper_csharp_space_after_operator_keyword = true diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 61d40e21..00000000 --- a/.gitattributes +++ /dev/null @@ -1,50 +0,0 @@ -#Image -*.jpg filter=lfs diff=lfs merge=lfs -text -*.jpeg filter=lfs diff=lfs merge=lfs -text -*.png filter=lfs diff=lfs merge=lfs -text -*.gif filter=lfs diff=lfs merge=lfs -text -*.psd filter=lfs diff=lfs merge=lfs -text -*.ai filter=lfs diff=lfs merge=lfs -text -*.tif filter=lfs diff=lfs merge=lfs -text - -#Audio -*.mp3 filter=lfs diff=lfs merge=lfs -text -*.wav filter=lfs diff=lfs merge=lfs -text -*.ogg filter=lfs diff=lfs merge=lfs -text - -#Video -*.mp4 filter=lfs diff=lfs merge=lfs -text -*.mov filter=lfs diff=lfs merge=lfs -text -*.avi filter=lfs diff=lfs merge=lfs -text -*.mkv filter=lfs diff=lfs merge=lfs -text -*.wmv filter=lfs diff=lfs merge=lfs -text - -#3D Object -*.FBX filter=lfs diff=lfs merge=lfs -text -*.fbx filter=lfs diff=lfs merge=lfs -text -*.blend filter=lfs diff=lfs merge=lfs -text -*.obj filter=lfs diff=lfs merge=lfs -text -*.stl filter=lfs diff=lfs merge=lfs -text -*.dae filter=lfs diff=lfs merge=lfs -text - -#zip -*.zip filter=lfs diff=lfs merge=lfs -text -*.7z filter=lfs diff=lfs merge=lfs -text -*.tar filter=lfs diff=lfs merge=lfs -text -*.tar.gz filter=lfs diff=lfs merge=lfs -text -*.tar.bz2 filter=lfs diff=lfs merge=lfs -text -*.iso filter=lfs diff=lfs merge=lfs -text - -#library -*.dll filter=lfs diff=lfs merge=lfs -text -*.a filter=lfs diff=lfs merge=lfs -text -*.so filter=lfs diff=lfs merge=lfs -text - -#ETC -*.ttf filter=lfs diff=lfs merge=lfs -text -*.pdf filter=lfs diff=lfs merge=lfs -text -*.docx filter=lfs diff=lfs merge=lfs -text -*.exe filter=lfs diff=lfs merge=lfs -text -*.pptx filter=lfs diff=lfs merge=lfs -text -*.xlsx filter=lfs diff=lfs merge=lfs -text -*.vsd filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..85e39afd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: ikpil diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..97d154c0 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,37 @@ +name-template: '$RESOLVED_VERSION' +tag-template: '$RESOLVED_VERSION' +template: | + # What's Changed + + $CHANGES + + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION + +categories: + - title: 'Changes' + labels: + - 'feature' + - 'feat' + + - title: 'Bug Fixes' + labels: + - 'bug' + - 'fix' + +version-resolver: + major: + labels: + - 'type: breaking' + minor: + labels: + - 'type: feature' + patch: + labels: + - 'type: bug' + - 'type: maintenance' + - 'type: docs' + - 'type: dependencies' + - 'type: security' + +exclude-labels: + - 'skip-changelog' \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..0093ad10 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,88 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '17 1 * * 1' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Set up .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: security-extended + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index fbad2de9..af233a58 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -5,26 +5,47 @@ name: .NET on: push: - branches: [ "main" ] + branches: + - 'main' + - 'pr/**' + paths: + - '**.cs' + - '**.csproj' + - '**.sln' + - '**.yml' pull_request: - branches: [ "main" ] + branches: + - 'pr/**' + paths: + - '**.cs' + - '**.csproj' + - '**.sln' + - '**.yml' jobs: - build: - - runs-on: ubuntu-latest + build-and-test: + name: test-${{matrix.os}}-${{matrix.dotnet-version}} + runs-on: ${{ matrix.os }} + strategy: + matrix: + dotnet-version: [ '6', '7', '8' ] + os: [ windows-latest, ubuntu-latest, macos-latest ] steps: - - uses: actions/checkout@v3 - with: - lfs: true - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 7.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Get all history to allow automatic versioning using MinVer + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.dotnet-version }}.x + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build -c Release --no-restore --framework net${{ matrix.dotnet-version }}.0 + + - name: Test + run: dotnet test -c Release --no-build --verbosity normal --framework net${{ matrix.dotnet-version }}.0 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..f00fc9f6 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,55 @@ +name: Nuget Publish + +on: + workflow_dispatch: + inputs: + version: + description: version + required: true + type: string + +jobs: + publish-to-nuget: + runs-on: ubuntu-latest + steps: + - name: version pattern + id: check-version + run: | + version="${{ github.event.inputs.version }}" + if [[ $version =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "Input matches pattern: $version" + else + echo "Input does not match pattern: $version" + exit 1 + fi + + - name: version check + if: success() + run: echo ok + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Get all history to allow automatic versioning using MinVer + + - name: Setup Dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build -c Release --no-restore + + - name: Test + run: dotnet test -c Release --no-build --verbosity normal + + - name: Pack + run: dotnet pack -p:PackageVersion=${{ github.event.inputs.version }} -c Release --nologo --output working-nuget + + - name: Publish the package to nuget.org + run: dotnet nuget push ./working-nuget/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json + env: + NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..2e75fbe1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,65 @@ +name: Release +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Get all history to allow automatic versioning using MinVer + + - name: Setup Dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.x' + + - name: restore dependencies + run: dotnet restore + + - name: build + run: dotnet build -c Release --no-restore + + - name: publish + run: dotnet publish src/DotRecast.Recast.Demo -c Release --framework net8.0 --no-restore --no-self-contained --output working-temp + + - name: version + id: version + run: | + tag=${GITHUB_REF/refs\/tags\//} + version=${tag} + major=${version%%.*} + echo "tag=${tag}" >> $GITHUB_OUTPUT + echo "version=${version}" >> $GITHUB_OUTPUT + echo "major=${major}" >> $GITHUB_OUTPUT + + - name: Zip + working-directory: ${{ env.working-directory }} + run: | + cp -rfv resources working-temp + cd working-temp + echo "dotnet DotRecast.Recast.Demo.dll" > run.bat + zip -r ../DotRecast.Recast.Demo-${{ steps.version.outputs.version}}.zip ./ + if: success() + + - uses: release-drafter/release-drafter@master + with: + version: ${{ steps.version.outputs.version }} + publish: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: zip Upload + uses: softprops/action-gh-release@v1 + with: + files: | + DotRecast.Recast.Demo-${{ steps.version.outputs.version}}.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + diff --git a/.vscode/launch.json b/.vscode/launch.json index 38594e31..68112dec 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/src/DotRecast.Recast.Demo/bin/Debug/net7.0/DotRecast.Recast.Demo.dll", + "program": "${workspaceFolder}/src/DotRecast.Recast.Demo/bin/Debug/net8.0/DotRecast.Recast.Demo.dll", "args": [], "cwd": "${workspaceFolder}/src/DotRecast.Recast.Demo", "console": "internalConsole", diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..bb2a12e7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "DotRecast.sln" +} \ No newline at end of file diff --git a/BuildingAndIntegrating.md b/BuildingAndIntegrating.md new file mode 100644 index 00000000..8f933681 --- /dev/null +++ b/BuildingAndIntegrating.md @@ -0,0 +1,97 @@ + +## 🔨 Build +- Building requires only .NET 8 SDK. + +### 🔨 Building with Command Prompt + +```shell +dotnet build -c Release +``` + +### 🔨 Building with an IDE + +1. Open IDE: Launch your C# IDE (e.g., Visual Studio). +2. Open Solution: Go to the "File" menu and select "Open Solution." +3. Build: In the IDE menu, select "Build" > "Build Solution" or click the "Build" icon on the toolbar. + +## ▶️ Run +- To verify the run for all modules, run [DotRecast.Recast.Demo](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj) +- on windows requirement : install to [Microsoft Visual C++ Redistributable Package](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist) + +### ▶️ Running With Command Prompt + +```shell +dotnet run --project src/DotRecast.Recast.Demo --framework net8.0 -c Release +``` + +### ▶️ Running With IDE (ex. Visual Studio 2022 or Rider ...) + +1. Open your C# IDE (like Visual Studio). +2. Go to "File" in the menu. +3. Choose "Open Project" or "Open Solution." +4. Find and select [DotRecast.sln](DotRecast.sln), then click "Open." +5. Run to [DotRecast.Recast.Demo](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast.Demo/DotRecast.Recast.Demo.csproj) + +## 🧪 Running Unit Test + +- [DotRecast.Core.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Core.Test) : ... +- [DotRecast.Recast.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Recast.Test) : ... +- [DotRecast.Detour.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.Test) : ... +- [DotRecast.Detour.TileCache.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.TileCache.Test) : ... +- [DotRecast.Detour.Crowd.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.Crowd.Test) : ... +- [DotRecast.Detour.Dynamic.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.Dynamic.Test) : ... +- [DotRecast.Detour.Extras.Test](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.Extras.Test) : ... + +### 🧪 Testing With Command Prompt + +```shell + dotnet test --framework net8.0 -c Release +``` + +### 🧪 Testing With IDE + +- Refer to the manual for your IDE. + +## 🛠️ Integration + +There are a few ways to integrate [DotRecast.Recast](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast) and [DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour) into your project. +Source integration is the most popular and most flexible, and is what the project was designed for from the beginning. + +### 🛠️ Source Integration + +It is recommended to add the source directories +[DotRecast.Core](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Core), +[DotRecast.Recast](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast), +[DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour), +[DotRecast.Detour.Crowd](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Crowd), +[DotRecast.Detour.TileCache](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.TileCache) +and directly into your project depending on which parts of the project you need. + +For example your level building tool could include +[DotRecast.Core](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Core), +[DotRecast.Recast](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast), +[DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour) +and your game runtime could just include +[DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour) + +- [DotRecast.Core](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Core) : Core Utils +- [DotRecast.Recast](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast) : Core navmesh building system. +- [DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour) : Runtime navmesh interface and query system. +- [DotRecast.Detour.TileCache](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.TileCache) : Runtime movement, obstacle avoidance, and crowd simulation systems. +- [DotRecast.Detour.Crowd](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Crowd) : Runtime navmesh dynamic obstacle and re-baking system. +- [DotRecast.Detour.Dynamic](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Dynamic) : robust support for dynamic nav meshes combining pre-built voxels with dynamic objects which can be freely added and removed +- [DotRecast.Detour.Extras](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Extras) : simple tool to import navmeshes created with [A* Pathfinding Project](https://arongranberg.com/astar/) + + +### 🛠️ Installation through Nuget + +- Nuget link : [DotRecast.Core](https://www.nuget.org/packages/DotRecast.Core) +- Nuget link : [DotRecast.Recast](https://www.nuget.org/packages/DotRecast.Recast) +- Nuget link : [DotRecast.Detour](https://www.nuget.org/packages/DotRecast.Detour) +- Nuget link : [DotRecast.Detour.TileCache](https://www.nuget.org/packages/DotRecast.Detour.TileCache) +- Nuget link : [DotRecast.Detour.Crowd](https://www.nuget.org/packages/DotRecast.Detour.Crowd) +- Nuget link : [DotRecast.Detour.Dynamic](https://www.nuget.org/packages/DotRecast.Detour.Dynamic) +- Nuget link : [DotRecast.Detour.Extras](https://www.nuget.org/packages/DotRecast.Detour.Extras) +- Nuget link : [DotRecast.Recast.Toolset](https://www.nuget.org/packages/DotRecast.Recast.Toolset) +- Nuget link : [DotRecast.Recast.Demo](https://www.nuget.org/packages/DotRecast.Recast.Demo) + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e6c3a066 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,188 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [Unreleased] - yyyy-mm-dd + +### Added +- Added RcBinaryMinHeap ([@Sarofc](https://github.com/Sarofc)) +- Added DotRecast.Benchmark ([@Sarofc](https://github.com/Sarofc)) + +### Fixed +- Fix raycast shortcuts ([@Sarofc](https://github.com/Sarofc)) [#72](https://github.com/ikpil/DotRecast/issues/72) + +### Changed +- Changed data structure of 'neis' from List to byte[] for optimized memory usage and improved access speed in `DtLayerMonotoneRegion` +- Changed new RcVec3f[3] to stackalloc RcVec3f[3] in DtNavMesh.GetPolyHeight() to reduce heap allocation +- Changed memory handling to use stackalloc in DtNavMeshQuery.GetPolyWallSegments for reducing SOH +- Changed DtNavMeshQuery.GetPolyWallSegments() to use Span for enhanced performance, memory efficiency. + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.3.1] - 2024-07-09 + +### Added +- Nothing + +### Fixed +- Fixed bug where the dynamic voxel save file browser doesn't appear in `Recast.Demo` + +### Changed +- Changed to reuse samples and edges list in `BuildPolyDetail()` +- Changed `heights`, `areas`, `cons`, and `regs` arrays to byte arrays for uniformity and efficiency in `DtTileCacheLayer` +- Changed `reg`, `area` arrays to byte arrays for uniformity and efficiency in `DtTileCacheContour` +- Changed `RcChunkyTriMesh` to separate the function and variable. +- Changed to consolidate vector-related functions into one place. +- Changed stack handling from List to a fixed-size array with manual index management for optimization in `RcLayers.BuildHeightfieldLayers()` +- Changed to use Span and stackalloc for improved performance and memory management in `RcLayers.BuildHeightfieldLayers()` +- Changed vertCount and triCount to byte in `DtPolyDetail` +- Changed `new float[]` to `stackalloc float[]` in `DtConvexConvexIntersections.Intersect()` +- Changed agents management from list to dictionary in `DtCrowd` +- Changed to efficiently stack nearby DtCrowdAgents in `DtCrowd.GetNeighbours()` +- Changed to limit neighbor search to a maximum count and use array for memory efficiency in `DtCrowd.AddNeighbour()` + +### Removed +- Removed RcMeshDetails.VdistSq2(float[], float[]) +- Removed RcVecUtils.Dot() +- Removed RcVecUtils.Scale() +- Removed RcVecUtils.Subtract(RcVec3f i, float[] verts, int j) +- Removed RcVecUtils.Subtract(float[] verts, int i, int j) +- Removed RcVecUtils.Min(), RcVecUtils.Max() +- Removed RcVecUtils.Create(float[] values) +- Removed RcVecUtils.Dot2D(this RcVec3f @this, Span v, int vi) + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + +## [2024.2.3] - 2024-06-03 + +### Added +- Added `DtCollectPolysQuery` and `FindCollectPolyTest` + +### Fixed +- Nothing + +### Changed +- Changed `IDtPolyQuery` interface to make `Process()` more versatile +- Changed `PolyQueryInvoker` to `DtActionPolyQuery` +- Changed `DtTileCacheBuilder` to a static class +- Changed `DtTileCacheLayerHeaderReader` to a static class +- Changed `Dictionary>` to `DtMeshTile[]` to optimize memory usage +- Changed `MAX_STEER_POINTS` from class constant to local. +- Changed `List` to `Span` for enhanced memory efficiency +- Changed `DtWriter` to a static class and renamed it to `RcIO` +- Changed class `Trajectory` to interface `ITrajectory` + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.2.2] - 2024-05-18 + +### Added +- Added RcSpans UnitTest + +### Fixed +- Nothing + +### Changed +- Changed class name of static functions to RcRecast and DtDetour +- Changed DtLink class member variable type from int to byte +- Changed initialization in DtNavMesh constructor to Init() function. + +### Removed +- Nothing + +### Special Thanks +- [@Doprez](https://github.com/Doprez) + + +## [2024.2.1] - 2024-05-04 + +### Added +- Added RcCircularBuffer [@ikpil](https://github.com/ikpil) +- Added struct DtCrowdScopedTimer to avoid allocations in scoped timer calls. [@wrenge](https://github.com/wrenge) +- Added struct RcScopedTimer to avoid allocations in RcContext scoped timer [@ikpil](https://github.com/ikpil) +- Added RcSpans [@ikpil](https://github.com/ikpil) + +### Fixed +- SOH issue [#14](https://github.com/ikpil/DotRecast/issues/41) +- Optimization: reduce number of allocations on hot path. [@awgil](https://github.com/awgil) + +### Changed +- Changed DtPathCorridor.Init(int maxPath) function to allow setting the maximum path [@ikpil](https://github.com/ikpil) +- Changed from List to RcCyclicBuffer in DtCrowdTelemetry execution timing sampling [@wrenge](https://github.com/wrenge) +- RcCyclicBuffer optimizations [@wrenge](https://github.com/wrenge) + +### Removed + +### Special Thanks +- [@Doprez](https://github.com/Doprez) +- [@Arctium](https://github.com/Arctium) + + +## [2024.1.3] - 2024-02-13 + +### Added +- Added DtNodeQueue UnitTest [@ikpil](https://github.com/ikpil) +- Added RcSortedQueue UnitTest [@ikpil](https://github.com/ikpil) +- Added IComparable interface to RcAtomicLong [@ikpil](https://github.com/ikpil) +- Added Menu bar in Demo [@ikpil](https://github.com/ikpil) + +### Fixed + +### Changed +- Update Microsoft.NET.Test.Sdk 17.8.0 to 17.9.0 +- Enhanced ToString method of DtNode to provide more detailed information. +- Reuse DtNode in DtNodePool + +### Removed + +### Special Thanks +- [@Doprez](https://github.com/Doprez) +- [@Arctium](https://github.com/Arctium) + +## [2024.1.2] - 2024-02-04 + +### Added +- Added DtNodePool tests [@ikpil](https://github.com/ikpil) +- Added WangHash() for DtNodePool [@ikpil](https://github.com/ikpil) +- Added avg, min, max, sampling updated times in CrowdAgentProfilingTool [@ikpil](https://github.com/ikpil) + +### Fixed +- Fixed SOH issue in DtNavMeshQuery.Raycast [@ikpil](https://github.com/ikpil) +- Fixed SOH issue in DtProximityGrid.QueryItems [@ikpil](https://github.com/ikpil) + +### Changed +- Upgrade NUnit.Analyzers 4.0.1 + +### Removed + +### Special Thanks +- [@Doprez](https://github.com/Doprez) +- [@Arctium](https://github.com/Arctium) + +## [2024.1.1] - 2024-01-05 + +### Fixed +- Fix typo ([#25](https://github.com/ikpil/DotRecast/pull/25)) [@c0nd3v](https://github.com/c0nd3v) +- Fix updated struct version ([#23](https://github.com/ikpil/DotRecast/pull/23)) [@c0nd3v](https://github.com/c0nd3v) +- Allow Radius 0 in Demo ([#22](https://github.com/ikpil/DotRecast/pull/22)) [@c0nd3v](https://github.com/c0nd3v) + +### Changed +- [Upstream] Cleanup filter code and improved documentation ([#30](https://github.com/ikpil/DotRecast/pull/30)) [@ikpil](https://github.com/ikpil) +- [Upstream] Make detail mesh edge detection more robust ([#26](https://github.com/ikpil/DotRecast/pull/26)) [@ikpil](https://github.com/ikpil) +- [Upstream] 248275e - Fix: typo error (#153) ([#21](https://github.com/ikpil/DotRecast/pull/21)) [@ikpil](https://github.com/ikpil) +- Code cleanup and small optimizations in RecastFilter.cpp ([#29](https://github.com/ikpil/DotRecast/pull/29)) [@ikpil](https://github.com/ikpil) +- Added UI scaling feature based on monitor resolution in Demo ([#28](https://github.com/ikpil/DotRecast/pull/28)) [@ikpil](https://github.com/ikpil) + diff --git a/DotRecast.sln b/DotRecast.sln index 58e4b2c1..9cb52782 100644 --- a/DotRecast.sln +++ b/DotRecast.sln @@ -19,8 +19,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.Extras", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.TileCache", "src\DotRecast.Detour.TileCache\DotRecast.Detour.TileCache.csproj", "{DEB16B90-CCD4-497E-A2E9-4CC66FD7EF47}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Recast.Toolset", "src\DotRecast.Recast.Toolset\DotRecast.Recast.Toolset.csproj", "{DF987948-8C23-4337-AF83-D87D6407518D}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A7CB8D8B-70DA-4567-8316-0659FCAE1C73}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Core.Test", "test\DotRecast.Core.Test\DotRecast.Core.Test.csproj", "{10395C8F-DFBD-4263-8A20-EA3500A6E55A}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Recast.Test", "test\DotRecast.Recast.Test\DotRecast.Recast.Test.csproj", "{88754FE2-A05A-4D4D-A81A-90418AD32362}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.Test", "test\DotRecast.Detour.Test\DotRecast.Detour.Test.csproj", "{554CB5BD-D58A-4856-BFE1-666A62C9BEA3}" @@ -33,11 +37,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.Extras.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Detour.TileCache.Test", "test\DotRecast.Detour.TileCache.Test\DotRecast.Detour.TileCache.Test.csproj", "{3CAA7306-088E-4373-A406-99755CC2B605}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Recast.Demo", "src\DotRecast.Recast.Demo\DotRecast.Recast.Demo.csproj", "{023E1E6A-4895-4573-89AE-3D5D8E0B39C8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{65754308-3C9B-4544-9D7B-F2C16A4E2486}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Recast.DemoTool", "src\DotRecast.Recast.DemoTool\DotRecast.Recast.DemoTool.csproj", "{DF987948-8C23-4337-AF83-D87D6407518D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotRecast.Benchmark", "test\DotRecast.Benchmark\DotRecast.Benchmark.csproj", "{D1EFC625-D095-4208-98A2-112B73CB40B0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -100,14 +100,18 @@ Global {3CAA7306-088E-4373-A406-99755CC2B605}.Debug|Any CPU.Build.0 = Debug|Any CPU {3CAA7306-088E-4373-A406-99755CC2B605}.Release|Any CPU.ActiveCfg = Release|Any CPU {3CAA7306-088E-4373-A406-99755CC2B605}.Release|Any CPU.Build.0 = Release|Any CPU - {023E1E6A-4895-4573-89AE-3D5D8E0B39C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023E1E6A-4895-4573-89AE-3D5D8E0B39C8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023E1E6A-4895-4573-89AE-3D5D8E0B39C8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023E1E6A-4895-4573-89AE-3D5D8E0B39C8}.Release|Any CPU.Build.0 = Release|Any CPU {DF987948-8C23-4337-AF83-D87D6407518D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DF987948-8C23-4337-AF83-D87D6407518D}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF987948-8C23-4337-AF83-D87D6407518D}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF987948-8C23-4337-AF83-D87D6407518D}.Release|Any CPU.Build.0 = Release|Any CPU + {10395C8F-DFBD-4263-8A20-EA3500A6E55A}.Debug|Any CPU.ActiveCfg = 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.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 EndGlobalSection GlobalSection(NestedProjects) = preSolution {FFE40BBF-843B-41FA-8504-F4ABD166762E} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} @@ -123,7 +127,8 @@ Global {7BAA69B2-EDC7-4603-B16F-BC7B24353F81} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} {DEB16B90-CCD4-497E-A2E9-4CC66FD7EF47} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {3CAA7306-088E-4373-A406-99755CC2B605} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} - {023E1E6A-4895-4573-89AE-3D5D8E0B39C8} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} {DF987948-8C23-4337-AF83-D87D6407518D} = {8ED75CF7-A3D6-423D-8499-9316DD413DAD} + {10395C8F-DFBD-4263-8A20-EA3500A6E55A} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} + {D1EFC625-D095-4208-98A2-112B73CB40B0} = {A7CB8D8B-70DA-4567-8316-0659FCAE1C73} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 0ca14263..8cc246e6 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,88 @@ -[![License: Zlib](https://img.shields.io/badge/License-Zlib-lightgrey.svg)](https://opensource.org/licenses/Zlib) -[![.NET](https://github.com/ikpil/DotRecast/actions/workflows/dotnet.yml/badge.svg)](https://github.com/ikpil/DotRecast/actions/workflows/dotnet.yml) +# DotRecast -# Introduction -1. DotRecast is a port of C++'s [recastnavigation](https://github.com/recastnavigation/recastnavigation) and Java's [recast4j](https://github.com/ppiastucki/recast4j) to the C# language. -2. For game development, C# servers, C# project, and Unity3D are supported. -3. DotRecast consists of Recast and Detour, Crowd, Dynamic, Extras, TileCache, DemoTool, Demo +*DotRecast is C# Recast & Detour, a port of [recastnavigation](https://github.com/recastnavigation/recastnavigation) and [recast4j](https://github.com/ppiastucki/recast4j) to the C# language.* +*If you'd like to support the project, we'd appreciate starring(⭐) our repos on Github for more visibility.* -![DotRecast of a navmesh baked with the sample program](/resources/screenshot.png?raw=true) +--- -## DotRecast.Recast +![GitHub License](https://img.shields.io/github/license/ikpil/DotRecast?style=for-the-badge) +![Languages](https://img.shields.io/github/languages/top/ikpil/DotRecast?style=for-the-badge) +![GitHub repo size](https://img.shields.io/github/repo-size/ikpil/DotRecast?style=for-the-badge) +[![GitHub Repo stars](https://img.shields.io/github/stars/ikpil/DotRecast?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/dotnet.yml?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast/actions/workflows/dotnet.yml) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ikpil/DotRecast/codeql.yml?style=for-the-badge&logo=github&label=CODEQL)](https://github.com/ikpil/DotRecast/actions/workflows/codeql.yml) +[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ikpil/DotRecast?style=for-the-badge&logo=github)](https://github.com/ikpil/DotRecast/commits) +[![GitHub issues](https://img.shields.io/github/issues-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=44cc11)](https://github.com/ikpil/DotRecast/issues) +[![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/ikpil/DotRecast?style=for-the-badge&logo=github&color=a371f7)](https://github.com/ikpil/DotRecast/issues) +[![NuGet Version](https://img.shields.io/nuget/vpre/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core) +[![NuGet Downloads](https://img.shields.io/nuget/dt/DotRecast.Core?style=for-the-badge&logo=nuget)](https://www.nuget.org/packages/DotRecast.Core) +[![GitHub Sponsors](https://img.shields.io/github/sponsors/ikpil?style=for-the-badge&logo=GitHub-Sponsors&link=https%3A%2F%2Fgithub.com%2Fsponsors%2Fikpil)](https://github.com/sponsors/ikpil) -Recast is state of the art navigation mesh construction toolset for games. +--- -Recast is... -* 🤖 **Automatic** - throw any level geometry at it and you will get a robust navmesh out -* 🏎️ **Fast** - swift turnaround times for level designers -* 🧘 **Flexible** - easily customize the navmesh generation and runtime navigation systems to suit your specific game's needs. +[![demo](https://user-images.githubusercontent.com/313821/266750582-8cf67832-1206-4b58-8c1f-7205210cbf22.gif)](https://youtu.be/zIFIgziKLhQ) -Recast constructs a navmesh through a multi-step rasterization process: -1. First Recast voxelizes the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. -2. Voxels in areas where the character would not be able to move are removed by applying simple voxel data filters. -3. The walkable areas described by the voxel grid are then divided into sets of 2D polygonal regions. -4. The navigation polygons are generated by triangulating and stiching together the generated 2d plygonal regions. -## DotRecast.Detour +## 🚀 Features + +- 🤖 Automatic - Recast can generate a navmesh from any level geometry you throw at it +- 🏎️ Fast - swift turnaround times for level designers +- 🧘 Flexible - detailed customization options and modular design let you tailor functionality to your specific needs +- 🚫 Dependency-Free - building Recast & Detour only requires a .NET compiler +- 💪 Industry Standard - Recast powers AI navigation features in Unity, Unreal, Godot, O3DE and countless AAA and indie games and engines -Recast is accompanied by Detour, a path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly. +Recast Navigation is divided into multiple modules, each contained in its own folder: -Detour offers a simple static navmesh data representation which is suitable for many simple cases. It also provides a tiled navigation mesh representation, which allows you to stream of navigation data in and out as the player progresses through the world and regenerate sections of the navmesh data as the world changes. +- [DotRecast.Core](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Core) : Core utils +- [DotRecast.Recast](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast) : Navmesh generation +- [DotRecast.Detour](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour) : Runtime loading of navmesh data, pathfinding, navmesh queries +- [DotRecast.Detour.TileCache](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.TileCache) : Navmesh streaming. Useful for large levels and open-world games +- [DotRecast.Detour.Crowd](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Crowd) : Agent movement, collision avoidance, and crowd simulation +- [DotRecast.Detour.Dynamic](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Dynamic) : robust support for dynamic nav meshes combining pre-built voxels with dynamic objects which can be freely added and removed +- [DotRecast.Detour.Extras](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Detour.Extras) : simple tool to import navmeshes created with [A* Pathfinding Project](https://arongranberg.com/astar/) +- [DotRecast.Recast.Toolset](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast.Toolset) : all modules +- [DotRecast.Recast.Demo](https://github.com/ikpil/DotRecast/tree/main/src/DotRecast.Recast.Demo) : Standalone, comprehensive demo app showcasing all aspects of Recast & Detour's functionality +- [Tests](https://github.com/ikpil/DotRecast/tree/main/test) : Unit tests -## DotRecast.Recast.Demo +## ⚡ Getting Started + +- To build or integrate into your own project, please check out [BuildingAndIntegrating.md](https://github.com/ikpil/DotRecast/tree/main/BuildingAndIntegrating.md) +- To create a NavMesh, please check out [RecastSoloMeshTest.cs](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Recast.Test/RecastSoloMeshTest.cs) +- To test pathfinding, please check out [FindPathTest.cs](https://github.com/ikpil/DotRecast/tree/main/test/DotRecast.Detour.Test/FindPathTest.cs) +- To watch the demo play video, please check out [Demo Video](#-demo-video) -You can find a comprehensive demo project in the `DotRecast.Recast.Demo` folder. It's a kitchen sink demo showcasing all the functionality of the library. If you are new to Recast & Detour, check out [SoloNavMeshBuilder.cs](/src/DotRecast.Recast.Demo/Builder/SoloNavMeshBuilder.cs) to get started with building navmeshes and [TestNavmeshTool.cs](/src/DotRecast.Recast.Demo/Tools/TestNavmeshTool.cs) to see how Detour can be used to find paths. +## ⚙ How it Works -### Building DotRecast.Recast.Demo +Recast constructs a navmesh through a multi-step mesh rasterization process. -`DotRecast.Recast.Demo` uses [dotnet 7](https://dotnet.microsoft.com/) to build platform specific projects. Download it and make sure it's available on your path, or specify the path to it. +1. First Recast rasterizes the input triangle meshes into voxels. +2. Voxels in areas where agents would not be able to move are filtered and removed. +3. The walkable areas described by the voxel grid are then divided into sets of polygonal regions. +4. The navigation polygons are generated by re-triangulating the generated polygonal regions into a navmesh. -#### Linux & macOS & Windows +You can use Recast to build a single navmesh, or a tiled navmesh. +Single meshes are suitable for many simple, static cases and are easy to work with. +Tiled navmeshes are more complex to work with but better support larger, more dynamic environments. Tiled meshes enable advance Detour features like re-baking, heirarchical path-planning, and navmesh data-streaming. -- Navigate to the `DotRecast.Recast.Demo` folder and run `dotnet run` +## 📚 Documentation & Links -### Running Unit tests +- DotRecast Links + - [DotRecast/issues](https://github.com/ikpil/DotRecast/issues) + +- Official Links + - [recastnavigation/discussions](https://github.com/recastnavigation/recastnavigation/discussions) + - [recastnav.com](https://recastnav.com) -## Integrating with your game or engine +## 🅾 License -It is recommended to add the source directories `DotRecast.Core`, `DotRecast.Detour.Crowd`, `DotRecast.Detour.Dynamic`, `DotRecast.Detour.TitleCache`, `DotRecast.Detour.Extras` and `DotRecast.Recast` into your own project depending on which parts of the project you need. For example your level building tool could include `DotRecast.Core`, `DotRecast.Recast`, and `DotRecast.Detour`, and your game runtime could just include `DotRecast.Detour`. +DotRecast is licensed under ZLib license, see [LICENSE.txt](https://github.com/ikpil/DotRecast/tree/main/LICENSE.txt) for more information. -## Discuss +## 📹 Demo Video -- Discuss Recast & Detour: http://groups.google.com/group/recastnavigation -- Development blog: http://digestingduck.blogspot.com/ +[![demo](https://img.youtube.com/vi/zIFIgziKLhQ/0.jpg)](https://youtu.be/zIFIgziKLhQ) -## License +[![demo](https://img.youtube.com/vi/CPvc19gNUEk/0.jpg)](https://youtu.be/CPvc19gNUEk) + +[![demo](https://img.youtube.com/vi/pe5jpGUNPRg/0.jpg)](https://youtu.be/pe5jpGUNPRg) -Recast & Detour is licensed under ZLib license, see `LICENSE.txt` for more information. diff --git a/resources/annotation_test.obj b/resources/annotation_test.obj index a7aaeb4d..7cfcf584 100644 --- a/resources/annotation_test.obj +++ b/resources/annotation_test.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16b8b493dd21a002ef6a8a2b81648c26c9ecc05cc4fbe745277ee234e950bd2b -size 118600 +oid sha256:f6025e893c23a6449f3c718192fbea882d618dc7fd8c518738b84b4e47e71bea +size 115038 diff --git a/resources/bridge.obj b/resources/bridge.obj index 35ee8139..38e92eeb 100644 --- a/resources/bridge.obj +++ b/resources/bridge.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86149d82b7ba7f99c5c23ded7b5db249906bb2d2006a1175393f4a05443745dc -size 1587 +oid sha256:2beefbb9887349752453a6e673761fee1dcacb8aac1732fe8995e5a355f42207 +size 1500 diff --git a/resources/convex.obj b/resources/convex.obj index ca282ef8..f35aa499 100644 --- a/resources/convex.obj +++ b/resources/convex.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:941dcb1de28af0995f68ba29c8c4ec22ef4905a226bead8d6079a7c99deb58da -size 7982 +oid sha256:0d0cb98e95950c3c9f077968c922e6fbc7b441aec123e17ad118f194941f04c4 +size 7574 diff --git a/resources/dungeon.obj b/resources/dungeon.obj index f1491aee..1907c3b0 100644 --- a/resources/dungeon.obj +++ b/resources/dungeon.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b157db01f8eaa4b0290d18bd8d6515e969f674213de6f3a3e014f82ae1facc4 -size 390446 +oid sha256:096058ad5764e1d60304454147b4bd8939eb6a5960527ab1be00ba7ef4925f8f +size 375212 diff --git a/resources/house.obj b/resources/house.obj index 4c00c314..deba585a 100644 --- a/resources/house.obj +++ b/resources/house.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8614704e80fc08bbe37444dc2f27ad073e31bd48bbdac718997ba8c208969d85 -size 163383 +oid sha256:618477bc74edc90a97513e427dbc1e300d3b8a53df2cdc46b83a7c02ab47176e +size 156062 diff --git a/resources/nav_test.obj b/resources/nav_test.obj index 03d3c9c7..f0afcf97 100644 --- a/resources/nav_test.obj +++ b/resources/nav_test.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd0d97d19d1ae891e99430b55982f49fe5f46ee05f2266d9c8d64b3baecf2808 -size 118565 +oid sha256:ed27fc1a85ef73faa4d0621d640b67842e9081606e732267c662327c35fabc0f +size 115059 diff --git a/resources/screenshot.png b/resources/screenshot.png deleted file mode 100644 index d2b35f7c..00000000 --- a/resources/screenshot.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f7498de4ed2f8bf209b7ff56e20685ddcdf493c0710c7aa584effb63ecd0533 -size 492615 diff --git a/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs b/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs new file mode 100644 index 00000000..343e821b --- /dev/null +++ b/src/DotRecast.Core/Buffers/RcCyclicBuffer.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net.Security; + +namespace DotRecast.Core.Buffers +{ + // https://github.com/joaoportela/CircularBuffer-CSharp/blob/master/CircularBuffer/CircularBuffer.cs + public class RcCyclicBuffer : IEnumerable + { + public struct Enumerator : IEnumerator + { + private readonly RcCyclicBuffer _cb; + private int _index; + private readonly int _size; + + internal Enumerator(RcCyclicBuffer cb) + { + _cb = cb; + _size = _cb._size; + _index = default; + Reset(); + } + + public bool MoveNext() + { + return ++_index < _size; + } + + public void Reset() + { + _index = -1; + } + + public T Current => _cb[_index]; + + object IEnumerator.Current => Current; + + public void Dispose() + { + // This could be used to unlock write access to collection + } + } + + private readonly T[] _buffer; + + private int _start; + private int _end; + private int _size; + + public RcCyclicBuffer(int capacity) + : this(capacity, new T[] { }) + { + } + + public RcCyclicBuffer(int capacity, T[] items) + { + if (capacity < 1) + { + throw new ArgumentException("RcCyclicBuffer cannot have negative or zero capacity.", nameof(capacity)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + + if (items.Length > capacity) + { + throw new ArgumentException("Too many items to fit RcCyclicBuffer", nameof(items)); + } + + _buffer = new T[capacity]; + + Array.Copy(items, _buffer, items.Length); + _size = items.Length; + + _start = 0; + _end = _size == capacity ? 0 : _size; + } + + public int Capacity => _buffer.Length; + public bool IsFull => Size == Capacity; + public bool IsEmpty => Size == 0; + + public int Size => _size; + + public T Front() + { + ThrowIfEmpty(); + return _buffer[_start]; + } + + public T Back() + { + ThrowIfEmpty(); + return _buffer[(_end != 0 ? _end : Capacity) - 1]; + } + + public T this[int index] + { + get + { + if (IsEmpty) + { + throw new IndexOutOfRangeException($"Cannot access index {index}. Buffer is empty"); + } + + if (index >= _size) + { + throw new IndexOutOfRangeException($"Cannot access index {index}. Buffer size is {_size}"); + } + + int actualIndex = InternalIndex(index); + return _buffer[actualIndex]; + } + set + { + if (IsEmpty) + { + throw new IndexOutOfRangeException($"Cannot access index {index}. Buffer is empty"); + } + + if (index >= _size) + { + throw new IndexOutOfRangeException($"Cannot access index {index}. Buffer size is {_size}"); + } + + int actualIndex = InternalIndex(index); + _buffer[actualIndex] = value; + } + } + + public void PushBack(T item) + { + if (IsFull) + { + _buffer[_end] = item; + Increment(ref _end); + _start = _end; + } + else + { + _buffer[_end] = item; + Increment(ref _end); + ++_size; + } + } + + public void PushFront(T item) + { + if (IsFull) + { + Decrement(ref _start); + _end = _start; + _buffer[_start] = item; + } + else + { + Decrement(ref _start); + _buffer[_start] = item; + ++_size; + } + } + + public void PopBack() + { + ThrowIfEmpty("Cannot take elements from an empty buffer."); + Decrement(ref _end); + _buffer[_end] = default(T); + --_size; + } + + public void PopFront() + { + ThrowIfEmpty("Cannot take elements from an empty buffer."); + _buffer[_start] = default(T); + Increment(ref _start); + --_size; + } + + public void Clear() + { + // to clear we just reset everything. + _start = 0; + _end = 0; + _size = 0; + Array.Clear(_buffer, 0, _buffer.Length); + } + + public T[] ToArray() + { + T[] newArray = new T[Size]; + CopyTo(newArray); + return newArray; + } + + public void CopyTo(Span destination) + { + var span1 = ArrayOne(); + span1.CopyTo(destination); + ArrayTwo().CopyTo(destination[span1.Length..]); + } + + private void ThrowIfEmpty(string message = "Cannot access an empty buffer.") + { + if (IsEmpty) + { + throw new InvalidOperationException(message); + } + } + + private void Increment(ref int index) + { + if (++index == Capacity) + { + index = 0; + } + } + + private void Decrement(ref int index) + { + if (index == 0) + { + index = Capacity; + } + + index--; + } + + private int InternalIndex(int index) + { + return _start + (index < (Capacity - _start) + ? index + : index - Capacity); + } + + internal Span ArrayOne() + { + if (IsEmpty) + { + return new Span(Array.Empty()); + } + + if (_start < _end) + { + return new Span(_buffer, _start, _end - _start); + } + + return new Span(_buffer, _start, _buffer.Length - _start); + } + + internal Span ArrayTwo() + { + if (IsEmpty) + { + return new Span(Array.Empty()); + } + + if (_start < _end) + { + return new Span(_buffer, _end, 0); + } + + return new Span(_buffer, 0, _end); + } + + public Enumerator GetEnumerator() => new Enumerator(this); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs b/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs new file mode 100644 index 00000000..fc942854 --- /dev/null +++ b/src/DotRecast.Core/Buffers/RcCyclicBuffers.cs @@ -0,0 +1,119 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace DotRecast.Core.Buffers +{ + public static class RcCyclicBuffers + { + public static long Sum(this ReadOnlySpan source) + { + var buffer = source; + var result = 0L; + if (Vector.IsHardwareAccelerated) + { + var vectors = MemoryMarshal.Cast>(buffer); + var vecSum = Vector.Zero; + foreach (var vec in vectors) + vecSum += vec; + + result = Vector.Dot(vecSum, Vector.One); + var remainder = source.Length % Vector.Count; + buffer = buffer[^remainder..]; + } + + foreach (var val in buffer) + result += val; + + return result; + } + + public static double Average(this ReadOnlySpan source) + { + if (0 >= source.Length) + return 0; + + return source.Sum() / (double)source.Length; + } + + private static long Min(this ReadOnlySpan source) + { + var buffer = source; + var result = long.MaxValue; + + if (Vector.IsHardwareAccelerated) + { + var vectors = MemoryMarshal.Cast>(buffer); + var vecMin = Vector.One * result; + + foreach (var vec in vectors) + vecMin = Vector.Min(vecMin, vec); + + for (int i = 0; i < Vector.Count; i++) + result = Math.Min(result, vecMin[i]); + + var remainder = source.Length % Vector.Count; + buffer = buffer[^remainder..]; + } + + foreach (var val in buffer) + result = Math.Min(result, val); + + return result; + } + + private static long Max(this ReadOnlySpan source) + { + var buffer = source; + var result = long.MinValue; + + if (Vector.IsHardwareAccelerated) + { + var vectors = MemoryMarshal.Cast>(buffer); + var vecMax = Vector.One * result; + + foreach (var vec in vectors) + vecMax = Vector.Max(vecMax, vec); + + for (int i = 0; i < Vector.Count; i++) + result = Math.Max(result, vecMax[i]); + + var remainder = source.Length % Vector.Count; + buffer = buffer[^remainder..]; + } + + foreach (var val in buffer) + result = Math.Max(result, val); + + return result; + } + + public static long Sum(this RcCyclicBuffer source) + { + return Sum(source.ArrayOne()) + Sum(source.ArrayTwo()); + } + + public static double Average(this RcCyclicBuffer source) + { + return Sum(source) / (double)source.Size; + } + + public static long Min(this RcCyclicBuffer source) + { + var firstHalf = source.ArrayOne(); + var secondHalf = source.ArrayTwo(); + var a = firstHalf.Length > 0 ? Min(firstHalf) : long.MaxValue; + var b = secondHalf.Length > 0 ? Min(secondHalf) : long.MaxValue; + return Math.Min(a, b); + } + + public static long Max(this RcCyclicBuffer source) + { + var firstHalf = source.ArrayOne(); + var secondHalf = source.ArrayTwo(); + var a = firstHalf.Length > 0 ? Max(firstHalf) : long.MinValue; + var b = secondHalf.Length > 0 ? Max(secondHalf) : long.MinValue; + return Math.Max(a, b); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Buffers/RcRentedArray.cs b/src/DotRecast.Core/Buffers/RcRentedArray.cs new file mode 100644 index 00000000..85800c57 --- /dev/null +++ b/src/DotRecast.Core/Buffers/RcRentedArray.cs @@ -0,0 +1,57 @@ +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Buffers +{ + public static class RcRentedArray + { + public static RcRentedArray Rent(int minimumLength) + { + var array = ArrayPool.Shared.Rent(minimumLength); + return new RcRentedArray(ArrayPool.Shared, array, minimumLength); + } + } + + public class RcRentedArray : IDisposable + { + private ArrayPool _owner; + private T[] _array; + + public int Length { get; } + public bool IsDisposed => null == _owner || null == _array; + + internal RcRentedArray(ArrayPool owner, T[] array, int length) + { + _owner = owner; + _array = array; + Length = length; + } + + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + return ref _array[index]; + } + } + + public T[] AsArray() + { + return _array; + } + + + public void Dispose() + { + if (null != _owner && null != _array) + { + _owner.Return(_array, true); + _owner = null; + _array = null; + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/CollectionExtensions.cs b/src/DotRecast.Core/CollectionExtensions.cs deleted file mode 100644 index b472f7ef..00000000 --- a/src/DotRecast.Core/CollectionExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace DotRecast.Core -{ - public static class CollectionExtensions - { - public static void ForEach(this IEnumerable collection, Action action) - { - foreach (var item in collection) - { - action.Invoke(item); - } - } - - public static void Shuffle(this IList list) - { - Random random = new Random(); - int n = list.Count; - while (n > 1) - { - n--; - int k = random.Next(n + 1); - T value = list[k]; - list[k] = list[n]; - list[n] = value; - } - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/CollectionExtensions.cs b/src/DotRecast.Core/Collections/CollectionExtensions.cs new file mode 100644 index 00000000..24d01fc9 --- /dev/null +++ b/src/DotRecast.Core/Collections/CollectionExtensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; + +namespace DotRecast.Core.Collections +{ + public static class CollectionExtensions + { + /// Sorts the given data in-place using insertion sort. + /// + /// @param data The data to sort + /// @param dataLength The number of elements in @p data + public static void InsertSort(this int[] data) + { + for (int valueIndex = 1; valueIndex < data.Length; valueIndex++) + { + int value = data[valueIndex]; + int insertionIndex; + for (insertionIndex = valueIndex - 1; insertionIndex >= 0 && data[insertionIndex] > value; insertionIndex--) + { + // Shift over values + data[insertionIndex + 1] = data[insertionIndex]; + } + + // Insert the value in sorted order. + data[insertionIndex + 1] = value; + } + } + + public static void ForEach(this IEnumerable collection, Action action) + { + foreach (var item in collection) + { + action.Invoke(item); + } + } + + public static void Shuffle(this IList list) + { + Random random = new Random(); + int n = list.Count; + while (n > 1) + { + n--; + int k = random.Next(n + 1); + (list[k], list[n]) = (list[n], list[k]); + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs b/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs new file mode 100644 index 00000000..900fd54d --- /dev/null +++ b/src/DotRecast.Core/Collections/RcBinaryMinHeap.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public sealed class RcBinaryMinHeap + { + private readonly List _items; + private readonly Comparison _comparision; + + public int Count => _items.Count; + public int Capacity => _items.Capacity; + + public RcBinaryMinHeap(Comparison comparision) + { + _items = new List(); + _comparision = comparision; + } + + public RcBinaryMinHeap(int capacity, Comparison comparison) : this(comparison) + { + if (capacity <= 0) + throw new ArgumentException("capacity must greater than zero"); + + _items = new List(capacity); + _comparision = comparison; + } + + public void Push(T val) + { + _items.Add(val); + SiftUp(_items.Count - 1); + } + + public T Pop() + { + var min = Peek(); + RemoveMin(); + return min; + } + + private void RemoveMin() + { + if (_items.Count == 0) + { + Throw(); + static void Throw() => throw new InvalidOperationException("no element to pop"); + } + + int last = _items.Count - 1; + Swap(0, last); + _items.RemoveAt(last); + + MinHeapify(0, last - 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Top() + { + return _items[0]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Peek() + { + if (IsEmpty()) + { + throw new Exception("Heap is empty."); + } + + return _items[0]; + } + + + public bool Modify(T node) + { + for (int i = 0; i < _items.Count; i++) + { + if (_items[i].Equals(node)) + { + SiftUp(i); + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + _items.Clear(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsEmpty() + { + return 0 == _items.Count; + } + + private void SiftUp(int nodeIndex) + { + int parent = (nodeIndex - 1) / 2; + while (_comparision.Invoke(_items[nodeIndex], _items[parent]) < 0) + { + Swap(parent, nodeIndex); + nodeIndex = parent; + parent = (nodeIndex - 1) / 2; + } + } + + + private void MinHeapify(int nodeIndex, int lastIndex) + { + int left = (nodeIndex * 2) + 1; + int right = left + 1; + int smallest = nodeIndex; + + if (left <= lastIndex && _comparision.Invoke(_items[left], _items[nodeIndex]) < 0) + smallest = left; + + if (right <= lastIndex && _comparision.Invoke(_items[right], _items[smallest]) < 0) + smallest = right; + + if (smallest == nodeIndex) + return; + + Swap(nodeIndex, smallest); + MinHeapify(smallest, lastIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Swap(int x, int y) + { + if (x == y) + return; + + (_items[y], _items[x]) = (_items[x], _items[y]); + } + + + public T[] ToArray() + { + return _items.ToArray(); + } + + public List ToList() + { + return new List(_items); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs new file mode 100644 index 00000000..c9ba1867 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Enumerable.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace DotRecast.Core.Collections +{ + public readonly partial struct RcImmutableArray + { + public IEnumerator GetEnumerator() + { + return EnumeratorObject.Create(_array); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return EnumeratorObject.Create(_array); + } + + private sealed class EnumeratorObject : IEnumerator + { + private static readonly IEnumerator EmptyEnumerator = new EnumeratorObject(Empty._array!); + private readonly T[] _array; + private int _index; + + internal static IEnumerator Create(T[] array) + { + if (array.Length != 0) + { + return new EnumeratorObject(array); + } + else + { + return EmptyEnumerator; + } + } + + + private EnumeratorObject(T[] array) + { + _index = -1; + _array = array; + } + + public T Current + { + get + { + if (unchecked((uint)_index) < (uint)_array.Length) + { + return _array[_index]; + } + + throw new InvalidOperationException(); + } + } + + object IEnumerator.Current => this.Current; + + public void Dispose() + { + } + + public bool MoveNext() + { + int newIndex = _index + 1; + int length = _array.Length; + + if ((uint)newIndex <= (uint)length) + { + _index = newIndex; + return (uint)newIndex < (uint)length; + } + + return false; + } + + void IEnumerator.Reset() + { + _index = -1; + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs new file mode 100644 index 00000000..253a5e18 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Listable.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; + +namespace DotRecast.Core.Collections +{ + public readonly partial struct RcImmutableArray : IList + { + public int Count => Length; + public bool IsReadOnly => true; + + T IList.this[int index] + { + get + { + var self = this; + return self[index]; + } + set => throw new NotSupportedException(); + } + + + public int IndexOf(T item) + { + for (int i = 0; i < Count; ++i) + { + if (_array![i].Equals(item)) + return i; + } + + return -1; + } + + public bool Contains(T item) + { + return IndexOf(item) >= 0; + } + + public void CopyTo(T[] array, int arrayIndex) + { + var self = this; + RcArrays.Copy(self._array!, 0, array, arrayIndex, self.Length); + } + + public void Add(T item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Remove(T item) + { + throw new NotSupportedException(); + } + + public void Insert(int index, T item) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs b/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs new file mode 100644 index 00000000..335f6c13 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcImmutableArray.Minimal.cs @@ -0,0 +1,19 @@ +namespace DotRecast.Core.Collections +{ + public readonly partial struct RcImmutableArray + { +#pragma warning disable CA1825 + public static readonly RcImmutableArray Empty = new RcImmutableArray(new T[0]); +#pragma warning restore CA1825 + + private readonly T[] _array; + + internal RcImmutableArray(T[] items) + { + _array = items; + } + + public T this[int index] => _array![index]; + public int Length => _array!.Length; + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcImmutableArray.cs b/src/DotRecast.Core/Collections/RcImmutableArray.cs new file mode 100644 index 00000000..65fa86c9 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcImmutableArray.cs @@ -0,0 +1,48 @@ +using System; + +namespace DotRecast.Core.Collections +{ + public static class RcImmutableArray + { + public static RcImmutableArray Create() + { + return RcImmutableArray.Empty; + } + + public static RcImmutableArray Create(T item1) + { + T[] array = new[] { item1 }; + return new RcImmutableArray(array); + } + + public static RcImmutableArray Create(T item1, T item2) + { + T[] array = new[] { item1, item2 }; + return new RcImmutableArray(array); + } + + public static RcImmutableArray Create(T item1, T item2, T item3) + { + T[] array = new[] { item1, item2, item3 }; + return new RcImmutableArray(array); + } + + public static RcImmutableArray Create(T item1, T item2, T item3, T item4) + { + T[] array = new[] { item1, item2, item3, item4 }; + return new RcImmutableArray(array); + } + + public static RcImmutableArray Create(params T[] items) + { + if (items == null || items.Length == 0) + { + return RcImmutableArray.Empty; + } + + var tmp = new T[items.Length]; + RcArrays.Copy(items, tmp, items.Length); + return new RcImmutableArray(tmp); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcSortedQueue.cs b/src/DotRecast.Core/Collections/RcSortedQueue.cs similarity index 64% rename from src/DotRecast.Core/RcSortedQueue.cs rename to src/DotRecast.Core/Collections/RcSortedQueue.cs index 03c99604..0c06aad9 100644 --- a/src/DotRecast.Core/RcSortedQueue.cs +++ b/src/DotRecast.Core/Collections/RcSortedQueue.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -21,18 +21,18 @@ 3. This notice may not be removed or altered from any source distribution. using System; using System.Collections.Generic; -namespace DotRecast.Core +namespace DotRecast.Core.Collections { public class RcSortedQueue { private bool _dirty; private readonly List _items; - private readonly Comparison _comparison; + private readonly Comparer _comparer; - public RcSortedQueue(Comparison comparison) + public RcSortedQueue(Comparison comp) { _items = new List(); - _comparison = (x, y) => comparison.Invoke(x, y) * -1; // reverse + _comparer = Comparer.Create((x, y) => comp.Invoke(x, y) * -1); } public int Count() @@ -40,47 +40,69 @@ public int Count() return _items.Count; } + public bool IsEmpty() + { + return 0 == _items.Count; + } + public void Clear() { _items.Clear(); + _dirty = false; } - public T Top() + private void Balance() { if (_dirty) { - _items.Sort(_comparison); // reverse + _items.Sort(_comparer); // reverse _dirty = false; } + } - return _items[_items.Count - 1]; + public T Peek() + { + Balance(); + return _items[^1]; } public T Dequeue() { - var node = Top(); - _items.Remove(node); + var node = Peek(); + _items.RemoveAt(_items.Count - 1); return node; } public void Enqueue(T item) { + if (null == item) + return; + _items.Add(item); _dirty = true; } - public void Remove(T item) + public bool Remove(T item) { + if (null == item) + return false; + + //int idx = _items.BinarySearch(item, _comparer); // don't use this! Because reference types can be reused externally. int idx = _items.FindLastIndex(x => item.Equals(x)); if (0 > idx) - return; + return false; _items.RemoveAt(idx); + return true; } - public bool IsEmpty() + + public List ToList() { - return 0 == _items.Count; + Balance(); + var temp = new List(_items); + temp.Reverse(); + return temp; } } } \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray128.cs b/src/DotRecast.Core/Collections/RcStackArray128.cs new file mode 100644 index 00000000..d80afad0 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray128.cs @@ -0,0 +1,421 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray128 + { + public static RcStackArray128 Empty => new RcStackArray128(); + + private const int Size = 128; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + public T V16; + public T V17; + public T V18; + public T V19; + public T V20; + public T V21; + public T V22; + public T V23; + public T V24; + public T V25; + public T V26; + public T V27; + public T V28; + public T V29; + public T V30; + public T V31; + public T V32; + public T V33; + public T V34; + public T V35; + public T V36; + public T V37; + public T V38; + public T V39; + public T V40; + public T V41; + public T V42; + public T V43; + public T V44; + public T V45; + public T V46; + public T V47; + public T V48; + public T V49; + public T V50; + public T V51; + public T V52; + public T V53; + public T V54; + public T V55; + public T V56; + public T V57; + public T V58; + public T V59; + public T V60; + public T V61; + public T V62; + public T V63; + public T V64; + public T V65; + public T V66; + public T V67; + public T V68; + public T V69; + public T V70; + public T V71; + public T V72; + public T V73; + public T V74; + public T V75; + public T V76; + public T V77; + public T V78; + public T V79; + public T V80; + public T V81; + public T V82; + public T V83; + public T V84; + public T V85; + public T V86; + public T V87; + public T V88; + public T V89; + public T V90; + public T V91; + public T V92; + public T V93; + public T V94; + public T V95; + public T V96; + public T V97; + public T V98; + public T V99; + public T V100; + public T V101; + public T V102; + public T V103; + public T V104; + public T V105; + public T V106; + public T V107; + public T V108; + public T V109; + public T V110; + public T V111; + public T V112; + public T V113; + public T V114; + public T V115; + public T V116; + public T V117; + public T V118; + public T V119; + public T V120; + public T V121; + public T V122; + public T V123; + public T V124; + public T V125; + public T V126; + public T V127; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + 16 => V16, + 17 => V17, + 18 => V18, + 19 => V19, + 20 => V20, + 21 => V21, + 22 => V22, + 23 => V23, + 24 => V24, + 25 => V25, + 26 => V26, + 27 => V27, + 28 => V28, + 29 => V29, + 30 => V30, + 31 => V31, + 32 => V32, + 33 => V33, + 34 => V34, + 35 => V35, + 36 => V36, + 37 => V37, + 38 => V38, + 39 => V39, + 40 => V40, + 41 => V41, + 42 => V42, + 43 => V43, + 44 => V44, + 45 => V45, + 46 => V46, + 47 => V47, + 48 => V48, + 49 => V49, + 50 => V50, + 51 => V51, + 52 => V52, + 53 => V53, + 54 => V54, + 55 => V55, + 56 => V56, + 57 => V57, + 58 => V58, + 59 => V59, + 60 => V60, + 61 => V61, + 62 => V62, + 63 => V63, + 64 => V64, + 65 => V65, + 66 => V66, + 67 => V67, + 68 => V68, + 69 => V69, + 70 => V70, + 71 => V71, + 72 => V72, + 73 => V73, + 74 => V74, + 75 => V75, + 76 => V76, + 77 => V77, + 78 => V78, + 79 => V79, + 80 => V80, + 81 => V81, + 82 => V82, + 83 => V83, + 84 => V84, + 85 => V85, + 86 => V86, + 87 => V87, + 88 => V88, + 89 => V89, + 90 => V90, + 91 => V91, + 92 => V92, + 93 => V93, + 94 => V94, + 95 => V95, + 96 => V96, + 97 => V97, + 98 => V98, + 99 => V99, + 100 => V100, + 101 => V101, + 102 => V102, + 103 => V103, + 104 => V104, + 105 => V105, + 106 => V106, + 107 => V107, + 108 => V108, + 109 => V109, + 110 => V110, + 111 => V111, + 112 => V112, + 113 => V113, + 114 => V114, + 115 => V115, + 116 => V116, + 117 => V117, + 118 => V118, + 119 => V119, + 120 => V120, + 121 => V121, + 122 => V122, + 123 => V123, + 124 => V124, + 125 => V125, + 126 => V126, + 127 => V127, + _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + case 16: V16 = value; break; + case 17: V17 = value; break; + case 18: V18 = value; break; + case 19: V19 = value; break; + case 20: V20 = value; break; + case 21: V21 = value; break; + case 22: V22 = value; break; + case 23: V23 = value; break; + case 24: V24 = value; break; + case 25: V25 = value; break; + case 26: V26 = value; break; + case 27: V27 = value; break; + case 28: V28 = value; break; + case 29: V29 = value; break; + case 30: V30 = value; break; + case 31: V31 = value; break; + case 32 : V32 = value; break; + case 33 : V33 = value; break; + case 34 : V34 = value; break; + case 35 : V35 = value; break; + case 36 : V36 = value; break; + case 37 : V37 = value; break; + case 38 : V38 = value; break; + case 39 : V39 = value; break; + case 40 : V40 = value; break; + case 41 : V41 = value; break; + case 42 : V42 = value; break; + case 43 : V43 = value; break; + case 44 : V44 = value; break; + case 45 : V45 = value; break; + case 46 : V46 = value; break; + case 47 : V47 = value; break; + case 48 : V48 = value; break; + case 49 : V49 = value; break; + case 50 : V50 = value; break; + case 51 : V51 = value; break; + case 52 : V52 = value; break; + case 53 : V53 = value; break; + case 54 : V54 = value; break; + case 55 : V55 = value; break; + case 56 : V56 = value; break; + case 57 : V57 = value; break; + case 58 : V58 = value; break; + case 59 : V59 = value; break; + case 60 : V60 = value; break; + case 61 : V61 = value; break; + case 62 : V62 = value; break; + case 63 : V63 = value; break; + case 64 : V64 = value; break; + case 65 : V65 = value; break; + case 66 : V66 = value; break; + case 67 : V67 = value; break; + case 68 : V68 = value; break; + case 69 : V69 = value; break; + case 70 : V70 = value; break; + case 71 : V71 = value; break; + case 72 : V72 = value; break; + case 73 : V73 = value; break; + case 74 : V74 = value; break; + case 75 : V75 = value; break; + case 76 : V76 = value; break; + case 77 : V77 = value; break; + case 78 : V78 = value; break; + case 79 : V79 = value; break; + case 80 : V80 = value; break; + case 81 : V81 = value; break; + case 82 : V82 = value; break; + case 83 : V83 = value; break; + case 84 : V84 = value; break; + case 85 : V85 = value; break; + case 86 : V86 = value; break; + case 87 : V87 = value; break; + case 88 : V88 = value; break; + case 89 : V89 = value; break; + case 90 : V90 = value; break; + case 91 : V91 = value; break; + case 92 : V92 = value; break; + case 93 : V93 = value; break; + case 94 : V94 = value; break; + case 95 : V95 = value; break; + case 96 : V96 = value; break; + case 97 : V97 = value; break; + case 98 : V98 = value; break; + case 99 : V99 = value; break; + case 100 : V100 = value; break; + case 101 : V101 = value; break; + case 102 : V102 = value; break; + case 103 : V103 = value; break; + case 104 : V104 = value; break; + case 105 : V105 = value; break; + case 106 : V106 = value; break; + case 107 : V107 = value; break; + case 108 : V108 = value; break; + case 109 : V109 = value; break; + case 110 : V110 = value; break; + case 111 : V111 = value; break; + case 112 : V112 = value; break; + case 113 : V113 = value; break; + case 114 : V114 = value; break; + case 115 : V115 = value; break; + case 116 : V116 = value; break; + case 117 : V117 = value; break; + case 118 : V118 = value; break; + case 119 : V119 = value; break; + case 120 : V120 = value; break; + case 121 : V121 = value; break; + case 122 : V122 = value; break; + case 123 : V123 = value; break; + case 124 : V124 = value; break; + case 125 : V125 = value; break; + case 126 : V126 = value; break; + case 127 : V127 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray16.cs b/src/DotRecast.Core/Collections/RcStackArray16.cs new file mode 100644 index 00000000..df3cb4f8 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray16.cs @@ -0,0 +1,85 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray16 + { + public static RcStackArray16 Empty => new RcStackArray16(); + + private const int Size = 16; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + _ => throw new IndexOutOfRangeException($"{index}") + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray2.cs b/src/DotRecast.Core/Collections/RcStackArray2.cs new file mode 100644 index 00000000..30affc00 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray2.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray2 + { + public static RcStackArray2 Empty => new RcStackArray2(); + + private const int Size = 2; + public int Length => Size; + + public T V0; + public T V1; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + _ => throw new IndexOutOfRangeException($"{index}") + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray256.cs b/src/DotRecast.Core/Collections/RcStackArray256.cs new file mode 100644 index 00000000..8dec9b94 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray256.cs @@ -0,0 +1,805 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray256 + { + public static RcStackArray256 Empty => new RcStackArray256(); + + private const int Size = 256; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + public T V16; + public T V17; + public T V18; + public T V19; + public T V20; + public T V21; + public T V22; + public T V23; + public T V24; + public T V25; + public T V26; + public T V27; + public T V28; + public T V29; + public T V30; + public T V31; + public T V32; + public T V33; + public T V34; + public T V35; + public T V36; + public T V37; + public T V38; + public T V39; + public T V40; + public T V41; + public T V42; + public T V43; + public T V44; + public T V45; + public T V46; + public T V47; + public T V48; + public T V49; + public T V50; + public T V51; + public T V52; + public T V53; + public T V54; + public T V55; + public T V56; + public T V57; + public T V58; + public T V59; + public T V60; + public T V61; + public T V62; + public T V63; + public T V64; + public T V65; + public T V66; + public T V67; + public T V68; + public T V69; + public T V70; + public T V71; + public T V72; + public T V73; + public T V74; + public T V75; + public T V76; + public T V77; + public T V78; + public T V79; + public T V80; + public T V81; + public T V82; + public T V83; + public T V84; + public T V85; + public T V86; + public T V87; + public T V88; + public T V89; + public T V90; + public T V91; + public T V92; + public T V93; + public T V94; + public T V95; + public T V96; + public T V97; + public T V98; + public T V99; + public T V100; + public T V101; + public T V102; + public T V103; + public T V104; + public T V105; + public T V106; + public T V107; + public T V108; + public T V109; + public T V110; + public T V111; + public T V112; + public T V113; + public T V114; + public T V115; + public T V116; + public T V117; + public T V118; + public T V119; + public T V120; + public T V121; + public T V122; + public T V123; + public T V124; + public T V125; + public T V126; + public T V127; + public T V128; + public T V129; + public T V130; + public T V131; + public T V132; + public T V133; + public T V134; + public T V135; + public T V136; + public T V137; + public T V138; + public T V139; + public T V140; + public T V141; + public T V142; + public T V143; + public T V144; + public T V145; + public T V146; + public T V147; + public T V148; + public T V149; + public T V150; + public T V151; + public T V152; + public T V153; + public T V154; + public T V155; + public T V156; + public T V157; + public T V158; + public T V159; + public T V160; + public T V161; + public T V162; + public T V163; + public T V164; + public T V165; + public T V166; + public T V167; + public T V168; + public T V169; + public T V170; + public T V171; + public T V172; + public T V173; + public T V174; + public T V175; + public T V176; + public T V177; + public T V178; + public T V179; + public T V180; + public T V181; + public T V182; + public T V183; + public T V184; + public T V185; + public T V186; + public T V187; + public T V188; + public T V189; + public T V190; + public T V191; + public T V192; + public T V193; + public T V194; + public T V195; + public T V196; + public T V197; + public T V198; + public T V199; + public T V200; + public T V201; + public T V202; + public T V203; + public T V204; + public T V205; + public T V206; + public T V207; + public T V208; + public T V209; + public T V210; + public T V211; + public T V212; + public T V213; + public T V214; + public T V215; + public T V216; + public T V217; + public T V218; + public T V219; + public T V220; + public T V221; + public T V222; + public T V223; + public T V224; + public T V225; + public T V226; + public T V227; + public T V228; + public T V229; + public T V230; + public T V231; + public T V232; + public T V233; + public T V234; + public T V235; + public T V236; + public T V237; + public T V238; + public T V239; + public T V240; + public T V241; + public T V242; + public T V243; + public T V244; + public T V245; + public T V246; + public T V247; + public T V248; + public T V249; + public T V250; + public T V251; + public T V252; + public T V253; + public T V254; + public T V255; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + 16 => V16, + 17 => V17, + 18 => V18, + 19 => V19, + 20 => V20, + 21 => V21, + 22 => V22, + 23 => V23, + 24 => V24, + 25 => V25, + 26 => V26, + 27 => V27, + 28 => V28, + 29 => V29, + 30 => V30, + 31 => V31, + 32 => V32, + 33 => V33, + 34 => V34, + 35 => V35, + 36 => V36, + 37 => V37, + 38 => V38, + 39 => V39, + 40 => V40, + 41 => V41, + 42 => V42, + 43 => V43, + 44 => V44, + 45 => V45, + 46 => V46, + 47 => V47, + 48 => V48, + 49 => V49, + 50 => V50, + 51 => V51, + 52 => V52, + 53 => V53, + 54 => V54, + 55 => V55, + 56 => V56, + 57 => V57, + 58 => V58, + 59 => V59, + 60 => V60, + 61 => V61, + 62 => V62, + 63 => V63, + 64 => V64, + 65 => V65, + 66 => V66, + 67 => V67, + 68 => V68, + 69 => V69, + 70 => V70, + 71 => V71, + 72 => V72, + 73 => V73, + 74 => V74, + 75 => V75, + 76 => V76, + 77 => V77, + 78 => V78, + 79 => V79, + 80 => V80, + 81 => V81, + 82 => V82, + 83 => V83, + 84 => V84, + 85 => V85, + 86 => V86, + 87 => V87, + 88 => V88, + 89 => V89, + 90 => V90, + 91 => V91, + 92 => V92, + 93 => V93, + 94 => V94, + 95 => V95, + 96 => V96, + 97 => V97, + 98 => V98, + 99 => V99, + 100 => V100, + 101 => V101, + 102 => V102, + 103 => V103, + 104 => V104, + 105 => V105, + 106 => V106, + 107 => V107, + 108 => V108, + 109 => V109, + 110 => V110, + 111 => V111, + 112 => V112, + 113 => V113, + 114 => V114, + 115 => V115, + 116 => V116, + 117 => V117, + 118 => V118, + 119 => V119, + 120 => V120, + 121 => V121, + 122 => V122, + 123 => V123, + 124 => V124, + 125 => V125, + 126 => V126, + 127 => V127, + 128 => V128, + 129 => V129, + 130 => V130, + 131 => V131, + 132 => V132, + 133 => V133, + 134 => V134, + 135 => V135, + 136 => V136, + 137 => V137, + 138 => V138, + 139 => V139, + 140 => V140, + 141 => V141, + 142 => V142, + 143 => V143, + 144 => V144, + 145 => V145, + 146 => V146, + 147 => V147, + 148 => V148, + 149 => V149, + 150 => V150, + 151 => V151, + 152 => V152, + 153 => V153, + 154 => V154, + 155 => V155, + 156 => V156, + 157 => V157, + 158 => V158, + 159 => V159, + 160 => V160, + 161 => V161, + 162 => V162, + 163 => V163, + 164 => V164, + 165 => V165, + 166 => V166, + 167 => V167, + 168 => V168, + 169 => V169, + 170 => V170, + 171 => V171, + 172 => V172, + 173 => V173, + 174 => V174, + 175 => V175, + 176 => V176, + 177 => V177, + 178 => V178, + 179 => V179, + 180 => V180, + 181 => V181, + 182 => V182, + 183 => V183, + 184 => V184, + 185 => V185, + 186 => V186, + 187 => V187, + 188 => V188, + 189 => V189, + 190 => V190, + 191 => V191, + 192 => V192, + 193 => V193, + 194 => V194, + 195 => V195, + 196 => V196, + 197 => V197, + 198 => V198, + 199 => V199, + 200 => V200, + 201 => V201, + 202 => V202, + 203 => V203, + 204 => V204, + 205 => V205, + 206 => V206, + 207 => V207, + 208 => V208, + 209 => V209, + 210 => V210, + 211 => V211, + 212 => V212, + 213 => V213, + 214 => V214, + 215 => V215, + 216 => V216, + 217 => V217, + 218 => V218, + 219 => V219, + 220 => V220, + 221 => V221, + 222 => V222, + 223 => V223, + 224 => V224, + 225 => V225, + 226 => V226, + 227 => V227, + 228 => V228, + 229 => V229, + 230 => V230, + 231 => V231, + 232 => V232, + 233 => V233, + 234 => V234, + 235 => V235, + 236 => V236, + 237 => V237, + 238 => V238, + 239 => V239, + 240 => V240, + 241 => V241, + 242 => V242, + 243 => V243, + 244 => V244, + 245 => V245, + 246 => V246, + 247 => V247, + 248 => V248, + 249 => V249, + 250 => V250, + 251 => V251, + 252 => V252, + 253 => V253, + 254 => V254, + 255 => V255, + _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + case 16: V16 = value; break; + case 17: V17 = value; break; + case 18: V18 = value; break; + case 19: V19 = value; break; + case 20: V20 = value; break; + case 21: V21 = value; break; + case 22: V22 = value; break; + case 23: V23 = value; break; + case 24: V24 = value; break; + case 25: V25 = value; break; + case 26: V26 = value; break; + case 27: V27 = value; break; + case 28: V28 = value; break; + case 29: V29 = value; break; + case 30: V30 = value; break; + case 31: V31 = value; break; + case 32: V32 = value; break; + case 33: V33 = value; break; + case 34: V34 = value; break; + case 35: V35 = value; break; + case 36: V36 = value; break; + case 37: V37 = value; break; + case 38: V38 = value; break; + case 39: V39 = value; break; + case 40: V40 = value; break; + case 41: V41 = value; break; + case 42: V42 = value; break; + case 43: V43 = value; break; + case 44: V44 = value; break; + case 45: V45 = value; break; + case 46: V46 = value; break; + case 47: V47 = value; break; + case 48: V48 = value; break; + case 49: V49 = value; break; + case 50: V50 = value; break; + case 51: V51 = value; break; + case 52: V52 = value; break; + case 53: V53 = value; break; + case 54: V54 = value; break; + case 55: V55 = value; break; + case 56: V56 = value; break; + case 57: V57 = value; break; + case 58: V58 = value; break; + case 59: V59 = value; break; + case 60: V60 = value; break; + case 61: V61 = value; break; + case 62: V62 = value; break; + case 63: V63 = value; break; + case 64: V64 = value; break; + case 65: V65 = value; break; + case 66: V66 = value; break; + case 67: V67 = value; break; + case 68: V68 = value; break; + case 69: V69 = value; break; + case 70: V70 = value; break; + case 71: V71 = value; break; + case 72: V72 = value; break; + case 73: V73 = value; break; + case 74: V74 = value; break; + case 75: V75 = value; break; + case 76: V76 = value; break; + case 77: V77 = value; break; + case 78: V78 = value; break; + case 79: V79 = value; break; + case 80: V80 = value; break; + case 81: V81 = value; break; + case 82: V82 = value; break; + case 83: V83 = value; break; + case 84: V84 = value; break; + case 85: V85 = value; break; + case 86: V86 = value; break; + case 87: V87 = value; break; + case 88: V88 = value; break; + case 89: V89 = value; break; + case 90: V90 = value; break; + case 91: V91 = value; break; + case 92: V92 = value; break; + case 93: V93 = value; break; + case 94: V94 = value; break; + case 95: V95 = value; break; + case 96: V96 = value; break; + case 97: V97 = value; break; + case 98: V98 = value; break; + case 99: V99 = value; break; + case 100: V100 = value; break; + case 101: V101 = value; break; + case 102: V102 = value; break; + case 103: V103 = value; break; + case 104: V104 = value; break; + case 105: V105 = value; break; + case 106: V106 = value; break; + case 107: V107 = value; break; + case 108: V108 = value; break; + case 109: V109 = value; break; + case 110: V110 = value; break; + case 111: V111 = value; break; + case 112: V112 = value; break; + case 113: V113 = value; break; + case 114: V114 = value; break; + case 115: V115 = value; break; + case 116: V116 = value; break; + case 117: V117 = value; break; + case 118: V118 = value; break; + case 119: V119 = value; break; + case 120: V120 = value; break; + case 121: V121 = value; break; + case 122: V122 = value; break; + case 123: V123 = value; break; + case 124: V124 = value; break; + case 125: V125 = value; break; + case 126: V126 = value; break; + case 127: V127 = value; break; + case 128: V128 = value; break; + case 129: V129 = value; break; + case 130: V130 = value; break; + case 131: V131 = value; break; + case 132: V132 = value; break; + case 133: V133 = value; break; + case 134: V134 = value; break; + case 135: V135 = value; break; + case 136: V136 = value; break; + case 137: V137 = value; break; + case 138: V138 = value; break; + case 139: V139 = value; break; + case 140: V140 = value; break; + case 141: V141 = value; break; + case 142: V142 = value; break; + case 143: V143 = value; break; + case 144: V144 = value; break; + case 145: V145 = value; break; + case 146: V146 = value; break; + case 147: V147 = value; break; + case 148: V148 = value; break; + case 149: V149 = value; break; + case 150: V150 = value; break; + case 151: V151 = value; break; + case 152: V152 = value; break; + case 153: V153 = value; break; + case 154: V154 = value; break; + case 155: V155 = value; break; + case 156: V156 = value; break; + case 157: V157 = value; break; + case 158: V158 = value; break; + case 159: V159 = value; break; + case 160: V160 = value; break; + case 161: V161 = value; break; + case 162: V162 = value; break; + case 163: V163 = value; break; + case 164: V164 = value; break; + case 165: V165 = value; break; + case 166: V166 = value; break; + case 167: V167 = value; break; + case 168: V168 = value; break; + case 169: V169 = value; break; + case 170: V170 = value; break; + case 171: V171 = value; break; + case 172: V172 = value; break; + case 173: V173 = value; break; + case 174: V174 = value; break; + case 175: V175 = value; break; + case 176: V176 = value; break; + case 177: V177 = value; break; + case 178: V178 = value; break; + case 179: V179 = value; break; + case 180: V180 = value; break; + case 181: V181 = value; break; + case 182: V182 = value; break; + case 183: V183 = value; break; + case 184: V184 = value; break; + case 185: V185 = value; break; + case 186: V186 = value; break; + case 187: V187 = value; break; + case 188: V188 = value; break; + case 189: V189 = value; break; + case 190: V190 = value; break; + case 191: V191 = value; break; + case 192: V192 = value; break; + case 193: V193 = value; break; + case 194: V194 = value; break; + case 195: V195 = value; break; + case 196: V196 = value; break; + case 197: V197 = value; break; + case 198: V198 = value; break; + case 199: V199 = value; break; + case 200: V200 = value; break; + case 201: V201 = value; break; + case 202: V202 = value; break; + case 203: V203 = value; break; + case 204: V204 = value; break; + case 205: V205 = value; break; + case 206: V206 = value; break; + case 207: V207 = value; break; + case 208: V208 = value; break; + case 209: V209 = value; break; + case 210: V210 = value; break; + case 211: V211 = value; break; + case 212: V212 = value; break; + case 213: V213 = value; break; + case 214: V214 = value; break; + case 215: V215 = value; break; + case 216: V216 = value; break; + case 217: V217 = value; break; + case 218: V218 = value; break; + case 219: V219 = value; break; + case 220: V220 = value; break; + case 221: V221 = value; break; + case 222: V222 = value; break; + case 223: V223 = value; break; + case 224: V224 = value; break; + case 225: V225 = value; break; + case 226: V226 = value; break; + case 227: V227 = value; break; + case 228: V228 = value; break; + case 229: V229 = value; break; + case 230: V230 = value; break; + case 231: V231 = value; break; + case 232: V232 = value; break; + case 233: V233 = value; break; + case 234: V234 = value; break; + case 235: V235 = value; break; + case 236: V236 = value; break; + case 237: V237 = value; break; + case 238: V238 = value; break; + case 239: V239 = value; break; + case 240: V240 = value; break; + case 241: V241 = value; break; + case 242: V242 = value; break; + case 243: V243 = value; break; + case 244: V244 = value; break; + case 245: V245 = value; break; + case 246: V246 = value; break; + case 247: V247 = value; break; + case 248: V248 = value; break; + case 249: V249 = value; break; + case 250: V250 = value; break; + case 251: V251 = value; break; + case 252: V252 = value; break; + case 253: V253 = value; break; + case 254: V254 = value; break; + case 255: V255 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray32.cs b/src/DotRecast.Core/Collections/RcStackArray32.cs new file mode 100644 index 00000000..acd5e065 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray32.cs @@ -0,0 +1,133 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray32 + { + public static RcStackArray32 Empty => new RcStackArray32(); + + private const int Size = 32; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + public T V16; + public T V17; + public T V18; + public T V19; + public T V20; + public T V21; + public T V22; + public T V23; + public T V24; + public T V25; + public T V26; + public T V27; + public T V28; + public T V29; + public T V30; + public T V31; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + 16 => V16, + 17 => V17, + 18 => V18, + 19 => V19, + 20 => V20, + 21 => V21, + 22 => V22, + 23 => V23, + 24 => V24, + 25 => V25, + 26 => V26, + 27 => V27, + 28 => V28, + 29 => V29, + 30 => V30, + 31 => V31, + _ => throw new IndexOutOfRangeException($"{index}") + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + case 16: V16 = value; break; + case 17: V17 = value; break; + case 18: V18 = value; break; + case 19: V19 = value; break; + case 20: V20 = value; break; + case 21: V21 = value; break; + case 22: V22 = value; break; + case 23: V23 = value; break; + case 24: V24 = value; break; + case 25: V25 = value; break; + case 26: V26 = value; break; + case 27: V27 = value; break; + case 28: V28 = value; break; + case 29: V29 = value; break; + case 30: V30 = value; break; + case 31: V31 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray4.cs b/src/DotRecast.Core/Collections/RcStackArray4.cs new file mode 100644 index 00000000..38d51333 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray4.cs @@ -0,0 +1,49 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray4 + { + public static RcStackArray4 Empty => new RcStackArray4(); + + private const int Size = 4; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + _ => throw new IndexOutOfRangeException($"{index}") + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray512.cs b/src/DotRecast.Core/Collections/RcStackArray512.cs new file mode 100644 index 00000000..95b59ae1 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray512.cs @@ -0,0 +1,1574 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray512 + { + public static RcStackArray512 Empty => new RcStackArray512(); + + private const int Size = 512; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + public T V16; + public T V17; + public T V18; + public T V19; + public T V20; + public T V21; + public T V22; + public T V23; + public T V24; + public T V25; + public T V26; + public T V27; + public T V28; + public T V29; + public T V30; + public T V31; + public T V32; + public T V33; + public T V34; + public T V35; + public T V36; + public T V37; + public T V38; + public T V39; + public T V40; + public T V41; + public T V42; + public T V43; + public T V44; + public T V45; + public T V46; + public T V47; + public T V48; + public T V49; + public T V50; + public T V51; + public T V52; + public T V53; + public T V54; + public T V55; + public T V56; + public T V57; + public T V58; + public T V59; + public T V60; + public T V61; + public T V62; + public T V63; + public T V64; + public T V65; + public T V66; + public T V67; + public T V68; + public T V69; + public T V70; + public T V71; + public T V72; + public T V73; + public T V74; + public T V75; + public T V76; + public T V77; + public T V78; + public T V79; + public T V80; + public T V81; + public T V82; + public T V83; + public T V84; + public T V85; + public T V86; + public T V87; + public T V88; + public T V89; + public T V90; + public T V91; + public T V92; + public T V93; + public T V94; + public T V95; + public T V96; + public T V97; + public T V98; + public T V99; + public T V100; + public T V101; + public T V102; + public T V103; + public T V104; + public T V105; + public T V106; + public T V107; + public T V108; + public T V109; + public T V110; + public T V111; + public T V112; + public T V113; + public T V114; + public T V115; + public T V116; + public T V117; + public T V118; + public T V119; + public T V120; + public T V121; + public T V122; + public T V123; + public T V124; + public T V125; + public T V126; + public T V127; + public T V128; + public T V129; + public T V130; + public T V131; + public T V132; + public T V133; + public T V134; + public T V135; + public T V136; + public T V137; + public T V138; + public T V139; + public T V140; + public T V141; + public T V142; + public T V143; + public T V144; + public T V145; + public T V146; + public T V147; + public T V148; + public T V149; + public T V150; + public T V151; + public T V152; + public T V153; + public T V154; + public T V155; + public T V156; + public T V157; + public T V158; + public T V159; + public T V160; + public T V161; + public T V162; + public T V163; + public T V164; + public T V165; + public T V166; + public T V167; + public T V168; + public T V169; + public T V170; + public T V171; + public T V172; + public T V173; + public T V174; + public T V175; + public T V176; + public T V177; + public T V178; + public T V179; + public T V180; + public T V181; + public T V182; + public T V183; + public T V184; + public T V185; + public T V186; + public T V187; + public T V188; + public T V189; + public T V190; + public T V191; + public T V192; + public T V193; + public T V194; + public T V195; + public T V196; + public T V197; + public T V198; + public T V199; + public T V200; + public T V201; + public T V202; + public T V203; + public T V204; + public T V205; + public T V206; + public T V207; + public T V208; + public T V209; + public T V210; + public T V211; + public T V212; + public T V213; + public T V214; + public T V215; + public T V216; + public T V217; + public T V218; + public T V219; + public T V220; + public T V221; + public T V222; + public T V223; + public T V224; + public T V225; + public T V226; + public T V227; + public T V228; + public T V229; + public T V230; + public T V231; + public T V232; + public T V233; + public T V234; + public T V235; + public T V236; + public T V237; + public T V238; + public T V239; + public T V240; + public T V241; + public T V242; + public T V243; + public T V244; + public T V245; + public T V246; + public T V247; + public T V248; + public T V249; + public T V250; + public T V251; + public T V252; + public T V253; + public T V254; + public T V255; + public T V256; + public T V257; + public T V258; + public T V259; + public T V260; + public T V261; + public T V262; + public T V263; + public T V264; + public T V265; + public T V266; + public T V267; + public T V268; + public T V269; + public T V270; + public T V271; + public T V272; + public T V273; + public T V274; + public T V275; + public T V276; + public T V277; + public T V278; + public T V279; + public T V280; + public T V281; + public T V282; + public T V283; + public T V284; + public T V285; + public T V286; + public T V287; + public T V288; + public T V289; + public T V290; + public T V291; + public T V292; + public T V293; + public T V294; + public T V295; + public T V296; + public T V297; + public T V298; + public T V299; + public T V300; + public T V301; + public T V302; + public T V303; + public T V304; + public T V305; + public T V306; + public T V307; + public T V308; + public T V309; + public T V310; + public T V311; + public T V312; + public T V313; + public T V314; + public T V315; + public T V316; + public T V317; + public T V318; + public T V319; + public T V320; + public T V321; + public T V322; + public T V323; + public T V324; + public T V325; + public T V326; + public T V327; + public T V328; + public T V329; + public T V330; + public T V331; + public T V332; + public T V333; + public T V334; + public T V335; + public T V336; + public T V337; + public T V338; + public T V339; + public T V340; + public T V341; + public T V342; + public T V343; + public T V344; + public T V345; + public T V346; + public T V347; + public T V348; + public T V349; + public T V350; + public T V351; + public T V352; + public T V353; + public T V354; + public T V355; + public T V356; + public T V357; + public T V358; + public T V359; + public T V360; + public T V361; + public T V362; + public T V363; + public T V364; + public T V365; + public T V366; + public T V367; + public T V368; + public T V369; + public T V370; + public T V371; + public T V372; + public T V373; + public T V374; + public T V375; + public T V376; + public T V377; + public T V378; + public T V379; + public T V380; + public T V381; + public T V382; + public T V383; + public T V384; + public T V385; + public T V386; + public T V387; + public T V388; + public T V389; + public T V390; + public T V391; + public T V392; + public T V393; + public T V394; + public T V395; + public T V396; + public T V397; + public T V398; + public T V399; + public T V400; + public T V401; + public T V402; + public T V403; + public T V404; + public T V405; + public T V406; + public T V407; + public T V408; + public T V409; + public T V410; + public T V411; + public T V412; + public T V413; + public T V414; + public T V415; + public T V416; + public T V417; + public T V418; + public T V419; + public T V420; + public T V421; + public T V422; + public T V423; + public T V424; + public T V425; + public T V426; + public T V427; + public T V428; + public T V429; + public T V430; + public T V431; + public T V432; + public T V433; + public T V434; + public T V435; + public T V436; + public T V437; + public T V438; + public T V439; + public T V440; + public T V441; + public T V442; + public T V443; + public T V444; + public T V445; + public T V446; + public T V447; + public T V448; + public T V449; + public T V450; + public T V451; + public T V452; + public T V453; + public T V454; + public T V455; + public T V456; + public T V457; + public T V458; + public T V459; + public T V460; + public T V461; + public T V462; + public T V463; + public T V464; + public T V465; + public T V466; + public T V467; + public T V468; + public T V469; + public T V470; + public T V471; + public T V472; + public T V473; + public T V474; + public T V475; + public T V476; + public T V477; + public T V478; + public T V479; + public T V480; + public T V481; + public T V482; + public T V483; + public T V484; + public T V485; + public T V486; + public T V487; + public T V488; + public T V489; + public T V490; + public T V491; + public T V492; + public T V493; + public T V494; + public T V495; + public T V496; + public T V497; + public T V498; + public T V499; + public T V500; + public T V501; + public T V502; + public T V503; + public T V504; + public T V505; + public T V506; + public T V507; + public T V508; + public T V509; + public T V510; + public T V511; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + 16 => V16, + 17 => V17, + 18 => V18, + 19 => V19, + 20 => V20, + 21 => V21, + 22 => V22, + 23 => V23, + 24 => V24, + 25 => V25, + 26 => V26, + 27 => V27, + 28 => V28, + 29 => V29, + 30 => V30, + 31 => V31, + 32 => V32, + 33 => V33, + 34 => V34, + 35 => V35, + 36 => V36, + 37 => V37, + 38 => V38, + 39 => V39, + 40 => V40, + 41 => V41, + 42 => V42, + 43 => V43, + 44 => V44, + 45 => V45, + 46 => V46, + 47 => V47, + 48 => V48, + 49 => V49, + 50 => V50, + 51 => V51, + 52 => V52, + 53 => V53, + 54 => V54, + 55 => V55, + 56 => V56, + 57 => V57, + 58 => V58, + 59 => V59, + 60 => V60, + 61 => V61, + 62 => V62, + 63 => V63, + 64 => V64, + 65 => V65, + 66 => V66, + 67 => V67, + 68 => V68, + 69 => V69, + 70 => V70, + 71 => V71, + 72 => V72, + 73 => V73, + 74 => V74, + 75 => V75, + 76 => V76, + 77 => V77, + 78 => V78, + 79 => V79, + 80 => V80, + 81 => V81, + 82 => V82, + 83 => V83, + 84 => V84, + 85 => V85, + 86 => V86, + 87 => V87, + 88 => V88, + 89 => V89, + 90 => V90, + 91 => V91, + 92 => V92, + 93 => V93, + 94 => V94, + 95 => V95, + 96 => V96, + 97 => V97, + 98 => V98, + 99 => V99, + 100 => V100, + 101 => V101, + 102 => V102, + 103 => V103, + 104 => V104, + 105 => V105, + 106 => V106, + 107 => V107, + 108 => V108, + 109 => V109, + 110 => V110, + 111 => V111, + 112 => V112, + 113 => V113, + 114 => V114, + 115 => V115, + 116 => V116, + 117 => V117, + 118 => V118, + 119 => V119, + 120 => V120, + 121 => V121, + 122 => V122, + 123 => V123, + 124 => V124, + 125 => V125, + 126 => V126, + 127 => V127, + 128 => V128, + 129 => V129, + 130 => V130, + 131 => V131, + 132 => V132, + 133 => V133, + 134 => V134, + 135 => V135, + 136 => V136, + 137 => V137, + 138 => V138, + 139 => V139, + 140 => V140, + 141 => V141, + 142 => V142, + 143 => V143, + 144 => V144, + 145 => V145, + 146 => V146, + 147 => V147, + 148 => V148, + 149 => V149, + 150 => V150, + 151 => V151, + 152 => V152, + 153 => V153, + 154 => V154, + 155 => V155, + 156 => V156, + 157 => V157, + 158 => V158, + 159 => V159, + 160 => V160, + 161 => V161, + 162 => V162, + 163 => V163, + 164 => V164, + 165 => V165, + 166 => V166, + 167 => V167, + 168 => V168, + 169 => V169, + 170 => V170, + 171 => V171, + 172 => V172, + 173 => V173, + 174 => V174, + 175 => V175, + 176 => V176, + 177 => V177, + 178 => V178, + 179 => V179, + 180 => V180, + 181 => V181, + 182 => V182, + 183 => V183, + 184 => V184, + 185 => V185, + 186 => V186, + 187 => V187, + 188 => V188, + 189 => V189, + 190 => V190, + 191 => V191, + 192 => V192, + 193 => V193, + 194 => V194, + 195 => V195, + 196 => V196, + 197 => V197, + 198 => V198, + 199 => V199, + 200 => V200, + 201 => V201, + 202 => V202, + 203 => V203, + 204 => V204, + 205 => V205, + 206 => V206, + 207 => V207, + 208 => V208, + 209 => V209, + 210 => V210, + 211 => V211, + 212 => V212, + 213 => V213, + 214 => V214, + 215 => V215, + 216 => V216, + 217 => V217, + 218 => V218, + 219 => V219, + 220 => V220, + 221 => V221, + 222 => V222, + 223 => V223, + 224 => V224, + 225 => V225, + 226 => V226, + 227 => V227, + 228 => V228, + 229 => V229, + 230 => V230, + 231 => V231, + 232 => V232, + 233 => V233, + 234 => V234, + 235 => V235, + 236 => V236, + 237 => V237, + 238 => V238, + 239 => V239, + 240 => V240, + 241 => V241, + 242 => V242, + 243 => V243, + 244 => V244, + 245 => V245, + 246 => V246, + 247 => V247, + 248 => V248, + 249 => V249, + 250 => V250, + 251 => V251, + 252 => V252, + 253 => V253, + 254 => V254, + 255 => V255, + 256 => V256, + 257 => V257, + 258 => V258, + 259 => V259, + 260 => V260, + 261 => V261, + 262 => V262, + 263 => V263, + 264 => V264, + 265 => V265, + 266 => V266, + 267 => V267, + 268 => V268, + 269 => V269, + 270 => V270, + 271 => V271, + 272 => V272, + 273 => V273, + 274 => V274, + 275 => V275, + 276 => V276, + 277 => V277, + 278 => V278, + 279 => V279, + 280 => V280, + 281 => V281, + 282 => V282, + 283 => V283, + 284 => V284, + 285 => V285, + 286 => V286, + 287 => V287, + 288 => V288, + 289 => V289, + 290 => V290, + 291 => V291, + 292 => V292, + 293 => V293, + 294 => V294, + 295 => V295, + 296 => V296, + 297 => V297, + 298 => V298, + 299 => V299, + 300 => V300, + 301 => V301, + 302 => V302, + 303 => V303, + 304 => V304, + 305 => V305, + 306 => V306, + 307 => V307, + 308 => V308, + 309 => V309, + 310 => V310, + 311 => V311, + 312 => V312, + 313 => V313, + 314 => V314, + 315 => V315, + 316 => V316, + 317 => V317, + 318 => V318, + 319 => V319, + 320 => V320, + 321 => V321, + 322 => V322, + 323 => V323, + 324 => V324, + 325 => V325, + 326 => V326, + 327 => V327, + 328 => V328, + 329 => V329, + 330 => V330, + 331 => V331, + 332 => V332, + 333 => V333, + 334 => V334, + 335 => V335, + 336 => V336, + 337 => V337, + 338 => V338, + 339 => V339, + 340 => V340, + 341 => V341, + 342 => V342, + 343 => V343, + 344 => V344, + 345 => V345, + 346 => V346, + 347 => V347, + 348 => V348, + 349 => V349, + 350 => V350, + 351 => V351, + 352 => V352, + 353 => V353, + 354 => V354, + 355 => V355, + 356 => V356, + 357 => V357, + 358 => V358, + 359 => V359, + 360 => V360, + 361 => V361, + 362 => V362, + 363 => V363, + 364 => V364, + 365 => V365, + 366 => V366, + 367 => V367, + 368 => V368, + 369 => V369, + 370 => V370, + 371 => V371, + 372 => V372, + 373 => V373, + 374 => V374, + 375 => V375, + 376 => V376, + 377 => V377, + 378 => V378, + 379 => V379, + 380 => V380, + 381 => V381, + 382 => V382, + 383 => V383, + 384 => V384, + 385 => V385, + 386 => V386, + 387 => V387, + 388 => V388, + 389 => V389, + 390 => V390, + 391 => V391, + 392 => V392, + 393 => V393, + 394 => V394, + 395 => V395, + 396 => V396, + 397 => V397, + 398 => V398, + 399 => V399, + 400 => V400, + 401 => V401, + 402 => V402, + 403 => V403, + 404 => V404, + 405 => V405, + 406 => V406, + 407 => V407, + 408 => V408, + 409 => V409, + 410 => V410, + 411 => V411, + 412 => V412, + 413 => V413, + 414 => V414, + 415 => V415, + 416 => V416, + 417 => V417, + 418 => V418, + 419 => V419, + 420 => V420, + 421 => V421, + 422 => V422, + 423 => V423, + 424 => V424, + 425 => V425, + 426 => V426, + 427 => V427, + 428 => V428, + 429 => V429, + 430 => V430, + 431 => V431, + 432 => V432, + 433 => V433, + 434 => V434, + 435 => V435, + 436 => V436, + 437 => V437, + 438 => V438, + 439 => V439, + 440 => V440, + 441 => V441, + 442 => V442, + 443 => V443, + 444 => V444, + 445 => V445, + 446 => V446, + 447 => V447, + 448 => V448, + 449 => V449, + 450 => V450, + 451 => V451, + 452 => V452, + 453 => V453, + 454 => V454, + 455 => V455, + 456 => V456, + 457 => V457, + 458 => V458, + 459 => V459, + 460 => V460, + 461 => V461, + 462 => V462, + 463 => V463, + 464 => V464, + 465 => V465, + 466 => V466, + 467 => V467, + 468 => V468, + 469 => V469, + 470 => V470, + 471 => V471, + 472 => V472, + 473 => V473, + 474 => V474, + 475 => V475, + 476 => V476, + 477 => V477, + 478 => V478, + 479 => V479, + 480 => V480, + 481 => V481, + 482 => V482, + 483 => V483, + 484 => V484, + 485 => V485, + 486 => V486, + 487 => V487, + 488 => V488, + 489 => V489, + 490 => V490, + 491 => V491, + 492 => V492, + 493 => V493, + 494 => V494, + 495 => V495, + 496 => V496, + 497 => V497, + 498 => V498, + 499 => V499, + 500 => V500, + 501 => V501, + 502 => V502, + 503 => V503, + 504 => V504, + 505 => V505, + 506 => V506, + 507 => V507, + 508 => V508, + 509 => V509, + 510 => V510, + 511 => V511, + + _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + case 16: V16 = value; break; + case 17: V17 = value; break; + case 18: V18 = value; break; + case 19: V19 = value; break; + case 20: V20 = value; break; + case 21: V21 = value; break; + case 22: V22 = value; break; + case 23: V23 = value; break; + case 24: V24 = value; break; + case 25: V25 = value; break; + case 26: V26 = value; break; + case 27: V27 = value; break; + case 28: V28 = value; break; + case 29: V29 = value; break; + case 30: V30 = value; break; + case 31: V31 = value; break; + case 32: V32 = value; break; + case 33: V33 = value; break; + case 34: V34 = value; break; + case 35: V35 = value; break; + case 36: V36 = value; break; + case 37: V37 = value; break; + case 38: V38 = value; break; + case 39: V39 = value; break; + case 40: V40 = value; break; + case 41: V41 = value; break; + case 42: V42 = value; break; + case 43: V43 = value; break; + case 44: V44 = value; break; + case 45: V45 = value; break; + case 46: V46 = value; break; + case 47: V47 = value; break; + case 48: V48 = value; break; + case 49: V49 = value; break; + case 50: V50 = value; break; + case 51: V51 = value; break; + case 52: V52 = value; break; + case 53: V53 = value; break; + case 54: V54 = value; break; + case 55: V55 = value; break; + case 56: V56 = value; break; + case 57: V57 = value; break; + case 58: V58 = value; break; + case 59: V59 = value; break; + case 60: V60 = value; break; + case 61: V61 = value; break; + case 62: V62 = value; break; + case 63: V63 = value; break; + case 64: V64 = value; break; + case 65: V65 = value; break; + case 66: V66 = value; break; + case 67: V67 = value; break; + case 68: V68 = value; break; + case 69: V69 = value; break; + case 70: V70 = value; break; + case 71: V71 = value; break; + case 72: V72 = value; break; + case 73: V73 = value; break; + case 74: V74 = value; break; + case 75: V75 = value; break; + case 76: V76 = value; break; + case 77: V77 = value; break; + case 78: V78 = value; break; + case 79: V79 = value; break; + case 80: V80 = value; break; + case 81: V81 = value; break; + case 82: V82 = value; break; + case 83: V83 = value; break; + case 84: V84 = value; break; + case 85: V85 = value; break; + case 86: V86 = value; break; + case 87: V87 = value; break; + case 88: V88 = value; break; + case 89: V89 = value; break; + case 90: V90 = value; break; + case 91: V91 = value; break; + case 92: V92 = value; break; + case 93: V93 = value; break; + case 94: V94 = value; break; + case 95: V95 = value; break; + case 96: V96 = value; break; + case 97: V97 = value; break; + case 98: V98 = value; break; + case 99: V99 = value; break; + case 100: V100 = value; break; + case 101: V101 = value; break; + case 102: V102 = value; break; + case 103: V103 = value; break; + case 104: V104 = value; break; + case 105: V105 = value; break; + case 106: V106 = value; break; + case 107: V107 = value; break; + case 108: V108 = value; break; + case 109: V109 = value; break; + case 110: V110 = value; break; + case 111: V111 = value; break; + case 112: V112 = value; break; + case 113: V113 = value; break; + case 114: V114 = value; break; + case 115: V115 = value; break; + case 116: V116 = value; break; + case 117: V117 = value; break; + case 118: V118 = value; break; + case 119: V119 = value; break; + case 120: V120 = value; break; + case 121: V121 = value; break; + case 122: V122 = value; break; + case 123: V123 = value; break; + case 124: V124 = value; break; + case 125: V125 = value; break; + case 126: V126 = value; break; + case 127: V127 = value; break; + case 128: V128 = value; break; + case 129: V129 = value; break; + case 130: V130 = value; break; + case 131: V131 = value; break; + case 132: V132 = value; break; + case 133: V133 = value; break; + case 134: V134 = value; break; + case 135: V135 = value; break; + case 136: V136 = value; break; + case 137: V137 = value; break; + case 138: V138 = value; break; + case 139: V139 = value; break; + case 140: V140 = value; break; + case 141: V141 = value; break; + case 142: V142 = value; break; + case 143: V143 = value; break; + case 144: V144 = value; break; + case 145: V145 = value; break; + case 146: V146 = value; break; + case 147: V147 = value; break; + case 148: V148 = value; break; + case 149: V149 = value; break; + case 150: V150 = value; break; + case 151: V151 = value; break; + case 152: V152 = value; break; + case 153: V153 = value; break; + case 154: V154 = value; break; + case 155: V155 = value; break; + case 156: V156 = value; break; + case 157: V157 = value; break; + case 158: V158 = value; break; + case 159: V159 = value; break; + case 160: V160 = value; break; + case 161: V161 = value; break; + case 162: V162 = value; break; + case 163: V163 = value; break; + case 164: V164 = value; break; + case 165: V165 = value; break; + case 166: V166 = value; break; + case 167: V167 = value; break; + case 168: V168 = value; break; + case 169: V169 = value; break; + case 170: V170 = value; break; + case 171: V171 = value; break; + case 172: V172 = value; break; + case 173: V173 = value; break; + case 174: V174 = value; break; + case 175: V175 = value; break; + case 176: V176 = value; break; + case 177: V177 = value; break; + case 178: V178 = value; break; + case 179: V179 = value; break; + case 180: V180 = value; break; + case 181: V181 = value; break; + case 182: V182 = value; break; + case 183: V183 = value; break; + case 184: V184 = value; break; + case 185: V185 = value; break; + case 186: V186 = value; break; + case 187: V187 = value; break; + case 188: V188 = value; break; + case 189: V189 = value; break; + case 190: V190 = value; break; + case 191: V191 = value; break; + case 192: V192 = value; break; + case 193: V193 = value; break; + case 194: V194 = value; break; + case 195: V195 = value; break; + case 196: V196 = value; break; + case 197: V197 = value; break; + case 198: V198 = value; break; + case 199: V199 = value; break; + case 200: V200 = value; break; + case 201: V201 = value; break; + case 202: V202 = value; break; + case 203: V203 = value; break; + case 204: V204 = value; break; + case 205: V205 = value; break; + case 206: V206 = value; break; + case 207: V207 = value; break; + case 208: V208 = value; break; + case 209: V209 = value; break; + case 210: V210 = value; break; + case 211: V211 = value; break; + case 212: V212 = value; break; + case 213: V213 = value; break; + case 214: V214 = value; break; + case 215: V215 = value; break; + case 216: V216 = value; break; + case 217: V217 = value; break; + case 218: V218 = value; break; + case 219: V219 = value; break; + case 220: V220 = value; break; + case 221: V221 = value; break; + case 222: V222 = value; break; + case 223: V223 = value; break; + case 224: V224 = value; break; + case 225: V225 = value; break; + case 226: V226 = value; break; + case 227: V227 = value; break; + case 228: V228 = value; break; + case 229: V229 = value; break; + case 230: V230 = value; break; + case 231: V231 = value; break; + case 232: V232 = value; break; + case 233: V233 = value; break; + case 234: V234 = value; break; + case 235: V235 = value; break; + case 236: V236 = value; break; + case 237: V237 = value; break; + case 238: V238 = value; break; + case 239: V239 = value; break; + case 240: V240 = value; break; + case 241: V241 = value; break; + case 242: V242 = value; break; + case 243: V243 = value; break; + case 244: V244 = value; break; + case 245: V245 = value; break; + case 246: V246 = value; break; + case 247: V247 = value; break; + case 248: V248 = value; break; + case 249: V249 = value; break; + case 250: V250 = value; break; + case 251: V251 = value; break; + case 252: V252 = value; break; + case 253: V253 = value; break; + case 254: V254 = value; break; + case 255: V255 = value; break; + case 256: V256 = value; break; + case 257: V257 = value; break; + case 258: V258 = value; break; + case 259: V259 = value; break; + case 260: V260 = value; break; + case 261: V261 = value; break; + case 262: V262 = value; break; + case 263: V263 = value; break; + case 264: V264 = value; break; + case 265: V265 = value; break; + case 266: V266 = value; break; + case 267: V267 = value; break; + case 268: V268 = value; break; + case 269: V269 = value; break; + case 270: V270 = value; break; + case 271: V271 = value; break; + case 272: V272 = value; break; + case 273: V273 = value; break; + case 274: V274 = value; break; + case 275: V275 = value; break; + case 276: V276 = value; break; + case 277: V277 = value; break; + case 278: V278 = value; break; + case 279: V279 = value; break; + case 280: V280 = value; break; + case 281: V281 = value; break; + case 282: V282 = value; break; + case 283: V283 = value; break; + case 284: V284 = value; break; + case 285: V285 = value; break; + case 286: V286 = value; break; + case 287: V287 = value; break; + case 288: V288 = value; break; + case 289: V289 = value; break; + case 290: V290 = value; break; + case 291: V291 = value; break; + case 292: V292 = value; break; + case 293: V293 = value; break; + case 294: V294 = value; break; + case 295: V295 = value; break; + case 296: V296 = value; break; + case 297: V297 = value; break; + case 298: V298 = value; break; + case 299: V299 = value; break; + case 300: V300 = value; break; + case 301: V301 = value; break; + case 302: V302 = value; break; + case 303: V303 = value; break; + case 304: V304 = value; break; + case 305: V305 = value; break; + case 306: V306 = value; break; + case 307: V307 = value; break; + case 308: V308 = value; break; + case 309: V309 = value; break; + case 310: V310 = value; break; + case 311: V311 = value; break; + case 312: V312 = value; break; + case 313: V313 = value; break; + case 314: V314 = value; break; + case 315: V315 = value; break; + case 316: V316 = value; break; + case 317: V317 = value; break; + case 318: V318 = value; break; + case 319: V319 = value; break; + case 320: V320 = value; break; + case 321: V321 = value; break; + case 322: V322 = value; break; + case 323: V323 = value; break; + case 324: V324 = value; break; + case 325: V325 = value; break; + case 326: V326 = value; break; + case 327: V327 = value; break; + case 328: V328 = value; break; + case 329: V329 = value; break; + case 330: V330 = value; break; + case 331: V331 = value; break; + case 332: V332 = value; break; + case 333: V333 = value; break; + case 334: V334 = value; break; + case 335: V335 = value; break; + case 336: V336 = value; break; + case 337: V337 = value; break; + case 338: V338 = value; break; + case 339: V339 = value; break; + case 340: V340 = value; break; + case 341: V341 = value; break; + case 342: V342 = value; break; + case 343: V343 = value; break; + case 344: V344 = value; break; + case 345: V345 = value; break; + case 346: V346 = value; break; + case 347: V347 = value; break; + case 348: V348 = value; break; + case 349: V349 = value; break; + case 350: V350 = value; break; + case 351: V351 = value; break; + case 352: V352 = value; break; + case 353: V353 = value; break; + case 354: V354 = value; break; + case 355: V355 = value; break; + case 356: V356 = value; break; + case 357: V357 = value; break; + case 358: V358 = value; break; + case 359: V359 = value; break; + case 360: V360 = value; break; + case 361: V361 = value; break; + case 362: V362 = value; break; + case 363: V363 = value; break; + case 364: V364 = value; break; + case 365: V365 = value; break; + case 366: V366 = value; break; + case 367: V367 = value; break; + case 368: V368 = value; break; + case 369: V369 = value; break; + case 370: V370 = value; break; + case 371: V371 = value; break; + case 372: V372 = value; break; + case 373: V373 = value; break; + case 374: V374 = value; break; + case 375: V375 = value; break; + case 376: V376 = value; break; + case 377: V377 = value; break; + case 378: V378 = value; break; + case 379: V379 = value; break; + case 380: V380 = value; break; + case 381: V381 = value; break; + case 382: V382 = value; break; + case 383: V383 = value; break; + case 384: V384 = value; break; + case 385: V385 = value; break; + case 386: V386 = value; break; + case 387: V387 = value; break; + case 388: V388 = value; break; + case 389: V389 = value; break; + case 390: V390 = value; break; + case 391: V391 = value; break; + case 392: V392 = value; break; + case 393: V393 = value; break; + case 394: V394 = value; break; + case 395: V395 = value; break; + case 396: V396 = value; break; + case 397: V397 = value; break; + case 398: V398 = value; break; + case 399: V399 = value; break; + case 400: V400 = value; break; + case 401: V401 = value; break; + case 402: V402 = value; break; + case 403: V403 = value; break; + case 404: V404 = value; break; + case 405: V405 = value; break; + case 406: V406 = value; break; + case 407: V407 = value; break; + case 408: V408 = value; break; + case 409: V409 = value; break; + case 410: V410 = value; break; + case 411: V411 = value; break; + case 412: V412 = value; break; + case 413: V413 = value; break; + case 414: V414 = value; break; + case 415: V415 = value; break; + case 416: V416 = value; break; + case 417: V417 = value; break; + case 418: V418 = value; break; + case 419: V419 = value; break; + case 420: V420 = value; break; + case 421: V421 = value; break; + case 422: V422 = value; break; + case 423: V423 = value; break; + case 424: V424 = value; break; + case 425: V425 = value; break; + case 426: V426 = value; break; + case 427: V427 = value; break; + case 428: V428 = value; break; + case 429: V429 = value; break; + case 430: V430 = value; break; + case 431: V431 = value; break; + case 432: V432 = value; break; + case 433: V433 = value; break; + case 434: V434 = value; break; + case 435: V435 = value; break; + case 436: V436 = value; break; + case 437: V437 = value; break; + case 438: V438 = value; break; + case 439: V439 = value; break; + case 440: V440 = value; break; + case 441: V441 = value; break; + case 442: V442 = value; break; + case 443: V443 = value; break; + case 444: V444 = value; break; + case 445: V445 = value; break; + case 446: V446 = value; break; + case 447: V447 = value; break; + case 448: V448 = value; break; + case 449: V449 = value; break; + case 450: V450 = value; break; + case 451: V451 = value; break; + case 452: V452 = value; break; + case 453: V453 = value; break; + case 454: V454 = value; break; + case 455: V455 = value; break; + case 456: V456 = value; break; + case 457: V457 = value; break; + case 458: V458 = value; break; + case 459: V459 = value; break; + case 460: V460 = value; break; + case 461: V461 = value; break; + case 462: V462 = value; break; + case 463: V463 = value; break; + case 464: V464 = value; break; + case 465: V465 = value; break; + case 466: V466 = value; break; + case 467: V467 = value; break; + case 468: V468 = value; break; + case 469: V469 = value; break; + case 470: V470 = value; break; + case 471: V471 = value; break; + case 472: V472 = value; break; + case 473: V473 = value; break; + case 474: V474 = value; break; + case 475: V475 = value; break; + case 476: V476 = value; break; + case 477: V477 = value; break; + case 478: V478 = value; break; + case 479: V479 = value; break; + case 480: V480 = value; break; + case 481: V481 = value; break; + case 482: V482 = value; break; + case 483: V483 = value; break; + case 484: V484 = value; break; + case 485: V485 = value; break; + case 486: V486 = value; break; + case 487: V487 = value; break; + case 488: V488 = value; break; + case 489: V489 = value; break; + case 490: V490 = value; break; + case 491: V491 = value; break; + case 492: V492 = value; break; + case 493: V493 = value; break; + case 494: V494 = value; break; + case 495: V495 = value; break; + case 496: V496 = value; break; + case 497: V497 = value; break; + case 498: V498 = value; break; + case 499: V499 = value; break; + case 500: V500 = value; break; + case 501: V501 = value; break; + case 502: V502 = value; break; + case 503: V503 = value; break; + case 504: V504 = value; break; + case 505: V505 = value; break; + case 506: V506 = value; break; + case 507: V507 = value; break; + case 508: V508 = value; break; + case 509: V509 = value; break; + case 510: V510 = value; break; + case 511: V511 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray64.cs b/src/DotRecast.Core/Collections/RcStackArray64.cs new file mode 100644 index 00000000..5aa88a31 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray64.cs @@ -0,0 +1,229 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray64 + { + public static RcStackArray64 Empty => new RcStackArray64(); + + private const int Size = 64; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + public T V8; + public T V9; + public T V10; + public T V11; + public T V12; + public T V13; + public T V14; + public T V15; + public T V16; + public T V17; + public T V18; + public T V19; + public T V20; + public T V21; + public T V22; + public T V23; + public T V24; + public T V25; + public T V26; + public T V27; + public T V28; + public T V29; + public T V30; + public T V31; + public T V32; + public T V33; + public T V34; + public T V35; + public T V36; + public T V37; + public T V38; + public T V39; + public T V40; + public T V41; + public T V42; + public T V43; + public T V44; + public T V45; + public T V46; + public T V47; + public T V48; + public T V49; + public T V50; + public T V51; + public T V52; + public T V53; + public T V54; + public T V55; + public T V56; + public T V57; + public T V58; + public T V59; + public T V60; + public T V61; + public T V62; + public T V63; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + 8 => V8, + 9 => V9, + 10 => V10, + 11 => V11, + 12 => V12, + 13 => V13, + 14 => V14, + 15 => V15, + 16 => V16, + 17 => V17, + 18 => V18, + 19 => V19, + 20 => V20, + 21 => V21, + 22 => V22, + 23 => V23, + 24 => V24, + 25 => V25, + 26 => V26, + 27 => V27, + 28 => V28, + 29 => V29, + 30 => V30, + 31 => V31, + 32 => V32, + 33 => V33, + 34 => V34, + 35 => V35, + 36 => V36, + 37 => V37, + 38 => V38, + 39 => V39, + 40 => V40, + 41 => V41, + 42 => V42, + 43 => V43, + 44 => V44, + 45 => V45, + 46 => V46, + 47 => V47, + 48 => V48, + 49 => V49, + 50 => V50, + 51 => V51, + 52 => V52, + 53 => V53, + 54 => V54, + 55 => V55, + 56 => V56, + 57 => V57, + 58 => V58, + 59 => V59, + 60 => V60, + 61 => V61, + 62 => V62, + 63 => V63, + _ => throw new ArgumentOutOfRangeException(nameof(index), index, null) + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + case 8: V8 = value; break; + case 9: V9 = value; break; + case 10: V10 = value; break; + case 11: V11 = value; break; + case 12: V12 = value; break; + case 13: V13 = value; break; + case 14: V14 = value; break; + case 15: V15 = value; break; + case 16: V16 = value; break; + case 17: V17 = value; break; + case 18: V18 = value; break; + case 19: V19 = value; break; + case 20: V20 = value; break; + case 21: V21 = value; break; + case 22: V22 = value; break; + case 23: V23 = value; break; + case 24: V24 = value; break; + case 25: V25 = value; break; + case 26: V26 = value; break; + case 27: V27 = value; break; + case 28: V28 = value; break; + case 29: V29 = value; break; + case 30: V30 = value; break; + case 31: V31 = value; break; + case 32 : V32 = value; break; + case 33 : V33 = value; break; + case 34 : V34 = value; break; + case 35 : V35 = value; break; + case 36 : V36 = value; break; + case 37 : V37 = value; break; + case 38 : V38 = value; break; + case 39 : V39 = value; break; + case 40 : V40 = value; break; + case 41 : V41 = value; break; + case 42 : V42 = value; break; + case 43 : V43 = value; break; + case 44 : V44 = value; break; + case 45 : V45 = value; break; + case 46 : V46 = value; break; + case 47 : V47 = value; break; + case 48 : V48 = value; break; + case 49 : V49 = value; break; + case 50 : V50 = value; break; + case 51 : V51 = value; break; + case 52 : V52 = value; break; + case 53 : V53 = value; break; + case 54 : V54 = value; break; + case 55 : V55 = value; break; + case 56 : V56 = value; break; + case 57 : V57 = value; break; + case 58 : V58 = value; break; + case 59 : V59 = value; break; + case 60 : V60 = value; break; + case 61 : V61 = value; break; + case 62 : V62 = value; break; + case 63 : V63 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Collections/RcStackArray8.cs b/src/DotRecast.Core/Collections/RcStackArray8.cs new file mode 100644 index 00000000..75e1b154 --- /dev/null +++ b/src/DotRecast.Core/Collections/RcStackArray8.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Collections +{ + public struct RcStackArray8 + { + public static RcStackArray8 Empty => new RcStackArray8(); + + private const int Size = 8; + public int Length => Size; + + public T V0; + public T V1; + public T V2; + public T V3; + public T V4; + public T V5; + public T V6; + public T V7; + + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + return index switch + { + 0 => V0, + 1 => V1, + 2 => V2, + 3 => V3, + 4 => V4, + 5 => V5, + 6 => V6, + 7 => V7, + _ => throw new IndexOutOfRangeException($"{index}") + }; + } + + set + { + RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); + + switch (index) + { + case 0: V0 = value; break; + case 1: V1 = value; break; + case 2: V2 = value; break; + case 3: V3 = value; break; + case 4: V4 = value; break; + case 5: V5 = value; break; + case 6: V6 = value; break; + case 7: V7 = value; break; + } + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Compression/FastLZ.cs b/src/DotRecast.Core/Compression/FastLZ.cs new file mode 100644 index 00000000..abf4d834 --- /dev/null +++ b/src/DotRecast.Core/Compression/FastLZ.cs @@ -0,0 +1,663 @@ +/* + FastLZ - Byte-aligned LZ77 compression library + Copyright (C) 2005-2020 Ariya Hidayat + Copyright (C) 2023 Choi Ikpil https://github.com/ikpil/DotFastLZ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +using System; + +namespace DotRecast.Core.Compression +{ + public static class FastLZ + { + public const int FASTLZ_VERSION = 0x000500; + public const int FASTLZ_VERSION_MAJOR = 0; + public const int FASTLZ_VERSION_MINOR = 5; + public const int FASTLZ_VERSION_REVISION = 0; + public const string FASTLZ_VERSION_STRING = "0.5.0"; + + public const int MAX_COPY = 32; + public const int MAX_LEN = 264; /* 256 + 8 */ + public const int MAX_L1_DISTANCE = 8192; + public const int MAX_L2_DISTANCE = 8191; + public const int MAX_FARDISTANCE = (65535 + MAX_L2_DISTANCE - 1); + public const int HASH_LOG = 13; + public const int HASH_SIZE = (1 << HASH_LOG); + public const int HASH_MASK = (HASH_SIZE - 1); + + /** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. + + Compression level can be specified in parameter level. At the moment, + only level 1 and level 2 are supported. + Level 1 is the fastest compression and generally useful for short data. + Level 2 is slightly slower but it gives better compression ratio. + + Note that the compressed data, regardless of the level, can always be + decompressed using the function fastlz_decompress below. + */ + // fastlz_compress + public static long CompressLevel(int level, byte[] input, long length, byte[] output) + { + return CompressLevel(level, input, 0, length, output); + } + + public static long CompressLevel(int level, byte[] input, long inputOffset, long length, byte[] output) + { + if (level == 1) + { + return CompressLevel1(input, inputOffset, length, output); + } + + if (level == 2) + { + return CompressLevel2(input, inputOffset, length, output); + } + + throw new Exception($"invalid level: {level} (expected: 1 or 2)"); + } + + // fastlz1_compress + public static long CompressLevel1(byte[] input, long inputOffset, long length, byte[] output) + { + long ip = inputOffset; + long ip_start = ip; + long ip_bound = ip + length - 4; + long ip_limit = ip + length - 12 - 1; + + long op = 0; + + long[] htab = new long[HASH_SIZE]; + long seq, hash; + + // Initializes hash table + for (hash = 0; hash < HASH_SIZE; ++hash) + { + htab[hash] = 0; + } + + // We start with literal copy + long anchor = ip; + ip += 2; + + // Main loop + while (ip < ip_limit) + { + long refIdx; + long distance, cmp; + + // Find potential match + do + { + seq = ReadUInt32(input, ip) & 0xffffff; + hash = Hash(seq); + refIdx = ip_start + htab[hash]; + htab[hash] = ip - ip_start; + distance = ip - refIdx; + cmp = distance < MAX_L1_DISTANCE + ? ReadUInt32(input, refIdx) & 0xffffff + : 0x1000000; + + if (ip >= ip_limit) + { + break; + } + + ++ip; + } while (seq != cmp); + + if (ip >= ip_limit) + { + break; + } + + --ip; + + if (ip > anchor) + { + op = Literals(ip - anchor, input, anchor, output, op); + } + + long len = MemCompare(input, refIdx + 3, input, ip + 3, ip_bound); + op = MatchLevel1(len, distance, output, op); + + // Update the hash at the match boundary + ip += len; + seq = ReadUInt32(input, ip); + hash = Hash(seq & 0xffffff); + htab[hash] = ip++ - ip_start; + seq >>= 8; + hash = Hash(seq); + htab[hash] = ip++ - ip_start; + + anchor = ip; + } + + long copy = length - anchor; + op = Literals(copy, input, anchor, output, op); + return op; + } + + // fastlz2_compress + public static long CompressLevel2(byte[] input, long inputOffset, long length, byte[] output) + { + long ip = inputOffset; + long ip_start = ip; + long ip_bound = ip + length - 4; /* because readU32 */ + long ip_limit = ip + length - 12 - 1; + + long op = 0; + + long[] htab = new long[HASH_SIZE]; + long seq, hash; + + /* initializes hash table */ + for (hash = 0; hash < HASH_SIZE; ++hash) + { + htab[hash] = 0; + } + + /* we start with literal copy */ + long anchor = ip; + ip += 2; + + /* main loop */ + while (ip < ip_limit) + { + long refs; + long distance, cmp; + + /* find potential match */ + do + { + seq = ReadUInt32(input, ip) & 0xffffff; + hash = Hash(seq); + refs = ip_start + htab[hash]; + htab[hash] = ip - ip_start; + distance = ip - refs; + cmp = distance < MAX_FARDISTANCE + ? ReadUInt32(input, refs) & 0xffffff + : 0x1000000; + + if (ip >= ip_limit) + { + break; + } + + ++ip; + } while (seq != cmp); + + if (ip >= ip_limit) + { + break; + } + + --ip; + + /* far, needs at least 5-byte match */ + if (distance >= MAX_L2_DISTANCE) + { + if (input[refs + 3] != input[ip + 3] || input[refs + 4] != input[ip + 4]) + { + ++ip; + continue; + } + } + + if (ip > anchor) + { + op = Literals(ip - anchor, input, anchor, output, op); + } + + long len = MemCompare(input, refs + 3, input, ip + 3, ip_bound); + op = MatchLevel2(len, distance, output, op); + + /* update the hash at match boundary */ + ip += len; + seq = ReadUInt32(input, ip); + hash = Hash(seq & 0xffffff); + htab[hash] = ip++ - ip_start; + seq >>= 8; + hash = Hash(seq); + htab[hash] = ip++ - ip_start; + + anchor = ip; + } + + long copy = length - anchor; + op = Literals(copy, input, anchor, output, op); + + /* marker for fastlz2 */ + output[inputOffset] |= (1 << 5); + + return op; + } + + /** + Decompress a block of compressed data and returns the size of the + decompressed block. If error occurs, e.g. the compressed data is + corrupted or the output buffer is not large enough, then 0 (zero) + will be returned instead. + + The input buffer and the output buffer can not overlap. + + Decompression is memory safe and guaranteed not to write the output buffer + more than what is specified in maxout. + + Note that the decompression will always work, regardless of the + compression level specified in fastlz_compress_level above (when + producing the compressed block). + */ + // fastlz_decompress + public static long Decompress(byte[] input, long length, byte[] output, long maxout) + { + return Decompress(input, 0, length, output, 0, maxout); + } + + public static long Decompress(byte[] input, long inputOffset, long length, byte[] output, long outputOffset, long maxout) + { + /* magic identifier for compression level */ + int level = (input[inputOffset] >> 5) + 1; + + if (level == 1) + { + return DecompressLevel1(input, inputOffset, length, output, outputOffset, maxout); + } + + if (level == 2) + { + return DecompressLevel2(input, inputOffset, length, output, outputOffset, maxout); + } + + throw new Exception($"invalid level: {level} (expected: 1 or 2)"); + } + + // fastlz1_decompress + public static long DecompressLevel1(byte[] input, long inputOffset, long length, byte[] output, long outputOffset, long maxout) + { + long ip = inputOffset; + long ip_limit = ip + length; + long ip_bound = ip_limit - 2; + + long op = outputOffset; + long op_limit = op + maxout; + long ctrl = input[ip++] & 31; + + while (true) + { + if (ctrl >= 32) + { + long len = (ctrl >> 5) - 1; + long ofs = (ctrl & 31) << 8; + long refs = op - ofs - 1; + if (len == 7 - 1) + { + if (!(ip <= ip_bound)) + { + return 0; + } + + len += input[ip++]; + } + + refs -= input[ip++]; + len += 3; + if (!(op + len <= op_limit)) + { + return 0; + } + + if (!(refs >= outputOffset)) + { + return 0; + } + + MemMove(output, op, output, refs, len); + op += len; + } + else + { + ctrl++; + if (!(op + ctrl <= op_limit)) + { + return 0; + } + + if (!(ip + ctrl <= ip_limit)) + { + return 0; + } + + RcArrays.Copy(input, ip, output, op, ctrl); + ip += ctrl; + op += ctrl; + } + + if (ip > ip_bound) + { + break; + } + + ctrl = input[ip++]; + } + + return op; + } + + // fastlz2_decompress + public static long DecompressLevel2(byte[] input, long inputOffset, long length, byte[] output, long outputOffset, long maxout) + { + long ip = inputOffset; + long ip_limit = ip + length; + long ip_bound = ip_limit - 2; + + long op = outputOffset; + long op_limit = op + maxout; + long ctrl = input[ip++] & 31; + + while (true) + { + if (ctrl >= 32) + { + long len = (ctrl >> 5) - 1; + long ofs = (ctrl & 31) << 8; + long refIdx = op - ofs - 1; + + long code; + if (len == 7 - 1) + { + do + { + if (!(ip <= ip_bound)) + { + return 0; + } + + code = input[ip++]; + len += code; + } while (code == 255); + } + + code = input[ip++]; + refIdx -= code; + len += 3; + + /* match from 16-bit distance */ + if (code == 255) + { + if (ofs == (31 << 8)) + { + if (!(ip < ip_bound)) + { + return 0; + } + + ofs = input[ip++] << 8; + ofs += input[ip++]; + refIdx = op - ofs - MAX_L2_DISTANCE - 1; + } + } + + if (!(op + len <= op_limit)) + { + return 0; + } + + if (!(refIdx >= outputOffset)) + { + return 0; + } + + MemMove(output, op, output, refIdx, len); + op += len; + } + else + { + ctrl++; + if (!(op + ctrl <= op_limit)) + { + return 0; + } + + if (!(ip + ctrl <= ip_limit)) + { + return 0; + } + + RcArrays.Copy(input, ip, output, op, ctrl); + ip += ctrl; + op += ctrl; + } + + if (ip >= ip_limit) + { + break; + } + + ctrl = input[ip++]; + } + + return op; + } + + // flz_readu32 + public static uint ReadUInt32(byte[] data, long offset) + { + return ((uint)data[offset + 3] & 0xff) << 24 | + ((uint)data[offset + 2] & 0xff) << 16 | + ((uint)data[offset + 1] & 0xff) << 8 | + ((uint)data[offset + 0] & 0xff); + } + + public static ushort ReadUInt16(byte[] data, long offset) + { + var u16 = ((uint)data[offset + 1] << 8) | + ((uint)data[offset + 0]); + return (ushort)u16; + } + + // flz_hash + public static ushort Hash(long v) + { + ulong h = ((ulong)v * 2654435769UL) >> (32 - HASH_LOG); + return (ushort)(h & HASH_MASK); + } + + // special case of memcpy: at most MAX_COPY bytes + // flz_smallcopy + public static void SmallCopy(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) + { + // if (count >= 4) + // { + // count -= count % 4; + // RcArrays.Copy(src, srcOffset, dest, destOffset, count); + // } + RcArrays.Copy(src, srcOffset, dest, destOffset, count); + } + + // special case of memcpy: exactly MAX_COPY bytes + // flz_maxcopy + static void MaxCopy(byte[] dest, long destOffset, byte[] src, long secOffset) + { + RcArrays.Copy(src, secOffset, dest, destOffset, MAX_COPY); + } + + // flz_literals + public static long Literals(long runs, byte[] src, long srcOffset, byte[] dest, long destOffset) + { + while (runs >= MAX_COPY) + { + dest[destOffset++] = MAX_COPY - 1; + MaxCopy(dest, destOffset, src, srcOffset); + srcOffset += MAX_COPY; + destOffset += MAX_COPY; + runs -= MAX_COPY; + } + + if (runs > 0) + { + dest[destOffset++] = (byte)(runs - 1); + SmallCopy(dest, destOffset, src, srcOffset, runs); + destOffset += runs; + } + + return destOffset; + } + + // flz1_match + public static long MatchLevel1(long len, long distance, byte[] output, long op) + { + --distance; + if (len > MAX_LEN - 2) + { + while (len > MAX_LEN - 2) + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + output[op++] = (byte)(MAX_LEN - 2 - 7 - 2); + output[op++] = (byte)(distance & 255); + len -= MAX_LEN - 2; + } + } + + if (len < 7) + { + output[op++] = (byte)((len << 5) + (distance >> 8)); + output[op++] = (byte)(distance & 255); + } + else + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + output[op++] = (byte)(len - 7); + output[op++] = (byte)((distance & 255)); + } + + return op; + } + + // flz2_match + public static long MatchLevel2(long len, long distance, byte[] output, long op) + { + --distance; + if (distance < MAX_L2_DISTANCE) + { + if (len < 7) + { + output[op++] = (byte)((len << 5) + (distance >> 8)); + output[op++] = (byte)((distance & 255)); + } + else + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + for (len -= 7; len >= 255; len -= 255) + { + output[op++] = 255; + } + + output[op++] = (byte)(len); + output[op++] = (byte)((distance & 255)); + } + } + else + { + /* far away, but not yet in the another galaxy... */ + if (len < 7) + { + distance -= MAX_L2_DISTANCE; + output[op++] = (byte)((len << 5) + 31); + output[op++] = (byte)(255); + output[op++] = (byte)(distance >> 8); + output[op++] = (byte)(distance & 255); + } + else + { + distance -= MAX_L2_DISTANCE; + output[op++] = (7 << 5) + 31; + for (len -= 7; len >= 255; len -= 255) + { + output[op++] = 255; + } + + output[op++] = (byte)(len); + output[op++] = (byte)(255); + output[op++] = (byte)(distance >> 8); + output[op++] = (byte)(distance & 255); + } + } + + return op; + } + + // flz_cmp + public static long MemCompare(byte[] p, long pOffset, byte[] q, long qOffset, long r) + { + long start = pOffset; + + if (4 <= r && ReadUInt32(p, pOffset) == ReadUInt32(q, qOffset)) + { + pOffset += 4; + qOffset += 4; + } + + while (qOffset < r) + { + if (p[pOffset++] != q[qOffset++]) + break; + } + + return pOffset - start; + } + + // fastlz_memmove + public static void MemMove(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) + { + if (dest.Length < destOffset + count) + { + throw new IndexOutOfRangeException($"{dest.Length} < {destOffset} + {count}"); + } + + if (src.Length < srcOffset + count) + { + throw new IndexOutOfRangeException($"{src.Length} < {srcOffset} + {count}"); + } + + for (long i = 0; i < count; ++i) + { + dest[destOffset + i] = src[srcOffset + i]; + } + } + + public static long EstimateCompressedSize(long size) + { + long estimatedSize = (long)Math.Ceiling(size * 1.06f); + return Math.Max(estimatedSize, 66); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/DotRecast.Core.csproj b/src/DotRecast.Core/DotRecast.Core.csproj index 149d35d6..1feedf37 100644 --- a/src/DotRecast.Core/DotRecast.Core.csproj +++ b/src/DotRecast.Core/DotRecast.Core.csproj @@ -1,12 +1,22 @@ - - netstandard2.1 - + + netstandard2.1;net6.0;net7.0;net8.0 + DotRecast.Core + README.md + ikpil + DotRecast - a port of Recast Detour, Industry-standard navigation mesh toolset for .NET, C#, Unity3D, games, servers + git + https://github.com/ikpil/DotRecast + https://github.com/ikpil/DotRecast + game gamedev ai csharp server unity navigation game-development unity3d pathfinding pathfinder recast detour navmesh crowd-simulation recastnavigation + https://github.com/ikpil/DotRecast/blob/main/CHANGELOG.md + true + + + + + - - - - diff --git a/src/DotRecast.Core/FRand.cs b/src/DotRecast.Core/FRand.cs deleted file mode 100644 index 3acee4c3..00000000 --- a/src/DotRecast.Core/FRand.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace DotRecast.Core -{ - public class FRand - { - private readonly Random _r; - - public FRand() - { - _r = new Random(); - } - - public FRand(long seed) - { - _r = new Random((int)seed); // TODO : 랜덤 시드 확인 필요 - } - - public float Next() - { - return (float)_r.NextDouble(); - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour.TileCache/IDtTileCacheCompressor.cs b/src/DotRecast.Core/IRcCompressor.cs similarity index 86% rename from src/DotRecast.Detour.TileCache/IDtTileCacheCompressor.cs rename to src/DotRecast.Core/IRcCompressor.cs index 6efbaa20..ef0bea7d 100644 --- a/src/DotRecast.Detour.TileCache/IDtTileCacheCompressor.cs +++ b/src/DotRecast.Core/IRcCompressor.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -18,10 +18,11 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ -namespace DotRecast.Detour.TileCache +namespace DotRecast.Core { - public interface IDtTileCacheCompressor + public interface IRcCompressor { + byte[] Decompress(byte[] data); byte[] Decompress(byte[] buf, int offset, int len, int outputlen); byte[] Compress(byte[] buf); diff --git a/src/DotRecast.Core/IRcRand.cs b/src/DotRecast.Core/IRcRand.cs new file mode 100644 index 00000000..15e810a1 --- /dev/null +++ b/src/DotRecast.Core/IRcRand.cs @@ -0,0 +1,9 @@ +namespace DotRecast.Core +{ + public interface IRcRand + { + float Next(); + double NextDouble(); + int NextInt32(); + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/IntersectResult.cs b/src/DotRecast.Core/IntersectResult.cs deleted file mode 100644 index 1bf8ba4d..00000000 --- a/src/DotRecast.Core/IntersectResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace DotRecast.Core -{ - public class IntersectResult - { - public bool intersects; - public float tmin; - public float tmax = 1f; - public int segMin = -1; - public int segMax = -1; - } -} \ No newline at end of file diff --git a/src/DotRecast.Core/Loader.cs b/src/DotRecast.Core/Loader.cs deleted file mode 100644 index 67d6fdc3..00000000 --- a/src/DotRecast.Core/Loader.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.IO; - -namespace DotRecast.Core -{ - public static class Loader - { - public static byte[] ToBytes(string filename) - { - var filepath = ToRPath(filename); - using var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read); - byte[] buffer = new byte[fs.Length]; - fs.Read(buffer, 0, buffer.Length); - - return buffer; - } - - public static string ToRPath(string filename) - { - string filePath = Path.Combine("resources", filename); - for (int i = 0; i < 10; ++i) - { - if (File.Exists(filePath)) - { - return Path.GetFullPath(filePath); - } - - filePath = Path.Combine("..", filePath); - } - - return Path.GetFullPath(filename); - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs b/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs new file mode 100644 index 00000000..2a09decb --- /dev/null +++ b/src/DotRecast.Core/Numerics/RcMatrix4x4f.cs @@ -0,0 +1,220 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Numerics +{ + public struct RcMatrix4x4f + { + private static readonly RcMatrix4x4f _identity = new RcMatrix4x4f + ( + 1f, 0f, 0f, 0f, + 0f, 1f, 0f, 0f, + 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f + ); + + public float M11; // 0 + public float M12; // 1 + public float M13; // 2 + public float M14; // 3 + public float M21; // 4 + public float M22; // 5 + public float M23; // 6 + public float M24; // 7 + public float M31; // 8 + public float M32; // 9 + public float M33; // 10 + public float M34; // 11 + public float M41; // 12 + public float M42; // 13 + public float M43; // 14 + public float M44; // 15 + + public RcMatrix4x4f( + float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44) + { + M11 = m11; + M12 = m12; + M13 = m13; + M14 = m14; + + M21 = m21; + M22 = m22; + M23 = m23; + M24 = m24; + + M31 = m31; + M32 = m32; + M33 = m33; + M34 = m34; + + M41 = m41; + M42 = m42; + M43 = m43; + M44 = m44; + } + + public RcMatrix4x4f(float[] m) + { + M11 = m[0]; + M12 = m[1]; + M13 = m[2]; + M14 = m[3]; + M21 = m[4]; + M22 = m[5]; + M23 = m[6]; + M24 = m[7]; + M31 = m[8]; + M32 = m[9]; + M33 = m[10]; + M34 = m[11]; + M41 = m[12]; + M42 = m[13]; + M43 = m[14]; + M44 = m[15]; + } + + + public void CopyTo(float[] m) + { + m[0] = M11; + m[1] = M12; + m[2] = M13; + m[3] = M14; + m[4] = M21; + m[5] = M22; + m[6] = M23; + m[7] = M24; + m[8] = M31; + m[9] = M32; + m[10] = M33; + m[11] = M34; + m[12] = M41; + m[13] = M42; + m[14] = M43; + m[15] = M44; + } + + + public static RcMatrix4x4f Identity => _identity; + + public readonly bool IsIdentity => + M11.Equals(1f) && M22.Equals(1f) && M33.Equals(1f) && M44.Equals(1f) && + M12 == 0f && M13 == 0f && M14 == 0f && + M21 == 0f && M23 == 0f && M24 == 0f && + M31 == 0f && M32 == 0f && M34 == 0f && + M41 == 0f && M42 == 0f && M43 == 0f; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcMatrix4x4f Mul(ref RcMatrix4x4f left, ref RcMatrix4x4f right) + { + float m11 = left.M11 * right.M11 + left.M21 * right.M12 + left.M31 * right.M13 + left.M41 * right.M14; + float m12 = left.M12 * right.M11 + left.M22 * right.M12 + left.M32 * right.M13 + left.M42 * right.M14; + float m13 = left.M13 * right.M11 + left.M23 * right.M12 + left.M33 * right.M13 + left.M43 * right.M14; + float m14 = left.M14 * right.M11 + left.M24 * right.M12 + left.M34 * right.M13 + left.M44 * right.M14; + float m21 = left.M11 * right.M21 + left.M21 * right.M22 + left.M31 * right.M23 + left.M41 * right.M24; + float m22 = left.M12 * right.M21 + left.M22 * right.M22 + left.M32 * right.M23 + left.M42 * right.M24; + float m23 = left.M13 * right.M21 + left.M23 * right.M22 + left.M33 * right.M23 + left.M43 * right.M24; + float m24 = left.M14 * right.M21 + left.M24 * right.M22 + left.M34 * right.M23 + left.M44 * right.M24; + float m31 = left.M11 * right.M31 + left.M21 * right.M32 + left.M31 * right.M33 + left.M41 * right.M34; + float m32 = left.M12 * right.M31 + left.M22 * right.M32 + left.M32 * right.M33 + left.M42 * right.M34; + float m33 = left.M13 * right.M31 + left.M23 * right.M32 + left.M33 * right.M33 + left.M43 * right.M34; + float m34 = left.M14 * right.M31 + left.M24 * right.M32 + left.M34 * right.M33 + left.M44 * right.M34; + float m41 = left.M11 * right.M41 + left.M21 * right.M42 + left.M31 * right.M43 + left.M41 * right.M44; + float m42 = left.M12 * right.M41 + left.M22 * right.M42 + left.M32 * right.M43 + left.M42 * right.M44; + float m43 = left.M13 * right.M41 + left.M23 * right.M42 + left.M33 * right.M43 + left.M43 * right.M44; + float m44 = left.M14 * right.M41 + left.M24 * right.M42 + left.M34 * right.M43 + left.M44 * right.M44; + + RcMatrix4x4f dest = new RcMatrix4x4f(); + dest.M11 = m11; + dest.M12 = m12; + dest.M13 = m13; + dest.M14 = m14; + dest.M21 = m21; + dest.M22 = m22; + dest.M23 = m23; + dest.M24 = m24; + dest.M31 = m31; + dest.M32 = m32; + dest.M33 = m33; + dest.M34 = m34; + dest.M41 = m41; + dest.M42 = m42; + dest.M43 = m43; + dest.M44 = m44; + + return dest; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcMatrix4x4f Mul(float[] left, float[] right) + { + float m00 = left[0] * right[0] + left[4] * right[1] + left[8] * right[2] + left[12] * right[3]; + float m01 = left[1] * right[0] + left[5] * right[1] + left[9] * right[2] + left[13] * right[3]; + float m02 = left[2] * right[0] + left[6] * right[1] + left[10] * right[2] + left[14] * right[3]; + float m03 = left[3] * right[0] + left[7] * right[1] + left[11] * right[2] + left[15] * right[3]; + float m10 = left[0] * right[4] + left[4] * right[5] + left[8] * right[6] + left[12] * right[7]; + float m11 = left[1] * right[4] + left[5] * right[5] + left[9] * right[6] + left[13] * right[7]; + float m12 = left[2] * right[4] + left[6] * right[5] + left[10] * right[6] + left[14] * right[7]; + float m13 = left[3] * right[4] + left[7] * right[5] + left[11] * right[6] + left[15] * right[7]; + float m20 = left[0] * right[8] + left[4] * right[9] + left[8] * right[10] + left[12] * right[11]; + float m21 = left[1] * right[8] + left[5] * right[9] + left[9] * right[10] + left[13] * right[11]; + float m22 = left[2] * right[8] + left[6] * right[9] + left[10] * right[10] + left[14] * right[11]; + float m23 = left[3] * right[8] + left[7] * right[9] + left[11] * right[10] + left[15] * right[11]; + float m30 = left[0] * right[12] + left[4] * right[13] + left[8] * right[14] + left[12] * right[15]; + float m31 = left[1] * right[12] + left[5] * right[13] + left[9] * right[14] + left[13] * right[15]; + float m32 = left[2] * right[12] + left[6] * right[13] + left[10] * right[14] + left[14] * right[15]; + float m33 = left[3] * right[12] + left[7] * right[13] + left[11] * right[14] + left[15] * right[15]; + + return new RcMatrix4x4f( + m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33 + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcMatrix4x4f CreateFromRotate(float a, float x, float y, float z) + { + var matrix = new RcMatrix4x4f(); + a = (float)(a * MathF.PI / 180.0); // convert to radians + float s = MathF.Sin(a); + float c = MathF.Cos(a); + float t = 1.0f - c; + + float tx = t * x; + float ty = t * y; + float tz = t * z; + + float sz = s * z; + float sy = s * y; + float sx = s * x; + + matrix.M11 = tx * x + c; + matrix.M12 = tx * y + sz; + matrix.M13 = tx * z - sy; + matrix.M14 = 0; + + matrix.M21 = tx * y - sz; + matrix.M22 = ty * y + c; + matrix.M23 = ty * z + sx; + matrix.M24 = 0; + + matrix.M31 = tx * z + sy; + matrix.M32 = ty * z - sx; + matrix.M33 = tz * z + c; + matrix.M34 = 0; + + matrix.M41 = 0; + matrix.M42 = 0; + matrix.M43 = 0; + matrix.M44 = 1; + + return matrix; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Numerics/RcVec.cs b/src/DotRecast.Core/Numerics/RcVec.cs new file mode 100644 index 00000000..aa847eec --- /dev/null +++ b/src/DotRecast.Core/Numerics/RcVec.cs @@ -0,0 +1,284 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Numerics +{ + public static class RcVec + { + public const float EPSILON = 1e-6f; + public static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Create(Span values, int n) + { + return new RcVec3f(values[n + 0], values[n + 1], values[n + 2]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Get(this RcVec2f v, int i) + { + switch (i) + { + case 0: return v.X; + case 1: return v.Y; + default: throw new IndexOutOfRangeException("vector2f index out of range"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Get(this RcVec3f v, int i) + { + switch (i) + { + case 0: return v.X; + case 1: return v.Y; + case 2: return v.Z; + default: throw new IndexOutOfRangeException("vector3f index out of range"); + } + } + + /// Performs a 'sloppy' colocation check of the specified points. + /// @param[in] p0 A point. [(x, y, z)] + /// @param[in] p1 A point. [(x, y, z)] + /// @return True if the points are considered to be at the same location. + /// + /// Basically, this function will return true if the specified points are + /// close enough to eachother to be considered colocated. + public static bool Equal(RcVec3f p0, RcVec3f p1) + { + 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) + /// @param[in] u A vector [(x, y, z)] + /// @param[in] v A vector [(x, y, z)] + /// @return The dot product on the xz-plane. + /// + /// The vectors are projected onto the xz-plane, so the y-values are + /// ignored. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot2D(this RcVec3f @this, RcVec3f v) + { + return @this.X * v.X + + @this.Z * v.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Cross(float[] dest, float[] v1, float[] v2) + { + dest[0] = v1[1] * v2[2] - v1[2] * v2[1]; + dest[1] = v1[2] * v2[0] - v1[0] * v2[2]; + dest[2] = v1[0] * v2[1] - v1[1] * v2[0]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(float[] @out, int n, float[] @in, int m) + { + @out[n + 0] = @in[m + 0]; + @out[n + 1] = @in[m + 1]; + @out[n + 2] = @in[m + 2]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(Span @out, int n, Span @in, int m) + { + @out[n + 0] = @in[m + 0]; + @out[n + 1] = @in[m + 1]; + @out[n + 2] = @in[m + 2]; + } + + /// Returns the distance between two points. + /// @param[in] v1 A point. [(x, y, z)] + /// @param[in] v2 A point. [(x, y, z)] + /// @return The distance between the two points. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistanceSquared(RcVec3f v1, float[] v2, int i) + { + float dx = v2[i] - v1.X; + float dy = v2[i + 1] - v1.Y; + float dz = v2[i + 2] - v1.Z; + return dx * dx + dy * dy + dz * dz; + } + + /// Normalizes the vector if the length is greater than zero. + /// If the magnitude is zero, the vector is unchanged. + /// @param[in,out] v The vector to normalize. [(x, y, z)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f SafeNormalize(RcVec3f v) + { + float sqMag = RcMath.Sqr(v.X) + RcMath.Sqr(v.Y) + RcMath.Sqr(v.Z); + if (sqMag > EPSILON) + { + float inverseMag = 1.0f / MathF.Sqrt(sqMag); + return new RcVec3f( + v.X *= inverseMag, + v.Y *= inverseMag, + v.Z *= inverseMag + ); + } + + return v; + } + + /// Derives the distance between the specified points on the xz-plane. + /// @param[in] v1 A point. [(x, y, z)] + /// @param[in] v2 A point. [(x, y, z)] + /// @return The distance between the point on the xz-plane. + /// + /// The vectors are projected onto the xz-plane, so the y-values are + /// ignored. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dist2D(RcVec3f v1, RcVec3f v2) + { + float dx = v2.X - v1.X; + float dz = v2.Z - v1.Z; + return (float)MathF.Sqrt(dx * dx + dz * dz); + } + + /// Derives the square of the distance between the specified points on the xz-plane. + /// @param[in] v1 A point. [(x, y, z)] + /// @param[in] v2 A point. [(x, y, z)] + /// @return The square of the distance between the point on the xz-plane. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dist2DSqr(RcVec3f v1, RcVec3f v2) + { + float dx = v2.X - v1.X; + float dz = v2.Z - v1.Z; + return dx * dx + dz * dz; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dist2DSqr(RcVec3f p, float[] verts, int i) + { + float dx = verts[i] - p.X; + float dz = verts[i + 2] - p.Z; + return dx * dx + dz * dz; + } + + /// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz) + /// @param[in] u The LHV vector [(x, y, z)] + /// @param[in] v The RHV vector [(x, y, z)] + /// @return The dot product on the xz-plane. + /// + /// The vectors are projected onto the xz-plane, so the y-values are + /// ignored. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Perp2D(RcVec3f u, RcVec3f v) + { + return u.Z * v.X - u.X * v.Z; + } + + /// Checks that the specified vector's components are all finite. + /// @param[in] v A point. [(x, y, z)] + /// @return True if all of the point's components are finite, i.e. not NaN + /// or any of the infinities. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite(this RcVec3f v) + { + return float.IsFinite(v.X) && float.IsFinite(v.Y) && float.IsFinite(v.Z); + } + + /// Checks that the specified vector's 2D components are finite. + /// @param[in] v A point. [(x, y, z)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFinite2D(this RcVec3f v) + { + return float.IsFinite(v.X) && float.IsFinite(v.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float PerpXZ(RcVec3f a, RcVec3f b) + { + return (a.X * b.Z) - (a.Z * b.X); + } + + /// Performs a linear interpolation between two vectors. (@p v1 toward @p + /// v2) + /// @param[out] dest The result vector. [(x, y, x)] + /// @param[in] v1 The starting vector. + /// @param[in] v2 The destination vector. + /// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Lerp(Span verts, int v1, int v2, float t) + { + return new RcVec3f( + verts[v1 + 0] + (verts[v2 + 0] - verts[v1 + 0]) * t, + verts[v1 + 1] + (verts[v2 + 1] - verts[v1 + 1]) * t, + verts[v1 + 2] + (verts[v2 + 2] - verts[v1 + 2]) * t + ); + } + + /// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s)) + /// @param[out] dest The result vector. [(x, y, z)] + /// @param[in] v1 The base vector. [(x, y, z)] + /// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] + /// @param[in] s The amount to scale @p v2 by before adding to @p v1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Mad(RcVec3f v1, RcVec3f v2, float s) + { + return new RcVec3f() + { + X = v1.X + (v2.X * s), + Y = v1.Y + (v2.Y * s), + Z = v1.Z + (v2.Z * s), + }; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcVec2f.cs b/src/DotRecast.Core/Numerics/RcVec2f.cs similarity index 61% rename from src/DotRecast.Core/RcVec2f.cs rename to src/DotRecast.Core/Numerics/RcVec2f.cs index 192b2fb2..e90b3e3a 100644 --- a/src/DotRecast.Core/RcVec2f.cs +++ b/src/DotRecast.Core/Numerics/RcVec2f.cs @@ -1,24 +1,19 @@ using System; using System.Runtime.CompilerServices; -namespace DotRecast.Core +namespace DotRecast.Core.Numerics { public struct RcVec2f { - public float x; - public float y; + public float X; + public float Y; - public static RcVec2f Zero { get; } = new RcVec2f { x = 0, y = 0 }; + public static readonly RcVec2f Zero = new RcVec2f { X = 0, Y = 0 }; - public float Get(int idx) + public RcVec2f(float x, float y) { - if (0 == idx) - return x; - - if (1 == idx) - return y; - - throw new IndexOutOfRangeException("vector2f index out of range"); + X = x; + Y = y; } public override bool Equals(object obj) @@ -32,14 +27,15 @@ public override bool Equals(object obj) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(RcVec2f other) { - return x.Equals(other.x) && - y.Equals(other.y); + return X.Equals(other.X) && + Y.Equals(other.Y); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - int hash = x.GetHashCode(); - hash = RcHashCodes.CombineHashCodes(hash, y.GetHashCode()); + int hash = X.GetHashCode(); + hash = RcHashCodes.CombineHashCodes(hash, Y.GetHashCode()); return hash; } @@ -54,10 +50,11 @@ public override int GetHashCode() { return !left.Equals(right); } - + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { - return $"{x}, {y}"; + return $"{X}, {Y}"; } } } \ No newline at end of file diff --git a/src/DotRecast.Core/Numerics/RcVec3f.cs b/src/DotRecast.Core/Numerics/RcVec3f.cs new file mode 100644 index 00000000..ab750da6 --- /dev/null +++ b/src/DotRecast.Core/Numerics/RcVec3f.cs @@ -0,0 +1,287 @@ +/* +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.Numerics; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core.Numerics +{ + public unsafe struct RcVec3f + { + public float X; + public float Y; + public float Z; + + public static readonly RcVec3f Zero = new RcVec3f(0.0f, 0.0f, 0.0f); + public static readonly RcVec3f One = new RcVec3f(1.0f); + public static readonly RcVec3f UnitX = new RcVec3f(1.0f, 0.0f, 0.0f); + public static readonly RcVec3f UnitY = new RcVec3f(0.0f, 1.0f, 0.0f); + public static readonly RcVec3f UnitZ = new RcVec3f(0.0f, 0.0f, 1.0f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RcVec3f(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RcVec3f(float f) + { + X = f; + Y = f; + Z = f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RcVec3f(ReadOnlySpan values) + { + if (values.Length < 3) + { + RcThrowHelper.ThrowArgumentOutOfRangeException(nameof(values)); + } + + X = values[0]; + Y = values[1]; + Z = values[2]; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly float Length() + { + float lengthSquared = LengthSquared(); + return MathF.Sqrt(lengthSquared); + } + + /// Derives the square of the scalar length of the vector. (len * len) + /// @param[in] v The vector. [(x, y, z)] + /// @return The square of the scalar length of the vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly float LengthSquared() + { + return Dot(this, this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Subtract(RcVec3f left, RcVec3f right) + { + return left - right; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Add(RcVec3f left, RcVec3f right) + { + return left + right; + } + + + public override bool Equals(object obj) + { + if (!(obj is RcVec3f)) + return false; + + return Equals((RcVec3f)obj); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RcVec3f other) + { + return X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + return HashCode.Combine(X, Y, Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Min(RcVec3f value1, RcVec3f value2) + { + return new RcVec3f( + (value1.X < value2.X) ? value1.X : value2.X, + (value1.Y < value2.Y) ? value1.Y : value2.Y, + (value1.Z < value2.Z) ? value1.Z : value2.Z + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Max(RcVec3f value1, RcVec3f value2) + { + return new RcVec3f( + (value1.X > value2.X) ? value1.X : value2.X, + (value1.Y > value2.Y) ? value1.Y : value2.Y, + (value1.Z > value2.Z) ? value1.Z : value2.Z + ); + } + + public override string ToString() + { + return $"{X}, {Y}, {Z}"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(RcVec3f left, RcVec3f right) + { + return left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(RcVec3f left, RcVec3f right) + { + return !left.Equals(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f operator -(RcVec3f left, RcVec3f right) + { + return new RcVec3f( + left.X - right.X, + left.Y - right.Y, + left.Z - right.Z + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f operator +(RcVec3f left, RcVec3f right) + { + return new RcVec3f( + left.X + right.X, + left.Y + right.Y, + left.Z + right.Z + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f operator *(RcVec3f left, RcVec3f right) + { + return new RcVec3f( + left.X * right.X, + left.Y * right.Y, + left.Z * right.Z + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f operator *(RcVec3f left, float right) + { + return left * new RcVec3f(right); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f operator *(float left, RcVec3f right) + { + return right * left; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Lerp(RcVec3f value1, RcVec3f value2, float amount) + { + return (value1 * (1f - amount)) + (value2 * amount); + // return new RcVec3f( + // value1.X + (value2.X - value1.X) * amount, + // value1.Y + (value2.Y - value1.Y) * amount, + // value1.Z + (value2.Z - value1.Z) * amount + // ); + } + + /// Returns the distance between two points. + /// @param[in] v1 A point. [(x, y, z)] + /// @param[in] v2 A point. [(x, y, z)] + /// @return The distance between the two points. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Distance(RcVec3f value1, RcVec3f value2) + { + float distanceSquared = DistanceSquared(value1, value2); + return MathF.Sqrt(distanceSquared); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float DistanceSquared(RcVec3f value1, RcVec3f value2) + { + var difference = value1 - value2; + return Dot(difference, difference); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Dot(RcVec3f vector1, RcVec3f vector2) + { + return (vector1.X * vector2.X) + + (vector1.Y * vector2.Y) + + (vector1.Z * vector2.Z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyTo(float[] array) + { + CopyTo(array, 0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyTo(float[] array, int n) + { + array[n + 0] = X; + array[n + 1] = Y; + array[n + 2] = Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Cross(RcVec3f v1, RcVec3f v2) + { + return new RcVec3f( + (v1.Y * v2.Z) - (v1.Z * v2.Y), + (v1.Z * v2.X) - (v1.X * v2.Z), + (v1.X * v2.Y) - (v1.Y * v2.X) + ); + } + + /// Normalizes the vector. + /// @param[in,out] v The vector to normalize. [(x, y, z)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RcVec3f Normalize(RcVec3f v) + { + float d = 1.0f / MathF.Sqrt(RcMath.Sqr(v.X) + RcMath.Sqr(v.Y) + RcMath.Sqr(v.Z)); + + return new RcVec3f( + v.X *= d, + v.Y *= d, + v.Z *= d + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe implicit operator Vector3(RcVec3f vec3f) + { + return *(Vector3*)&vec3f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe implicit operator RcVec3f(Vector3 vec3) + { + return *(RcVec3f*)&vec3; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcArrayUtils.cs b/src/DotRecast.Core/RcArrays.cs similarity index 54% rename from src/DotRecast.Core/RcArrayUtils.cs rename to src/DotRecast.Core/RcArrays.cs index b308de09..e8c291af 100644 --- a/src/DotRecast.Core/RcArrayUtils.cs +++ b/src/DotRecast.Core/RcArrays.cs @@ -1,9 +1,24 @@ using System; +using System.Runtime.CompilerServices; namespace DotRecast.Core { - public static class RcArrayUtils + public static class RcArrays { + // Type Safe Copy + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(T[] sourceArray, long sourceIndex, T[] destinationArray, long destinationIndex, long length) + { + Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + } + + // Type Safe Copy + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(T[] sourceArray, T[] destinationArray, long length) + { + Array.Copy(sourceArray, destinationArray, length); + } + public static T[] CopyOf(T[] source, int startIdx, int length) { var deatArr = new T[length]; @@ -15,7 +30,7 @@ public static T[] CopyOf(T[] source, int startIdx, int length) return deatArr; } - public static T[] CopyOf(T[] source, int length) + public static T[] CopyOf(T[] source, long length) { var deatArr = new T[length]; var count = Math.Max(0, Math.Min(source.Length, length)); diff --git a/src/DotRecast.Core/RcAtomicLong.cs b/src/DotRecast.Core/RcAtomicLong.cs index 0ea3e01c..f224ffa3 100644 --- a/src/DotRecast.Core/RcAtomicLong.cs +++ b/src/DotRecast.Core/RcAtomicLong.cs @@ -1,8 +1,9 @@ -using System.Threading; +using System; +using System.Threading; namespace DotRecast.Core { - public class RcAtomicLong + public class RcAtomicLong : IComparable { private long _location; @@ -15,6 +16,11 @@ public RcAtomicLong(long location) _location = location; } + public int CompareTo(RcAtomicLong other) + { + return Read().CompareTo(other.Read()); + } + public long IncrementAndGet() { return Interlocked.Increment(ref _location); diff --git a/src/DotRecast.Detour.Dynamic/Io/ByteUtils.cs b/src/DotRecast.Core/RcByteUtils.cs similarity index 87% rename from src/DotRecast.Detour.Dynamic/Io/ByteUtils.cs rename to src/DotRecast.Core/RcByteUtils.cs index 8e90aff9..01135260 100644 --- a/src/DotRecast.Detour.Dynamic/Io/ByteUtils.cs +++ b/src/DotRecast.Core/RcByteUtils.cs @@ -1,5 +1,6 @@ /* 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 @@ -16,11 +17,9 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ -using DotRecast.Core; - -namespace DotRecast.Detour.Dynamic.Io +namespace DotRecast.Core { - public static class ByteUtils + public static class RcByteUtils { public static int GetInt(byte[] data, int position, RcByteOrder order) { @@ -29,13 +28,17 @@ public static int GetInt(byte[] data, int position, RcByteOrder order) public static int GetIntBE(byte[] data, int position) { - return ((data[position] & 0xff) << 24) | ((data[position + 1] & 0xff) << 16) | ((data[position + 2] & 0xff) << 8) + return ((data[position] & 0xff) << 24) + | ((data[position + 1] & 0xff) << 16) + | ((data[position + 2] & 0xff) << 8) | (data[position + 3] & 0xff); } public static int GetIntLE(byte[] data, int position) { - return ((data[position + 3] & 0xff) << 24) | ((data[position + 2] & 0xff) << 16) | ((data[position + 1] & 0xff) << 8) + return ((data[position + 3] & 0xff) << 24) + | ((data[position + 2] & 0xff) << 16) + | ((data[position + 1] & 0xff) << 8) | (data[position] & 0xff); } diff --git a/src/DotRecast.Core/RcTelemetry.cs b/src/DotRecast.Core/RcContext.cs similarity index 61% rename from src/DotRecast.Core/RcTelemetry.cs rename to src/DotRecast.Core/RcContext.cs index 6deec472..e9a8ce9f 100644 --- a/src/DotRecast.Core/RcTelemetry.cs +++ b/src/DotRecast.Core/RcContext.cs @@ -1,5 +1,6 @@ /* 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 @@ -24,27 +25,46 @@ 3. This notice may not be removed or altered from any source distribution. namespace DotRecast.Core { - public class RcTelemetry + /// Provides an interface for optional logging and performance tracking of the Recast + /// build process. + /// + /// This class does not provide logging or timer functionality on its + /// own. Both must be provided by a concrete implementation + /// by overriding the protected member functions. Also, this class does not + /// provide an interface for extracting log messages. (Only adding them.) + /// So concrete implementations must provide one. + /// + /// If no logging or timers are required, just pass an instance of this + /// class through the Recast build process. + /// + /// @ingroup recast + public class RcContext { private readonly ThreadLocal> _timerStart; private readonly ConcurrentDictionary _timerAccum; - public RcTelemetry() + public RcContext() { _timerStart = new ThreadLocal>(() => new Dictionary()); _timerAccum = new ConcurrentDictionary(); } - public void StartTimer(string name) + public RcScopedTimer ScopedTimer(RcTimerLabel label) { - _timerStart.Value[name] = new RcAtomicLong(RcFrequency.Ticks); + return new RcScopedTimer(this, label); } - public void StopTimer(string name) + public void StartTimer(RcTimerLabel label) + { + _timerStart.Value[label.Name] = new RcAtomicLong(RcFrequency.Ticks); + } + + + public void StopTimer(RcTimerLabel label) { _timerAccum - .GetOrAdd(name, _ => new RcAtomicLong(0)) - .AddAndGet(RcFrequency.Ticks - _timerStart.Value?[name].Read() ?? 0); + .GetOrAdd(label.Name, _ => new RcAtomicLong(0)) + .AddAndGet(RcFrequency.Ticks - _timerStart.Value?[label.Name].Read() ?? 0); } public void Warn(string message) diff --git a/src/DotRecast.Core/ConvexUtils.cs b/src/DotRecast.Core/RcConvexUtils.cs similarity index 73% rename from src/DotRecast.Core/ConvexUtils.cs rename to src/DotRecast.Core/RcConvexUtils.cs index a67269f2..4fa71b29 100644 --- a/src/DotRecast.Core/ConvexUtils.cs +++ b/src/DotRecast.Core/RcConvexUtils.cs @@ -1,5 +1,6 @@ /* 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 @@ -17,25 +18,24 @@ 3. This notice may not be removed or altered from any source distribution. */ using System.Collections.Generic; +using DotRecast.Core.Numerics; namespace DotRecast.Core { - public static class ConvexUtils + public static class RcConvexUtils { // Calculates convex hull on xz-plane of points on 'pts', // stores the indices of the resulting hull in 'out' and // returns number of points on hull. - public static List Convexhull(List pts) + public static List Convexhull(List pts) { - int npts = pts.Count / 3; + int npts = pts.Count; List @out = new List(); // Find lower-leftmost point. int hull = 0; for (int i = 1; i < npts; ++i) { - RcVec3f a = RcVec3f.Of(pts[i * 3], pts[i * 3 + 1], pts[i * 3 + 2]); - RcVec3f b = RcVec3f.Of(pts[hull * 3], pts[hull * 3 + 1], pts[hull * 3 + 2]); - if (Cmppt(a, b)) + if (Cmppt(pts[i], pts[hull])) { hull = i; } @@ -49,9 +49,9 @@ public static List Convexhull(List pts) endpt = 0; for (int j = 1; j < npts; ++j) { - RcVec3f a = RcVec3f.Of(pts[hull * 3], pts[hull * 3 + 1], pts[hull * 3 + 2]); - RcVec3f b = RcVec3f.Of(pts[endpt * 3], pts[endpt * 3 + 1], pts[endpt * 3 + 2]); - RcVec3f c = RcVec3f.Of(pts[j * 3], pts[j * 3 + 1], pts[j * 3 + 2]); + RcVec3f a = pts[hull]; + RcVec3f b = pts[endpt]; + RcVec3f c = pts[j]; if (hull == endpt || Left(a, b, c)) { endpt = j; @@ -67,22 +67,22 @@ public static List Convexhull(List pts) // Returns true if 'a' is more lower-left than 'b'. private static bool Cmppt(RcVec3f a, RcVec3f b) { - if (a.x < b.x) + if (a.X < b.X) { return true; } - if (a.x > b.x) + if (a.X > b.X) { return false; } - if (a.z < b.z) + if (a.Z < b.Z) { return true; } - if (a.z > b.z) + if (a.Z > b.Z) { return false; } @@ -93,11 +93,11 @@ private static bool Cmppt(RcVec3f a, RcVec3f b) // Returns true if 'c' is left of line 'a'-'b'. private static bool Left(RcVec3f a, RcVec3f b, RcVec3f c) { - float u1 = b.x - a.x; - float v1 = b.z - a.z; - float u2 = c.x - a.x; - float v2 = c.z - a.z; + float u1 = b.X - a.X; + float v1 = b.Z - a.Z; + float u2 = c.X - a.X; + float v2 = c.Z - a.Z; return u1 * v2 - v1 * u2 < 0; } } -} +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcDirectory.cs b/src/DotRecast.Core/RcDirectory.cs new file mode 100644 index 00000000..46a5362e --- /dev/null +++ b/src/DotRecast.Core/RcDirectory.cs @@ -0,0 +1,57 @@ +using System.IO; +using System.Linq; + +namespace DotRecast.Core +{ + public static class RcDirectory + { + 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; + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcHashCodes.cs b/src/DotRecast.Core/RcHashCodes.cs index d94f6cf4..3202c9aa 100644 --- a/src/DotRecast.Core/RcHashCodes.cs +++ b/src/DotRecast.Core/RcHashCodes.cs @@ -1,10 +1,26 @@ -namespace DotRecast.Core +using System.Runtime.CompilerServices; + +namespace DotRecast.Core { public static class RcHashCodes { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CombineHashCodes(int h1, int h2) { return (((h1 << 5) + h1) ^ h2); } + + // From Thomas Wang, https://gist.github.com/badboy/6267743 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint WangHash(uint a) + { + a = (~a) + (a << 18); // a = (a << 18) - a - 1; + a = a ^ (a >> 31); + a = a * 21; // a = (a + (a << 2)) + (a << 4); + a = a ^ (a >> 11); + a = a + (a << 6); + a = a ^ (a >> 22); + return (uint)a; + } } } \ No newline at end of file diff --git a/src/DotRecast.Core/RcIO.cs b/src/DotRecast.Core/RcIO.cs new file mode 100644 index 00000000..f08f89c3 --- /dev/null +++ b/src/DotRecast.Core/RcIO.cs @@ -0,0 +1,165 @@ +/* +Recast4J Copyright (c) 2015 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +using System; +using System.IO; + +namespace DotRecast.Core +{ + public static class RcIO + { + public static RcByteBuffer ToByteBuffer(BinaryReader br, bool direct) + { + byte[] data = ToByteArray(br); + if (direct) + { + Array.Reverse(data); + } + + return new RcByteBuffer(data); + } + + public static byte[] ToByteArray(BinaryReader br) + { + using var ms = new MemoryStream(); + Span buffer = stackalloc byte[4096]; + int l; + while ((l = br.Read(buffer)) > 0) + { + ms.Write(buffer.Slice(0, l)); + } + + return ms.ToArray(); + } + + + public static RcByteBuffer ToByteBuffer(BinaryReader br) + { + var bytes = ToByteArray(br); + return new RcByteBuffer(bytes); + } + + public static int SwapEndianness(int i) + { + var s = (((uint)i >> 24) & 0xFF) | (((uint)i >> 8) & 0xFF00) | (((uint)i << 8) & 0xFF0000) | ((i << 24) & 0xFF000000); + return (int)s; + } + + public static byte[] ReadFileIfFound(string filename) + { + if (string.IsNullOrEmpty(filename)) + return null; + + string filePath = filename; + + if (!File.Exists(filePath)) + { + var searchFilePath = RcDirectory.SearchFile($"{filename}"); + if (!File.Exists(searchFilePath)) + { + searchFilePath = RcDirectory.SearchFile($"resources/{filename}"); + } + + if (File.Exists(searchFilePath)) + { + filePath = searchFilePath; + } + } + + using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + byte[] buffer = new byte[fs.Length]; + var read = fs.Read(buffer, 0, buffer.Length); + if (read != buffer.Length) + return null; + + return buffer; + } + + public static void Write(BinaryWriter ws, float value, RcByteOrder order) + { + byte[] bytes = BitConverter.GetBytes(value); + int i = BitConverter.ToInt32(bytes, 0); + Write(ws, i, order); + } + + public static void Write(BinaryWriter ws, short value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)(value & 0xFF)); + } + else + { + ws.Write((byte)(value & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + } + } + + public static void Write(BinaryWriter ws, long value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + Write(ws, (int)((ulong)value >> 32), order); + Write(ws, (int)(value & 0xFFFFFFFF), order); + } + else + { + Write(ws, (int)(value & 0xFFFFFFFF), order); + Write(ws, (int)((ulong)value >> 32), order); + } + } + + public static void Write(BinaryWriter ws, int value, RcByteOrder order) + { + if (order == RcByteOrder.BIG_ENDIAN) + { + ws.Write((byte)((value >> 24) & 0xFF)); + ws.Write((byte)((value >> 16) & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)(value & 0xFF)); + } + else + { + ws.Write((byte)(value & 0xFF)); + ws.Write((byte)((value >> 8) & 0xFF)); + ws.Write((byte)((value >> 16) & 0xFF)); + ws.Write((byte)((value >> 24) & 0xFF)); + } + } + + public static void Write(BinaryWriter ws, bool value) + { + Write(ws, (byte)(value ? 1 : 0)); + } + + public static void Write(BinaryWriter ws, byte value) + { + ws.Write(value); + } + + public static void Write(BinaryWriter ws, MemoryStream ms) + { + ms.Position = 0; + byte[] buffer = new byte[ms.Length]; + ms.Read(buffer, 0, buffer.Length); + ws.Write(buffer); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/Intersections.cs b/src/DotRecast.Core/RcIntersections.cs similarity index 72% rename from src/DotRecast.Core/Intersections.cs rename to src/DotRecast.Core/RcIntersections.cs index 6779bdaf..55cf2b32 100644 --- a/src/DotRecast.Core/Intersections.cs +++ b/src/DotRecast.Core/RcIntersections.cs @@ -1,5 +1,7 @@ /* +Copyright (c) 2009-2010 Mikko Mononen memon@inside.org 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 @@ -17,17 +19,19 @@ 3. This notice may not be removed or altered from any source distribution. */ using System; +using DotRecast.Core.Numerics; namespace DotRecast.Core { - public static class Intersections + public static class RcIntersections { - public static float? IntersectSegmentTriangle(RcVec3f sp, RcVec3f sq, RcVec3f a, RcVec3f b, RcVec3f c) + public static bool IntersectSegmentTriangle(RcVec3f sp, RcVec3f sq, RcVec3f a, RcVec3f b, RcVec3f c, out float t) { + t = 0; float v, w; - RcVec3f ab = b.Subtract(a); - RcVec3f ac = c.Subtract(a); - RcVec3f qp = sp.Subtract(sq); + RcVec3f ab = RcVec3f.Subtract(b, a); + RcVec3f ac = RcVec3f.Subtract(c, a); + RcVec3f qp = RcVec3f.Subtract(sp, sq); // Compute triangle normal. Can be precalculated or cached if // intersecting multiple segments against the same triangle @@ -38,22 +42,22 @@ public static class Intersections float d = RcVec3f.Dot(qp, norm); if (d <= 0.0f) { - return null; + return false; } // Compute intersection t value of pq with plane of triangle. A ray // intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay // dividing by d until intersection has been found to pierce triangle - RcVec3f ap = sp.Subtract(a); - float t = RcVec3f.Dot(ap, norm); + RcVec3f ap = RcVec3f.Subtract(sp, a); + t = RcVec3f.Dot(ap, norm); if (t < 0.0f) { - return null; + return false; } if (t > d) { - return null; // For segment; exclude this code line for a ray test + return false; // For segment; exclude this code line for a ray test } // Compute barycentric coordinate components and test if within bounds @@ -61,19 +65,19 @@ public static class Intersections v = RcVec3f.Dot(ac, e); if (v < 0.0f || v > d) { - return null; + return false; } w = -RcVec3f.Dot(ab, e); if (w < 0.0f || v + w > d) { - return null; + return false; } // Segment/ray intersects triangle. Perform delayed division t /= d; - return t; + return true; } public static bool IsectSegAABB(RcVec3f sp, RcVec3f sq, RcVec3f amin, RcVec3f amax, out float tmin, out float tmax) @@ -81,26 +85,26 @@ public static bool IsectSegAABB(RcVec3f sp, RcVec3f sq, RcVec3f amin, RcVec3f am const float EPS = 1e-6f; RcVec3f d = new RcVec3f(); - d.x = sq.x - sp.x; - d.y = sq.y - sp.y; - d.z = sq.z - sp.z; + d.X = sq.X - sp.X; + d.Y = sq.Y - sp.Y; + d.Z = sq.Z - sp.Z; tmin = 0.0f; tmax = float.MaxValue; for (int i = 0; i < 3; i++) { - if (Math.Abs(d[i]) < EPS) + if (MathF.Abs(d.Get(i)) < EPS) { - if (sp[i] < amin[i] || sp[i] > amax[i]) + if (sp.Get(i) < amin.Get(i) || sp.Get(i) > amax.Get(i)) { return false; } } else { - float ood = 1.0f / d[i]; - float t1 = (amin[i] - sp[i]) * ood; - float t2 = (amax[i] - sp[i]) * ood; + float ood = 1.0f / d.Get(i); + float t1 = (amin.Get(i) - sp.Get(i)) * ood; + float t2 = (amax.Get(i) - sp.Get(i)) * ood; if (t1 > t2) { diff --git a/src/DotRecast.Core/RcMath.cs b/src/DotRecast.Core/RcMath.cs index a62e1031..b981bd1b 100644 --- a/src/DotRecast.Core/RcMath.cs +++ b/src/DotRecast.Core/RcMath.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -18,25 +18,22 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ -using System; +using System.Runtime.CompilerServices; namespace DotRecast.Core { public static class RcMath { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Sqr(float f) { return f * f; } - - public static float Clamp(float v, float min, float max) - { - return Math.Max(Math.Min(v, max), min); - } - - public static int Clamp(int v, int min, int max) + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Lerp(float value1, float value2, float amount) { - return Math.Max(Math.Min(v, max), min); + return (value1 * (1.0f - amount)) + (value2 * amount); } } } \ No newline at end of file diff --git a/src/DotRecast.Recast/ObjImporter.cs b/src/DotRecast.Core/RcObjImporter.cs similarity index 71% rename from src/DotRecast.Recast/ObjImporter.cs rename to src/DotRecast.Core/RcObjImporter.cs index 2401a191..43d7045b 100644 --- a/src/DotRecast.Recast/ObjImporter.cs +++ b/src/DotRecast.Core/RcObjImporter.cs @@ -1,5 +1,6 @@ /* recast4j Copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -17,26 +18,20 @@ 3. This notice may not be removed or altered from any source distribution. */ using System; -using System.Collections.Generic; +using System.Globalization; using System.IO; -using DotRecast.Recast.Geom; +using DotRecast.Core.Numerics; -namespace DotRecast.Recast +namespace DotRecast.Core { - public static class ObjImporter + public static class RcObjImporter { - public static IInputGeomProvider Load(byte[] chunck) + public static RcObjImporterContext LoadContext(byte[] chunk) { - var context = LoadContext(chunck); - return new SimpleInputGeomProvider(context.vertexPositions, context.meshFaces); - } - - public static ObjImporterContext LoadContext(byte[] chunck) - { - ObjImporterContext context = new ObjImporterContext(); + RcObjImporterContext context = new RcObjImporterContext(); try { - using StreamReader reader = new StreamReader(new MemoryStream(chunck)); + using StreamReader reader = new StreamReader(new MemoryStream(chunk)); string line; while ((line = reader.ReadLine()) != null) { @@ -53,7 +48,7 @@ public static ObjImporterContext LoadContext(byte[] chunck) } - public static void ReadLine(string line, ObjImporterContext context) + public static void ReadLine(string line, RcObjImporterContext context) { if (line.StartsWith("v")) { @@ -65,19 +60,18 @@ public static void ReadLine(string line, ObjImporterContext context) } } - private static void ReadVertex(string line, ObjImporterContext context) + private static void ReadVertex(string line, RcObjImporterContext context) { if (line.StartsWith("v ")) { - float[] vert = ReadVector3f(line); - foreach (float vp in vert) - { - context.vertexPositions.Add(vp); - } + var vert = ReadVector3f(line); + context.vertexPositions.Add(vert.X); + context.vertexPositions.Add(vert.Y); + context.vertexPositions.Add(vert.Z); } } - private static float[] ReadVector3f(string line) + private static RcVec3f ReadVector3f(string line) { string[] v = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (v.Length < 4) @@ -85,10 +79,15 @@ private static float[] ReadVector3f(string line) throw new Exception("Invalid vector, expected 3 coordinates, found " + (v.Length - 1)); } - return new float[] { float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]) }; + // fix - https://github.com/ikpil/DotRecast/issues/7 + return new RcVec3f( + float.Parse(v[1], CultureInfo.InvariantCulture), + float.Parse(v[2], CultureInfo.InvariantCulture), + float.Parse(v[3], CultureInfo.InvariantCulture) + ); } - private static void ReadFace(string line, ObjImporterContext context) + private static void ReadFace(string line, RcObjImporterContext context) { string[] v = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (v.Length < 4) @@ -106,7 +105,7 @@ private static void ReadFace(string line, ObjImporterContext context) } } - private static int ReadFaceVertex(string face, ObjImporterContext context) + private static int ReadFaceVertex(string face, RcObjImporterContext context) { string[] v = face.Split("/"); return GetIndex(int.Parse(v[0]), context.vertexPositions.Count); diff --git a/src/DotRecast.Recast/ObjImporterContext.cs b/src/DotRecast.Core/RcObjImporterContext.cs similarity index 73% rename from src/DotRecast.Recast/ObjImporterContext.cs rename to src/DotRecast.Core/RcObjImporterContext.cs index e6cab53e..a08deaf8 100644 --- a/src/DotRecast.Recast/ObjImporterContext.cs +++ b/src/DotRecast.Core/RcObjImporterContext.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -namespace DotRecast.Recast +namespace DotRecast.Core { - public class ObjImporterContext + public class RcObjImporterContext { public List vertexPositions = new List(); public List meshFaces = new List(); diff --git a/src/DotRecast.Core/RcProcess.cs b/src/DotRecast.Core/RcProcess.cs new file mode 100644 index 00000000..05987aec --- /dev/null +++ b/src/DotRecast.Core/RcProcess.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace DotRecast.Core +{ + public static class RcProcess + { + public static void OpenUrl(string url) + { + try + { + // OS에 따라 다른 명령 실행 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var psi = new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }; + Process.Start(psi); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("open", url); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error opening web browser: {ex.Message}"); + } + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcRand.cs b/src/DotRecast.Core/RcRand.cs new file mode 100644 index 00000000..6111aff6 --- /dev/null +++ b/src/DotRecast.Core/RcRand.cs @@ -0,0 +1,38 @@ +using System; + +namespace DotRecast.Core +{ + public class RcRand : IRcRand + { + private readonly Random _r; + + public RcRand() : this(new Random()) + { + } + + public RcRand(Random r) + { + _r = r; + } + + public RcRand(long seed) + { + _r = new Random((int)seed); // TODO : 랜덤 시드 확인 필요 + } + + public float Next() + { + return (float)_r.NextDouble(); + } + + public double NextDouble() + { + return _r.NextDouble(); + } + + public int NextInt32() + { + return _r.Next(); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcScopedTimer.cs b/src/DotRecast.Core/RcScopedTimer.cs new file mode 100644 index 00000000..6b84e286 --- /dev/null +++ b/src/DotRecast.Core/RcScopedTimer.cs @@ -0,0 +1,23 @@ +using System; + +namespace DotRecast.Core +{ + public readonly struct RcScopedTimer : IDisposable + { + private readonly RcContext _context; + private readonly RcTimerLabel _label; + + internal RcScopedTimer(RcContext context, RcTimerLabel label) + { + _context = context; + _label = label; + + _context.StartTimer(_label); + } + + public void Dispose() + { + _context.StopTimer(_label); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcSegmentVert.cs b/src/DotRecast.Core/RcSegmentVert.cs new file mode 100644 index 00000000..7cab3981 --- /dev/null +++ b/src/DotRecast.Core/RcSegmentVert.cs @@ -0,0 +1,22 @@ +using DotRecast.Core.Numerics; + +namespace DotRecast.Core +{ + public struct RcSegmentVert + { + public RcVec3f vmin; + public RcVec3f vmax; + + public RcSegmentVert(float v0, float v1, float v2, float v3, float v4, float v5) + { + vmin.X = v0; + vmin.Y = v1; + vmin.Z = v2; + + vmax.X = v3; + vmax.Y = v4; + vmax.Z = v5; + } + + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcSpans.cs b/src/DotRecast.Core/RcSpans.cs new file mode 100644 index 00000000..be7f8321 --- /dev/null +++ b/src/DotRecast.Core/RcSpans.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.CompilerServices; + +namespace DotRecast.Core +{ + public static class RcSpans + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(Span src, Span dst) + { + src.CopyTo(dst); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(Span src, int srcIdx, Span dst, int dstIdx, int length) + { + var slicedSrc = src.Slice(srcIdx, length); + var slicedDst = dst.Slice(dstIdx); + slicedSrc.CopyTo(slicedDst); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Move(Span src, int srcIdx, int dstIdx, int length) + { + var slicedSrc = src.Slice(srcIdx, length); + var slicedDst = src.Slice(dstIdx, length); + slicedSrc.CopyTo(slicedDst); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Fill(Span span, T value, int start, int count) + { + span.Slice(start, count).Fill(value); + } + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcThrowHelper.cs b/src/DotRecast.Core/RcThrowHelper.cs new file mode 100644 index 00000000..b3f484b6 --- /dev/null +++ b/src/DotRecast.Core/RcThrowHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.Runtime.CompilerServices; +using DotRecast.Core.Collections; + +namespace DotRecast.Core +{ + public static class RcThrowHelper + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowExceptionIfIndexOutOfRange(int index, int size) + { + if (0 > index || index >= size) + { + throw new IndexOutOfRangeException($"Index {index} is out of range - size({size})"); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ThrowArgumentOutOfRangeException(string argument) + { + throw new ArgumentOutOfRangeException(argument); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StackOverflow() + { + var array_128_512_1 = RcStackArray128>.Empty; // 128 * 512 = 65536 + var array_128_512_2 = RcStackArray128>.Empty; // 128 * 512 = 65536 + + var array_32_512_1 = RcStackArray32>.Empty; // 32 * 512 = 16384 + + var array_16_512_1 = RcStackArray16>.Empty; // 16 * 512 = 8192 + + var array_8_512_1 = RcStackArray8>.Empty; // 8 * 512 = 4196 + var array_4_256_1 = RcStackArray4>.Empty; // 4 * 256 = 1024 + var array_4_64_1 = RcStackArray4>.Empty; // 4 * 64 = 256 + + // + var array_2_8_1 = RcStackArray2>.Empty; // 2 * 8 = 16 + var array_2_4_1 = RcStackArray2>.Empty; // 2 * 2 = 4 + + float f1 = 0.0f; // 1 + //float f2 = 0.0f; // my system stack overflow! + } + + + } +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcTimerLabel.cs b/src/DotRecast.Core/RcTimerLabel.cs new file mode 100644 index 00000000..4492cf06 --- /dev/null +++ b/src/DotRecast.Core/RcTimerLabel.cs @@ -0,0 +1,109 @@ +namespace DotRecast.Core +{ + /// Recast performance timer categories. + /// @see rcContext + public class RcTimerLabel + { + /// The user defined total time of the build. + public static readonly RcTimerLabel RC_TIMER_TOTAL = new RcTimerLabel(nameof(RC_TIMER_TOTAL)); + + /// A user defined build time. + public static readonly RcTimerLabel RC_TIMER_TEMP = new RcTimerLabel(nameof(RC_TIMER_TEMP)); + + /// The time to rasterize the triangles. (See: #rcRasterizeTriangle) + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_TRIANGLES = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_TRIANGLES)); + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_SPHERE = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_SPHERE)); + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_CAPSULE = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_CAPSULE)); + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_CYLINDER = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_CYLINDER)); + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_BOX = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_BOX)); + public static readonly RcTimerLabel RC_TIMER_RASTERIZE_CONVEX = new RcTimerLabel(nameof(RC_TIMER_RASTERIZE_CONVEX)); + + /// The time to build the compact heightfield. (See: #rcBuildCompactHeightfield) + public static readonly RcTimerLabel RC_TIMER_BUILD_COMPACTHEIGHTFIELD = new RcTimerLabel(nameof(RC_TIMER_BUILD_COMPACTHEIGHTFIELD)); + + /// The total time to build the contours. (See: #rcBuildContours) + public static readonly RcTimerLabel RC_TIMER_BUILD_CONTOURS = new RcTimerLabel(nameof(RC_TIMER_BUILD_CONTOURS)); + + /// The time to trace the boundaries of the contours. (See: #rcBuildContours) + public static readonly RcTimerLabel RC_TIMER_BUILD_CONTOURS_TRACE = new RcTimerLabel(nameof(RC_TIMER_BUILD_CONTOURS_TRACE)); + + public static readonly RcTimerLabel RC_TIMER_BUILD_CONTOURS_WALK = new RcTimerLabel(nameof(RC_TIMER_BUILD_CONTOURS_WALK)); + + /// The time to simplify the contours. (See: #rcBuildContours) + public static readonly RcTimerLabel RC_TIMER_BUILD_CONTOURS_SIMPLIFY = new RcTimerLabel(nameof(RC_TIMER_BUILD_CONTOURS_SIMPLIFY)); + + /// The time to filter ledge spans. (See: #rcFilterLedgeSpans) + public static readonly RcTimerLabel RC_TIMER_FILTER_BORDER = new RcTimerLabel(nameof(RC_TIMER_FILTER_BORDER)); + + /// The time to filter low height spans. (See: #rcFilterWalkableLowHeightSpans) + public static readonly RcTimerLabel RC_TIMER_FILTER_WALKABLE = new RcTimerLabel(nameof(RC_TIMER_FILTER_WALKABLE)); + + /// The time to apply the median filter. (See: #rcMedianFilterWalkableArea) + public static readonly RcTimerLabel RC_TIMER_MEDIAN_AREA = new RcTimerLabel(nameof(RC_TIMER_MEDIAN_AREA)); + + /// The time to filter low obstacles. (See: #rcFilterLowHangingWalkableObstacles) + public static readonly RcTimerLabel RC_TIMER_FILTER_LOW_OBSTACLES = new RcTimerLabel(nameof(RC_TIMER_FILTER_LOW_OBSTACLES)); + + /// The time to build the polygon mesh. (See: #rcBuildPolyMesh) + public static readonly RcTimerLabel RC_TIMER_BUILD_POLYMESH = new RcTimerLabel(nameof(RC_TIMER_BUILD_POLYMESH)); + + /// The time to merge polygon meshes. (See: #rcMergePolyMeshes) + public static readonly RcTimerLabel RC_TIMER_MERGE_POLYMESH = new RcTimerLabel(nameof(RC_TIMER_MERGE_POLYMESH)); + + /// The time to erode the walkable area. (See: #rcErodeWalkableArea) + public static readonly RcTimerLabel RC_TIMER_ERODE_AREA = new RcTimerLabel(nameof(RC_TIMER_ERODE_AREA)); + + /// The time to mark a box area. (See: #rcMarkBoxArea) + public static readonly RcTimerLabel RC_TIMER_MARK_BOX_AREA = new RcTimerLabel(nameof(RC_TIMER_MARK_BOX_AREA)); + + /// The time to mark a cylinder area. (See: #rcMarkCylinderArea) + public static readonly RcTimerLabel RC_TIMER_MARK_CYLINDER_AREA = new RcTimerLabel(nameof(RC_TIMER_MARK_CYLINDER_AREA)); + + /// The time to mark a convex polygon area. (See: #rcMarkConvexPolyArea) + public static readonly RcTimerLabel RC_TIMER_MARK_CONVEXPOLY_AREA = new RcTimerLabel(nameof(RC_TIMER_MARK_CONVEXPOLY_AREA)); + + /// The total time to build the distance field. (See: #rcBuildDistanceField) + public static readonly RcTimerLabel RC_TIMER_BUILD_DISTANCEFIELD = new RcTimerLabel(nameof(RC_TIMER_BUILD_DISTANCEFIELD)); + + /// The time to build the distances of the distance field. (See: #rcBuildDistanceField) + public static readonly RcTimerLabel RC_TIMER_BUILD_DISTANCEFIELD_DIST = new RcTimerLabel(nameof(RC_TIMER_BUILD_DISTANCEFIELD_DIST)); + + /// The time to blur the distance field. (See: #rcBuildDistanceField) + public static readonly RcTimerLabel RC_TIMER_BUILD_DISTANCEFIELD_BLUR = new RcTimerLabel(nameof(RC_TIMER_BUILD_DISTANCEFIELD_BLUR)); + + /// The total time to build the regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) + public static readonly RcTimerLabel RC_TIMER_BUILD_REGIONS = new RcTimerLabel(nameof(RC_TIMER_BUILD_REGIONS)); + + /// The total time to apply the watershed algorithm. (See: #rcBuildRegions) + public static readonly RcTimerLabel RC_TIMER_BUILD_REGIONS_WATERSHED = new RcTimerLabel(nameof(RC_TIMER_BUILD_REGIONS_WATERSHED)); + + /// The time to expand regions while applying the watershed algorithm. (See: #rcBuildRegions) + public static readonly RcTimerLabel RC_TIMER_BUILD_REGIONS_EXPAND = new RcTimerLabel(nameof(RC_TIMER_BUILD_REGIONS_EXPAND)); + + /// The time to flood regions while applying the watershed algorithm. (See: #rcBuildRegions) + public static readonly RcTimerLabel RC_TIMER_BUILD_REGIONS_FLOOD = new RcTimerLabel(nameof(RC_TIMER_BUILD_REGIONS_FLOOD)); + + /// The time to filter out small regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) + public static readonly RcTimerLabel RC_TIMER_BUILD_REGIONS_FILTER = new RcTimerLabel(nameof(RC_TIMER_BUILD_REGIONS_FILTER)); + + /// The time to build heightfield layers. (See: #rcBuildHeightfieldLayers) + public static readonly RcTimerLabel RC_TIMER_BUILD_LAYERS = new RcTimerLabel(nameof(RC_TIMER_BUILD_LAYERS)); + + /// The time to build the polygon mesh detail. (See: #rcBuildPolyMeshDetail) + public static readonly RcTimerLabel RC_TIMER_BUILD_POLYMESHDETAIL = new RcTimerLabel(nameof(RC_TIMER_BUILD_POLYMESHDETAIL)); + + /// The time to merge polygon mesh details. (See: #rcMergePolyMeshDetails) + public static readonly RcTimerLabel RC_TIMER_MERGE_POLYMESHDETAIL = new RcTimerLabel(nameof(RC_TIMER_MERGE_POLYMESHDETAIL)); + + + /// The maximum number of timers. (Used for iterating timers.) + public static readonly RcTimerLabel RC_MAX_TIMERS = new RcTimerLabel(nameof(RC_MAX_TIMERS)); + + public readonly string Name; + + private RcTimerLabel(string name) + { + Name = name; + } + }; +} \ No newline at end of file diff --git a/src/DotRecast.Core/RcVec3f.cs b/src/DotRecast.Core/RcVec3f.cs deleted file mode 100644 index 7abb28da..00000000 --- a/src/DotRecast.Core/RcVec3f.cs +++ /dev/null @@ -1,621 +0,0 @@ -/* -recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -using System; -using System.Runtime.CompilerServices; - -namespace DotRecast.Core -{ - public struct RcVec3f - { - public float x; - public float y; - public float z; - - public static RcVec3f Zero { get; } = new RcVec3f(0.0f, 0.0f, 0.0f); - public static RcVec3f One { get; } = new RcVec3f(1.0f); - public static RcVec3f UnitX { get; } = new RcVec3f(1.0f, 0.0f, 0.0f); - public static RcVec3f UnitY { get; } = new RcVec3f(0.0f, 1.0f, 0.0f); - public static RcVec3f UnitZ { get; } = new RcVec3f(0.0f, 0.0f, 1.0f); - - public static RcVec3f Of(float[] f) - { - return Of(f, 0); - } - - public static RcVec3f Of(float[] f, int idx) - { - return Of(f[idx + 0], f[idx + 1], f[idx + 2]); - } - - public static RcVec3f Of(float x, float y, float z) - { - return new RcVec3f(x, y, z); - } - - public RcVec3f(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public RcVec3f(float f) - { - x = f; - y = f; - z = f; - } - - - public RcVec3f(float[] f) - { - x = f[0]; - y = f[1]; - z = f[2]; - } - - public float this[int index] - { - get => GetElement(index); - set => SetElement(index, value); - } - - public float GetElement(int index) - { - switch (index) - { - case 0: return x; - case 1: return y; - case 2: return z; - default: throw new IndexOutOfRangeException($"{index}"); - } - } - - public void SetElement(int index, float value) - { - switch (index) - { - case 0: - x = value; - break; - case 1: - y = value; - break; - case 2: - z = value; - break; - - default: throw new IndexOutOfRangeException($"{index}-{value}"); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(float a, float b, float c) - { - x = a; - y = b; - z = c; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(float[] @in) - { - Set(@in, 0); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(float[] @in, int i) - { - x = @in[i]; - y = @in[i + 1]; - z = @in[i + 2]; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Length() - { - return (float)Math.Sqrt(x * x + y * y + z * z); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly RcVec3f Subtract(RcVec3f right) - { - return new RcVec3f( - x - right.x, - y - right.y, - z - right.z - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly RcVec3f Add(RcVec3f v2) - { - return new RcVec3f( - x + v2.x, - y + v2.y, - z + v2.z - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly RcVec3f Scale(float scale) - { - return new RcVec3f( - x * scale, - y * scale, - z * scale - ); - } - - - /// Derives the dot product of two vectors on the xz-plane. (@p u . @p v) - /// @param[in] u A vector [(x, y, z)] - /// @param[in] v A vector [(x, y, z)] - /// @return The dot product on the xz-plane. - /// - /// The vectors are projected onto the xz-plane, so the y-values are - /// ignored. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Dot2D(RcVec3f v) - { - return x * v.x + z * v.z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly float Dot2D(float[] v, int vi) - { - return x * v[vi] + z * v[vi + 2]; - } - - - public override bool Equals(object obj) - { - if (!(obj is RcVec3f)) - return false; - - return Equals((RcVec3f)obj); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RcVec3f other) - { - return x.Equals(other.x) && - y.Equals(other.y) && - z.Equals(other.z); - } - - - public override int GetHashCode() - { - int hash = x.GetHashCode(); - hash = RcHashCodes.CombineHashCodes(hash, y.GetHashCode()); - hash = RcHashCodes.CombineHashCodes(hash, z.GetHashCode()); - return hash; - } - - /// Normalizes the vector. - /// @param[in,out] v The vector to normalize. [(x, y, z)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Normalize() - { - float d = (float)(1.0f / Math.Sqrt(RcMath.Sqr(x) + RcMath.Sqr(y) + RcMath.Sqr(z))); - if (d != 0) - { - x *= d; - y *= d; - z *= d; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Min(float[] @in, int i) - { - x = Math.Min(x, @in[i]); - y = Math.Min(y, @in[i + 1]); - z = Math.Min(z, @in[i + 2]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Min(RcVec3f b) - { - x = Math.Min(x, b.x); - y = Math.Min(y, b.y); - z = Math.Min(z, b.z); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Max(RcVec3f b) - { - x = Math.Max(x, b.x); - y = Math.Max(y, b.y); - z = Math.Max(z, b.z); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Max(float[] @in, int i) - { - x = Math.Max(x, @in[i]); - y = Math.Max(y, @in[i + 1]); - z = Math.Max(z, @in[i + 2]); - } - - public override string ToString() - { - return $"{x}, {y}, {z}"; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(RcVec3f left, RcVec3f right) - { - return left.Equals(right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(RcVec3f left, RcVec3f right) - { - return !left.Equals(right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f operator -(RcVec3f left, RcVec3f right) - { - return left.Subtract(right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f operator +(RcVec3f left, RcVec3f right) - { - return left.Add(right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f operator *(RcVec3f left, RcVec3f right) - { - return new RcVec3f( - left.x * right.x, - left.y * right.y, - left.z * right.z - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f operator *(RcVec3f left, float right) - { - return left * new RcVec3f(right); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f operator *(float left, RcVec3f right) - { - return right * left; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Cross(RcVec3f v1, RcVec3f v2) - { - return new RcVec3f( - (v1.y * v2.z) - (v1.z * v2.y), - (v1.z * v2.x) - (v1.x * v2.z), - (v1.x * v2.y) - (v1.y * v2.x) - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Lerp(RcVec3f v1, RcVec3f v2, float t) - { - return new RcVec3f( - v1.x + (v2.x - v1.x) * t, - v1.y + (v2.y - v1.y) * t, - v1.z + (v2.z - v1.z) * t - ); - } - - /// Returns the distance between two points. - /// @param[in] v1 A point. [(x, y, z)] - /// @param[in] v2 A point. [(x, y, z)] - /// @return The distance between the two points. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Distance(RcVec3f v1, RcVec3f v2) - { - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(RcVec3f v1, RcVec3f v2) - { - return (v1.x * v2.x) + (v1.y * v2.y) - + (v1.z * v2.z); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(float[] v1, float[] v2) - { - return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dot(float[] v1, RcVec3f v2) - { - return v1[0] * v2.x + v1[1] * v2.y + v1[2] * v2.z; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float PerpXZ(RcVec3f a, RcVec3f b) - { - return (a.x * b.z) - (a.z * b.x); - } - - /// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s)) - /// @param[out] dest The result vector. [(x, y, z)] - /// @param[in] v1 The base vector. [(x, y, z)] - /// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] - /// @param[in] s The amount to scale @p v2 by before adding to @p v1. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Mad(RcVec3f v1, RcVec3f v2, float s) - { - return new RcVec3f() - { - x = v1.x + (v2.x * s), - y = v1.y + (v2.y * s), - z = v1.z + (v2.z * s), - }; - } - - /// Performs a linear interpolation between two vectors. (@p v1 toward @p - /// v2) - /// @param[out] dest The result vector. [(x, y, x)] - /// @param[in] v1 The starting vector. - /// @param[in] v2 The destination vector. - /// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RcVec3f Lerp(float[] verts, int v1, int v2, float t) - { - return new RcVec3f( - verts[v1 + 0] + (verts[v2 + 0] - verts[v1 + 0]) * t, - verts[v1 + 1] + (verts[v2 + 1] - verts[v1 + 1]) * t, - verts[v1 + 2] + (verts[v2 + 2] - verts[v1 + 2]) * t - ); - } - - - /// Returns the distance between two points. - /// @param[in] v1 A point. [(x, y, z)] - /// @param[in] v2 A point. [(x, y, z)] - /// @return The distance between the two points. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistSqr(RcVec3f v1, float[] v2, int i) - { - float dx = v2[i] - v1.x; - float dy = v2[i + 1] - v1.y; - float dz = v2[i + 2] - v1.z; - return dx * dx + dy * dy + dz * dz; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistSqr(RcVec3f v1, RcVec3f v2) - { - float dx = v2.x - v1.x; - float dy = v2.y - v1.y; - float dz = v2.z - v1.z; - return dx * dx + dy * dy + dz * dz; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistSqr(float[] v, int i, int j) - { - float dx = v[i] - v[j]; - float dy = v[i + 1] - v[j + 1]; - float dz = v[i + 2] - v[j + 2]; - return dx * dx + dy * dy + dz * dz; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DistSqr(float[] v1, float[] v2) - { - float dx = v2[0] - v1[0]; - float dy = v2[1] - v1[1]; - float dz = v2[2] - v1[2]; - return dx * dx + dy * dy + dz * dz; - } - - /// Derives the distance between the specified points on the xz-plane. - /// @param[in] v1 A point. [(x, y, z)] - /// @param[in] v2 A point. [(x, y, z)] - /// @return The distance between the point on the xz-plane. - /// - /// The vectors are projected onto the xz-plane, so the y-values are - /// ignored. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dist2D(float[] v1, float[] v2) - { - float dx = v2[0] - v1[0]; - float dz = v2[2] - v1[2]; - return (float)Math.Sqrt(dx * dx + dz * dz); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dist2D(RcVec3f v1, RcVec3f v2) - { - float dx = v2.x - v1.x; - float dz = v2.z - v1.z; - return (float)Math.Sqrt(dx * dx + dz * dz); - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dist2DSqr(float[] v1, float[] v2) - { - float dx = v2[0] - v1[0]; - float dz = v2[2] - v1[2]; - return dx * dx + dz * dz; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dist2DSqr(RcVec3f v1, RcVec3f v2) - { - float dx = v2.x - v1.x; - float dz = v2.z - v1.z; - return dx * dx + dz * dz; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Dist2DSqr(RcVec3f p, float[] verts, int i) - { - float dx = verts[i] - p.x; - float dz = verts[i + 2] - p.z; - return dx * dx + dz * dz; - } - - /// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz) - /// @param[in] u The LHV vector [(x, y, z)] - /// @param[in] v The RHV vector [(x, y, z)] - /// @return The dot product on the xz-plane. - /// - /// The vectors are projected onto the xz-plane, so the y-values are - /// ignored. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Perp2D(RcVec3f u, RcVec3f v) - { - return u.z * v.x - u.x * v.z; - } - - /// Derives the square of the scalar length of the vector. (len * len) - /// @param[in] v The vector. [(x, y, z)] - /// @return The square of the scalar length of the vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float LenSqr(RcVec3f v) - { - return v.x * v.x + v.y * v.y + v.z * v.z; - } - - - /// Checks that the specified vector's components are all finite. - /// @param[in] v A point. [(x, y, z)] - /// @return True if all of the point's components are finite, i.e. not NaN - /// or any of the infinities. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFinite(RcVec3f v) - { - return float.IsFinite(v.x) && float.IsFinite(v.y) && float.IsFinite(v.z); - } - - /// Checks that the specified vector's 2D components are finite. - /// @param[in] v A point. [(x, y, z)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFinite2D(RcVec3f v) - { - return float.IsFinite(v.x) && float.IsFinite(v.z); - } - - - public static void Copy(ref RcVec3f @out, float[] @in, int i) - { - Copy(ref @out, 0, @in, i); - } - - public static void Copy(float[] @out, int n, float[] @in, int m) - { - @out[n] = @in[m]; - @out[n + 1] = @in[m + 1]; - @out[n + 2] = @in[m + 2]; - } - - public static void Copy(float[] @out, int n, RcVec3f @in, int m) - { - @out[n] = @in[m]; - @out[n + 1] = @in[m + 1]; - @out[n + 2] = @in[m + 2]; - } - - public static void Copy(ref RcVec3f @out, int n, float[] @in, int m) - { - @out[n] = @in[m]; - @out[n + 1] = @in[m + 1]; - @out[n + 2] = @in[m + 2]; - } - - public static void Add(ref RcVec3f e0, RcVec3f a, float[] verts, int i) - { - e0.x = a.x + verts[i]; - e0.y = a.y + verts[i + 1]; - e0.z = a.z + verts[i + 2]; - } - - - public static void Sub(ref RcVec3f e0, float[] verts, int i, int j) - { - e0.x = verts[i] - verts[j]; - e0.y = verts[i + 1] - verts[j + 1]; - e0.z = verts[i + 2] - verts[j + 2]; - } - - - public static void Sub(ref RcVec3f e0, RcVec3f i, float[] verts, int j) - { - e0.x = i.x - verts[j]; - e0.y = i.y - verts[j + 1]; - e0.z = i.z - verts[j + 2]; - } - - - public static void Cross(float[] dest, float[] v1, float[] v2) - { - dest[0] = v1[1] * v2[2] - v1[2] * v2[1]; - dest[1] = v1[2] * v2[0] - v1[0] * v2[2]; - dest[2] = v1[0] * v2[1] - v1[1] * v2[0]; - } - - public static void Cross(float[] dest, RcVec3f v1, RcVec3f v2) - { - dest[0] = v1.y * v2.z - v1.z * v2.y; - dest[1] = v1.z * v2.x - v1.x * v2.z; - dest[2] = v1.x * v2.y - v1.y * v2.x; - } - - public static void Cross(ref RcVec3f dest, RcVec3f v1, RcVec3f v2) - { - dest.x = v1.y * v2.z - v1.z * v2.y; - dest.y = v1.z * v2.x - v1.x * v2.z; - dest.z = v1.x * v2.y - v1.y * v2.x; - } - - - public static void Normalize(float[] v) - { - float d = (float)(1.0f / Math.Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])); - v[0] *= d; - v[1] *= d; - v[2] *= d; - } - - public static void Normalize(ref RcVec3f v) - { - float d = (float)(1.0f / Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)); - v.x *= d; - v.y *= d; - v.z *= d; - } - } -} \ No newline at end of file diff --git a/src/DotRecast.Core/SegmentVert.cs b/src/DotRecast.Core/SegmentVert.cs deleted file mode 100644 index 0e04c4e5..00000000 --- a/src/DotRecast.Core/SegmentVert.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace DotRecast.Core -{ - public struct SegmentVert - { - public RcVec3f vmin; - public RcVec3f vmax; - - public SegmentVert(float v0, float v1, float v2, float v3, float v4, float v5) - { - vmin.x = v0; - vmin.y = v1; - vmin.z = v2; - - vmax.x = v3; - vmax.y = v4; - vmax.z = v5; - } - - } -} \ No newline at end of file diff --git a/src/DotRecast.Detour.Crowd/CrowdAgentState.cs b/src/DotRecast.Detour.Crowd/CrowdAgentState.cs deleted file mode 100644 index 06c24f61..00000000 --- a/src/DotRecast.Detour.Crowd/CrowdAgentState.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace DotRecast.Detour.Crowd -{ - /// The type of navigation mesh polygon the agent is currently traversing. - /// @ingroup crowd - public enum CrowdAgentState - { - DT_CROWDAGENT_STATE_INVALID, - - /// < The agent is not in a valid state. - DT_CROWDAGENT_STATE_WALKING, - - /// < The agent is traversing a normal navigation mesh polygon. - DT_CROWDAGENT_STATE_OFFMESH, /// < The agent is traversing an off-mesh connection. - }; -} \ No newline at end of file diff --git a/src/DotRecast.Detour.Crowd/DotRecast.Detour.Crowd.csproj b/src/DotRecast.Detour.Crowd/DotRecast.Detour.Crowd.csproj index 4d6a3447..3bb3ade3 100644 --- a/src/DotRecast.Detour.Crowd/DotRecast.Detour.Crowd.csproj +++ b/src/DotRecast.Detour.Crowd/DotRecast.Detour.Crowd.csproj @@ -1,12 +1,24 @@ - - netstandard2.1 + + netstandard2.1;net6.0;net7.0;net8.0 + DotRecast.Detour.Crowd + README.md + ikpil + DotRecast - a port of Recast Detour, Industry-standard navigation mesh toolset for .NET, C#, Unity3D, games, servers + git + https://github.com/ikpil/DotRecast + https://github.com/ikpil/DotRecast + game gamedev ai csharp server unity navigation game-development unity3d pathfinding pathfinder recast detour navmesh crowd-simulation recastnavigation + https://github.com/ikpil/DotRecast/blob/main/CHANGELOG.md + - + + + - - - + + + diff --git a/src/DotRecast.Detour.Crowd/DtCrowd.cs b/src/DotRecast.Detour.Crowd/DtCrowd.cs index 722c1df3..d90f4ec1 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowd.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowd.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -19,174 +19,164 @@ 3. This notice may not be removed or altered from any source distribution. */ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; +using System.Diagnostics; using DotRecast.Core; -using DotRecast.Detour.Crowd.Tracking; - +using DotRecast.Core.Collections; +using DotRecast.Core.Numerics; namespace DotRecast.Detour.Crowd { - using static DotRecast.Core.RcMath; + /////////////////////////////////////////////////////////////////////////// + +// This section contains detailed documentation for members that don't have +// a source file. It reduces clutter in the main section of the header. /** - * Members in this module implement local steering and dynamic avoidance features. - * - * The crowd is the big beast of the navigation features. It not only handles a lot of the path management for you, but - * also local steering and dynamic avoidance between members of the crowd. I.e. It can keep your agents from running - * into each other. - * - * Main class: Crowd - * - * The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy to use path planning features. But in - * the end they only give you points that your navigation client should be moving toward. When it comes to deciding - * things like agent velocity and steering to avoid other agents, that is up to you to implement. Unless, of course, you - * decide to use Crowd. - * - * Basically, you add an agent to the crowd, providing various configuration settings such as maximum speed and - * acceleration. You also provide a local target to move toward. The crowd manager then provides, with every update, the - * new agent position and velocity for the frame. The movement will be constrained to the navigation mesh, and steering - * will be applied to ensure agents managed by the crowd do not collide with each other. - * - * This is very powerful feature set. But it comes with limitations. - * - * The biggest limitation is that you must give control of the agent's position completely over to the crowd manager. - * You can update things like maximum speed and acceleration. But in order for the crowd manager to do its thing, it - * can't allow you to constantly be giving it overrides to position and velocity. So you give up direct control of the - * agent's movement. It belongs to the crowd. - * - * The second biggest limitation revolves around the fact that the crowd manager deals with local planning. So the - * agent's target should never be more than 256 polygons away from its current position. If it is, you risk your agent - * failing to reach its target. So you may still need to do long distance planning and provide the crowd manager with - * intermediate targets. - * - * Other significant limitations: - * - * - All agents using the crowd manager will use the same #dtQueryFilter. - Crowd management is relatively expensive. - * The maximum agents under crowd management at any one time is between 20 and 30. A good place to start is a maximum of - * 25 agents for 0.5ms per frame. - * - * @note This is a summary list of members. Use the index or search feature to find minor members. - * - * @struct dtCrowdAgentParams - * @see CrowdAgent, Crowd::AddAgent(), Crowd::UpdateAgentParameters() - * - * @var dtCrowdAgentParams::obstacleAvoidanceType - * @par - * - * #dtCrowd permits agents to use different avoidance configurations. This value is the index of the - * #dtObstacleAvoidanceParams within the crowd. - * - * @see dtObstacleAvoidanceParams, dtCrowd::SetObstacleAvoidanceParams(), dtCrowd::GetObstacleAvoidanceParams() - * - * @var dtCrowdAgentParams::collisionQueryRange - * @par - * - * Collision elements include other agents and navigation mesh boundaries. - * - * This value is often based on the agent radius and/or maximum speed. E.g. radius * 8 - * - * @var dtCrowdAgentParams::pathOptimizationRange - * @par - * - * Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag. - * - * This value is often based on the agent radius. E.g. radius * 30 - * - * @see dtPathCorridor::OptimizePathVisibility() - * - * @var dtCrowdAgentParams::separationWeight - * @par - * - * A higher value will result in agents trying to stay farther away from each other at the cost of more difficult - * steering in tight spaces. - * - */ - /** - * This is the core class of the refs crowd module. See the refs crowd documentation for a summary of the crowd - * features. A common method for setting up the crowd is as follows: -# Allocate the crowd -# Set the avoidance - * configurations using #SetObstacleAvoidanceParams(). -# Add agents using #AddAgent() and make an initial movement - * request using #RequestMoveTarget(). A common process for managing the crowd is as follows: -# Call #Update() to allow - * the crowd to manage its agents. -# Retrieve agent information using #GetActiveAgents(). -# Make movement requests - * using #RequestMoveTarget() when movement goal changes. -# Repeat every frame. Some agent configuration settings can - * be updated using #UpdateAgentParameters(). But the crowd owns the agent position. So it is not possible to update an - * active agent's position. If agent position must be fed back into the crowd, the agent must be removed and re-added. - * Notes: - Path related information is available for newly added agents only after an #Update() has been performed. - - * Agent objects are kept in a pool and re-used. So it is important when using agent objects to check the value of - * #dtCrowdAgent::active to determine if the agent is actually in use or not. - This class is meant to provide 'local' - * movement. There is a limit of 256 polygons in the path corridor. So it is not meant to provide automatic pathfinding - * services over long distances. - * - * @see DtAllocCrowd(), DtFreeCrowd(), Init(), dtCrowdAgent - */ + + @defgroup crowd Crowd + + Members in this module implement local steering and dynamic avoidance features. + + The crowd is the big beast of the navigation features. It not only handles a + lot of the path management for you, but also local steering and dynamic + avoidance between members of the crowd. I.e. It can keep your agents from + running into each other. + + Main class: #dtCrowd + + The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy + to use path planning features. But in the end they only give you points that + your navigation client should be moving toward. When it comes to deciding things + like agent velocity and steering to avoid other agents, that is up to you to + implement. Unless, of course, you decide to use #dtCrowd. + + Basically, you add an agent to the crowd, providing various configuration + settings such as maximum speed and acceleration. You also provide a local + target to more toward. The crowd manager then provides, with every update, the + new agent position and velocity for the frame. The movement will be + constrained to the navigation mesh, and steering will be applied to ensure + agents managed by the crowd do not collide with each other. + + This is very powerful feature set. But it comes with limitations. + + The biggest limitation is that you must give control of the agent's position + completely over to the crowd manager. You can update things like maximum speed + and acceleration. But in order for the crowd manager to do its thing, it can't + allow you to constantly be giving it overrides to position and velocity. So + you give up direct control of the agent's movement. It belongs to the crowd. + + The second biggest limitation revolves around the fact that the crowd manager + deals with local planning. So the agent's target should never be more than + 256 polygons aways from its current position. If it is, you risk + your agent failing to reach its target. So you may still need to do long + distance planning and provide the crowd manager with intermediate targets. + + Other significant limitations: + + - All agents using the crowd manager will use the same #dtQueryFilter. + - Crowd management is relatively expensive. The maximum agents under crowd + management at any one time is between 20 and 30. A good place to start + is a maximum of 25 agents for 0.5ms per frame. + + @note This is a summary list of members. Use the index or search + feature to find minor members. + + @struct dtCrowdAgentParams + @see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters() + + @var dtCrowdAgentParams::obstacleAvoidanceType + @par + + #dtCrowd permits agents to use different avoidance configurations. This value + is the index of the #dtObstacleAvoidanceParams within the crowd. + + @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), + dtCrowd::getObstacleAvoidanceParams() + + @var dtCrowdAgentParams::collisionQueryRange + @par + + Collision elements include other agents and navigation mesh boundaries. + + This value is often based on the agent radius and/or maximum speed. E.g. radius * 8 + + @var dtCrowdAgentParams::pathOptimizationRange + @par + + Only applicable if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag. + + This value is often based on the agent radius. E.g. radius * 30 + + @see dtPathCorridor::optimizePathVisibility() + + @var dtCrowdAgentParams::separationWeight + @par + + A higher value will result in agents trying to stay farther away from each other at + the cost of more difficult steering in tight spaces. + */ + /// Provides local steering behaviors for a group of agents. + /// @ingroup crowd public class DtCrowd { - /// The maximum number of corners a crowd agent will look ahead in the path. - /// This value is used for sizing the crowd agent corner buffers. - /// Due to the behavior of the crowd manager, the actual number of useful - /// corners will be one less than this number. - /// @ingroup crowd - public const int DT_CROWDAGENT_MAX_CORNERS = 4; - - /// The maximum number of crowd avoidance configurations supported by the - /// crowd manager. - /// @ingroup crowd - /// @see dtObstacleAvoidanceParams, dtCrowd::SetObstacleAvoidanceParams(), dtCrowd::GetObstacleAvoidanceParams(), - /// dtCrowdAgentParams::obstacleAvoidanceType - public const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8; - - /// The maximum number of query filter types supported by the crowd manager. - /// @ingroup crowd - /// @see dtQueryFilter, dtCrowd::GetFilter() dtCrowd::GetEditableFilter(), - /// dtCrowdAgentParams::queryFilterType - public const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16; - - private readonly RcAtomicInteger _agentId = new RcAtomicInteger(); - private readonly List _agents; + private readonly RcAtomicInteger _agentIdx; + private readonly Dictionary _agents; + private readonly List _activeAgents; + private readonly DtPathQueue _pathQ; - private readonly DtObstacleAvoidanceParams[] _obstacleQueryParams = new DtObstacleAvoidanceParams[DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]; + + private readonly DtObstacleAvoidanceParams[] _obstacleQueryParams; private readonly DtObstacleAvoidanceQuery _obstacleQuery; + private DtProximityGrid _grid; - private readonly RcVec3f _ext = new RcVec3f(); - private readonly IDtQueryFilter[] _filters = new IDtQueryFilter[DT_CROWD_MAX_QUERY_FILTER_TYPE]; + + private int _maxPathResult; + private readonly RcVec3f _agentPlacementHalfExtents; + + private readonly IDtQueryFilter[] _filters; + + private readonly DtCrowdConfig _config; + private int _velocitySampleCount; + private DtNavMeshQuery _navQuery; + private DtNavMesh _navMesh; - private readonly DtCrowdConfig _config; private readonly DtCrowdTelemetry _telemetry = new DtCrowdTelemetry(); - private int _velocitySampleCount; - public DtCrowd(DtCrowdConfig config, DtNavMesh nav) : - this(config, nav, i => new DtQueryDefaultFilter()) + public DtCrowd(DtCrowdConfig config, DtNavMesh nav) : this(config, nav, i => new DtQueryDefaultFilter()) { } public DtCrowd(DtCrowdConfig config, DtNavMesh nav, Func queryFilterFactory) { _config = config; - _ext.Set(config.maxAgentRadius * 2.0f, config.maxAgentRadius * 1.5f, config.maxAgentRadius * 2.0f); + _agentPlacementHalfExtents = new RcVec3f(config.maxAgentRadius * 2.0f, config.maxAgentRadius * 1.5f, config.maxAgentRadius * 2.0f); _obstacleQuery = new DtObstacleAvoidanceQuery(config.maxObstacleAvoidanceCircles, config.maxObstacleAvoidanceSegments); - for (int i = 0; i < DT_CROWD_MAX_QUERY_FILTER_TYPE; i++) + _filters = new IDtQueryFilter[DtCrowdConst.DT_CROWD_MAX_QUERY_FILTER_TYPE]; + for (int i = 0; i < DtCrowdConst.DT_CROWD_MAX_QUERY_FILTER_TYPE; i++) { _filters[i] = queryFilterFactory.Invoke(i); } // Init obstacle query option. - for (int i = 0; i < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS; ++i) + _obstacleQueryParams = new DtObstacleAvoidanceParams[DtCrowdConst.DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]; + for (int i = 0; i < DtCrowdConst.DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS; ++i) { _obstacleQueryParams[i] = new DtObstacleAvoidanceParams(); } // Allocate temp buffer for merging paths. + _maxPathResult = DtCrowdConst.MAX_PATH_RESULT; _pathQ = new DtPathQueue(config); - _agents = new List(); + _agentIdx = new RcAtomicInteger(0); + _agents = new Dictionary(); + _activeAgents = new List(); // The navQuery is mostly used for local searches, no need for large node pool. - _navMesh = nav; - _navQuery = new DtNavMeshQuery(nav); + SetNavMesh(nav); } public void SetNavMesh(DtNavMesh nav) @@ -195,13 +185,23 @@ public void SetNavMesh(DtNavMesh nav) _navQuery = new DtNavMeshQuery(nav); } + public DtNavMesh GetNavMesh() + { + return _navMesh; + } + + public DtNavMeshQuery GetNavMeshQuery() + { + return _navQuery; + } + /// Sets the shared avoidance configuration for the specified index. /// @param[in] idx The index. [Limits: 0 <= value < /// #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS] /// @param[in] option The new configuration. public void SetObstacleAvoidanceParams(int idx, DtObstacleAvoidanceParams option) { - if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) + if (idx >= 0 && idx < DtCrowdConst.DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) { _obstacleQueryParams[idx] = new DtObstacleAvoidanceParams(option); } @@ -213,7 +213,7 @@ public void SetObstacleAvoidanceParams(int idx, DtObstacleAvoidanceParams option /// @return The requested configuration. public DtObstacleAvoidanceParams GetObstacleAvoidanceParams(int idx) { - if (idx >= 0 && idx < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) + if (idx >= 0 && idx < DtCrowdConst.DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS) { return _obstacleQueryParams[idx]; } @@ -229,23 +229,23 @@ public void UpdateAgentParameters(DtCrowdAgent agent, DtCrowdAgentParams option) agent.option = option; } - /** - * Adds a new agent to the crowd. - * - * @param pos - * The requested position of the agent. [(x, y, z)] - * @param params - * The configutation of the agent. - * @return The newly created agent object - */ + /// @par + /// + /// The agent's position will be constrained to the surface of the navigation mesh. + /// Adds a new agent to the crowd. + /// @param[in] pos The requested position of the agent. [(x, y, z)] + /// @param[in] params The configuration of the agent. + /// @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) { - DtCrowdAgent ag = new DtCrowdAgent(_agentId.GetAndIncrement()); - _agents.Add(ag); + int idx = _agentIdx.GetAndIncrement(); + DtCrowdAgent ag = new DtCrowdAgent(idx); + ag.corridor.Init(_maxPathResult); + AddAgent(ag); UpdateAgentParameters(ag, option); // Find nearest position on navmesh and place the agent there. - var status = _navQuery.FindNearestPoly(pos, _ext, _filters[ag.option.queryFilterType], out var refs, out var nearestPt, out var _); + var status = _navQuery.FindNearestPoly(pos, _agentPlacementHalfExtents, _filters[ag.option.queryFilterType], out var refs, out var nearestPt, out var _); if (status.Failed()) { nearestPt = pos; @@ -258,6 +258,7 @@ public DtCrowdAgent AddAgent(RcVec3f pos, DtCrowdAgentParams option) ag.topologyOptTime = 0; ag.targetReplanTime = 0; + ag.nneis = 0; ag.dvel = RcVec3f.Zero; ag.nvel = RcVec3f.Zero; @@ -268,27 +269,39 @@ public DtCrowdAgent AddAgent(RcVec3f pos, DtCrowdAgentParams option) if (refs != 0) { - ag.state = CrowdAgentState.DT_CROWDAGENT_STATE_WALKING; + ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING; } else { - ag.state = CrowdAgentState.DT_CROWDAGENT_STATE_INVALID; + ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID; } - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_NONE; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE; return ag; } - /** - * Removes the agent from the crowd. - * - * @param agent - * Agent to be removed - */ + public DtCrowdAgent GetAgent(int idx) + { + return _agents.GetValueOrDefault(idx); + } + + // Add the agent from the crowd. + public void AddAgent(DtCrowdAgent agent) + { + if (_agents.TryAdd(agent.idx, agent)) + { + _activeAgents.Add(agent); + } + } + + // Removes the agent from the crowd. public void RemoveAgent(DtCrowdAgent agent) { - _agents.Remove(agent); + if (_agents.Remove(agent.idx)) + { + _activeAgents.Remove(agent); + } } private bool RequestMoveTargetReplan(DtCrowdAgent ag, long refs, RcVec3f pos) @@ -333,7 +346,7 @@ public bool RequestMoveVelocity(DtCrowdAgent agent, RcVec3f vel) agent.targetPos = vel; agent.targetPathQueryResult = null; agent.targetReplan = false; - agent.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY; + agent.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY; return true; } @@ -349,7 +362,7 @@ public bool ResetMoveTarget(DtCrowdAgent agent) agent.dvel = RcVec3f.Zero; agent.targetPathQueryResult = null; agent.targetReplan = false; - agent.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_NONE; + agent.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE; return true; } @@ -360,17 +373,17 @@ public bool ResetMoveTarget(DtCrowdAgent agent) */ public IList GetActiveAgents() { - return _agents; + return _activeAgents; } public RcVec3f GetQueryExtents() { - return _ext; + return _agentPlacementHalfExtents; } public IDtQueryFilter GetFilter(int i) { - return i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE ? _filters[i] : null; + return i >= 0 && i < DtCrowdConst.DT_CROWD_MAX_QUERY_FILTER_TYPE ? _filters[i] : null; } public DtProximityGrid GetGrid() @@ -444,11 +457,12 @@ public DtCrowdTelemetry Update(float dt, DtCrowdAgentDebugInfo debug) private void CheckPathValidity(IList agents, float dt) { - _telemetry.Start("checkPathValidity"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.CheckPathValidity); - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } @@ -465,7 +479,7 @@ private void CheckPathValidity(IList agents, float dt) { // Current location is not valid, try to reposition. // TODO: this can snap agents, how to handle that? - _navQuery.FindNearestPoly(ag.npos, _ext, _filters[ag.option.queryFilterType], out agentRef, out var nearestPt, out var _); + _navQuery.FindNearestPoly(ag.npos, _agentPlacementHalfExtents, _filters[ag.option.queryFilterType], out agentRef, out var nearestPt, out var _); agentPos = nearestPt; if (agentRef == 0) @@ -474,7 +488,7 @@ private void CheckPathValidity(IList agents, float dt) ag.corridor.Reset(0, agentPos); ag.partial = false; ag.boundary.Reset(); - ag.state = CrowdAgentState.DT_CROWDAGENT_STATE_INVALID; + ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID; continue; } @@ -492,20 +506,20 @@ private void CheckPathValidity(IList agents, float dt) // If the agent does not have move target or is controlled by // velocity, no need to recover the target nor replan. - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } // Try to recover move request position. - if (ag.targetState != MoveRequestState.DT_CROWDAGENT_TARGET_NONE - && ag.targetState != MoveRequestState.DT_CROWDAGENT_TARGET_FAILED) + if (ag.targetState != DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + && ag.targetState != DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED) { if (!_navQuery.IsValidPolyRef(ag.targetRef, _filters[ag.option.queryFilterType])) { // Current target is not valid, try to reposition. - _navQuery.FindNearestPoly(ag.targetPos, _ext, _filters[ag.option.queryFilterType], out ag.targetRef, out var nearestPt, out var _); + _navQuery.FindNearestPoly(ag.targetPos, _agentPlacementHalfExtents, _filters[ag.option.queryFilterType], out ag.targetRef, out var nearestPt, out var _); ag.targetPos = nearestPt; replan = true; } @@ -515,7 +529,7 @@ private void CheckPathValidity(IList agents, float dt) // Failed to reposition target, fail moverequest. ag.corridor.Reset(agentRef, agentPos); ag.partial = false; - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_NONE; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE; } } @@ -529,9 +543,8 @@ private void CheckPathValidity(IList agents, float dt) replan = true; } - // If the end of the path is near and it is not the requested - // location, replan. - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VALID) + // If the end of the path is near and it is not the requested location, replan. + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID) { if (ag.targetReplanTime > _config.targetReplanDelay && ag.corridor.GetPathCount() < _config.checkLookAhead && ag.corridor.GetLastPoly() != ag.targetRef) @@ -543,38 +556,37 @@ private void CheckPathValidity(IList agents, float dt) // Try to replan path to goal. if (replan) { - if (ag.targetState != MoveRequestState.DT_CROWDAGENT_TARGET_NONE) + if (ag.targetState != DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE) { RequestMoveTargetReplan(ag, ag.targetRef, ag.targetPos); } } } - - _telemetry.Stop("checkPathValidity"); } private void UpdateMoveRequest(IList agents, float dt) { - _telemetry.Start("updateMoveRequest"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.UpdateMoveRequest); RcSortedQueue queue = new RcSortedQueue((a1, a2) => a2.targetReplanTime.CompareTo(a1.targetReplanTime)); // Fire off new requests. List reqPath = new List(); - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state == CrowdAgentState.DT_CROWDAGENT_STATE_INVALID) + var ag = agents[i]; + if (ag.state == DtCrowdAgentState.DT_CROWDAGENT_STATE_INVALID) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_REQUESTING) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_REQUESTING) { List path = ag.corridor.GetPath(); if (0 == path.Count) @@ -592,7 +604,7 @@ private void UpdateMoveRequest(IList agents, float dt) if (ag.targetReplan) // && npath > 10) { // Try to use existing steady path during replan if possible. - status = _navQuery.FinalizeSlicedFindPathPartial(path, ref reqPath); + status = _navQuery.FinalizeSlicedFindPathPartial(path, path.Count, ref reqPath); } else { @@ -634,19 +646,19 @@ private void UpdateMoveRequest(IList agents, float dt) if (reqPath[reqPath.Count - 1] == ag.targetRef) { - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_VALID; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID; ag.targetReplanTime = 0; } else { // The path is longer or potentially unreachable, full plan. - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE; } ag.targetReplanWaitTime = 0; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE) { queue.Enqueue(ag); } @@ -655,11 +667,10 @@ private void UpdateMoveRequest(IList agents, float dt) while (!queue.IsEmpty()) { DtCrowdAgent ag = queue.Dequeue(); - ag.targetPathQueryResult = _pathQ.Request(ag.corridor.GetLastPoly(), ag.targetRef, ag.corridor.GetTarget(), - ag.targetPos, _filters[ag.option.queryFilterType]); + ag.targetPathQueryResult = _pathQ.Request(ag.corridor.GetLastPoly(), ag.targetRef, ag.corridor.GetTarget(), ag.targetPos, _filters[ag.option.queryFilterType]); if (ag.targetPathQueryResult != null) { - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_PATH; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_PATH; } else { @@ -669,20 +680,22 @@ private void UpdateMoveRequest(IList agents, float dt) } // Update requests. - _telemetry.Start("pathQueueUpdate"); - _pathQ.Update(_navMesh); - _telemetry.Stop("pathQueueUpdate"); + using (var timer2 = _telemetry.ScopedTimer(DtCrowdTimerLabel.PathQueueUpdate)) + { + _pathQ.Update(_navMesh); + } // Process path results. - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + var ag = agents[i]; + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_PATH) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_WAITING_FOR_PATH) { // _telemetry.RecordPathWaitTime(ag.targetReplanTime); // Poll path queue. @@ -694,11 +707,11 @@ private void UpdateMoveRequest(IList agents, float dt) ag.targetPathQueryResult = null; if (ag.targetRef != 0) { - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_REQUESTING; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_REQUESTING; } else { - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_FAILED; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED; } ag.targetReplanTime = 0; @@ -790,12 +803,12 @@ private void UpdateMoveRequest(IList agents, float dt) ag.corridor.SetCorridor(targetPos, res); // Force to update boundary. ag.boundary.Reset(); - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_VALID; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_VALID; } else { // Something went wrong. - ag.targetState = MoveRequestState.DT_CROWDAGENT_TARGET_FAILED; + ag.targetState = DtMoveRequestState.DT_CROWDAGENT_TARGET_FAILED; } ag.targetReplanTime = 0; @@ -805,30 +818,29 @@ private void UpdateMoveRequest(IList agents, float dt) ag.targetReplanWaitTime += dt; } } - - _telemetry.Stop("updateMoveRequest"); } private void UpdateTopologyOptimization(IList agents, float dt) { - _telemetry.Start("updateTopologyOptimization"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.UpdateTopologyOptimization); RcSortedQueue queue = new RcSortedQueue((a1, a2) => a2.topologyOptTime.CompareTo(a1.topologyOptTime)); - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } - if ((ag.option.updateFlags & DtCrowdAgentParams.DT_CROWD_OPTIMIZE_TOPO) == 0) + if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OPTIMIZE_TOPO) == 0) { continue; } @@ -846,31 +858,31 @@ private void UpdateTopologyOptimization(IList agents, float dt) ag.corridor.OptimizePathTopology(_navQuery, _filters[ag.option.queryFilterType], _config.maxTopologyOptimizationIterations); ag.topologyOptTime = 0; } - - _telemetry.Stop("updateTopologyOptimization"); } private void BuildProximityGrid(IList agents) { - _telemetry.Start("buildProximityGrid"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.BuildProximityGrid); + _grid = new DtProximityGrid(_config.maxAgentRadius * 3); - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { + var ag = agents[i]; RcVec3f p = ag.npos; float r = ag.option.radius; - _grid.AddItem(ag, p.x - r, p.z - r, p.x + r, p.z + r); + _grid.AddItem(ag, p.X - r, p.Z - r, p.X + r, p.Z + r); } - - _telemetry.Stop("buildProximityGrid"); } private void BuildNeighbours(IList agents) { - _telemetry.Start("buildNeighbours"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.BuildNeighbours); + + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } @@ -878,7 +890,7 @@ private void BuildNeighbours(IList agents) // Update the collision boundary after certain distance has been passed or // if it has become invalid. float updateThr = ag.option.collisionQueryRange * 0.25f; - if (RcVec3f.Dist2DSqr(ag.npos, ag.boundary.GetCenter()) > Sqr(updateThr) + if (RcVec.Dist2DSqr(ag.npos, ag.boundary.GetCenter()) > RcMath.Sqr(updateThr) || !ag.boundary.IsValid(_navQuery, _filters[ag.option.queryFilterType])) { ag.boundary.Update(ag.corridor.GetFirstPoly(), ag.npos, ag.option.collisionQueryRange, _navQuery, @@ -886,72 +898,118 @@ private void BuildNeighbours(IList 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); } - - _telemetry.Stop("buildNeighbours"); } + public static int AddNeighbour(DtCrowdAgent idx, float dist, Span neis, int nneis, int maxNeis) + { + // 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, ref List result, DtProximityGrid grid) + private int GetNeighbours(RcVec3f pos, float height, float range, DtCrowdAgent skip, DtCrowdNeighbour[] result, int maxResult, DtProximityGrid grid) { - result.Clear(); + int n = 0; - var proxAgents = new HashSet(); - int nids = grid.QueryItems(pos.x - range, pos.z - range, pos.x + range, pos.z + range, ref proxAgents); - foreach (DtCrowdAgent ag in proxAgents) + const int MAX_NEIS = 32; + Span ids = stackalloc int[MAX_NEIS]; + int nids = grid.QueryItems(pos.X - range, pos.Z - range, + pos.X + range, pos.Z + range, + ids, ids.Length); + + for (int i = 0; i < nids; ++i) { + var ag = GetAgent(ids[i]); if (ag == skip) { continue; } // Check for overlap. - RcVec3f diff = pos.Subtract(ag.npos); - if (Math.Abs(diff.y) >= (height + ag.option.height) / 2.0f) + RcVec3f diff = RcVec3f.Subtract(pos, ag.npos); + if (MathF.Abs(diff.Y) >= (height + ag.option.height) / 2.0f) { continue; } - diff.y = 0; - float distSqr = RcVec3f.LenSqr(diff); - if (distSqr > Sqr(range)) + diff.Y = 0; + float distSqr = diff.LengthSquared(); + if (distSqr > RcMath.Sqr(range)) { continue; } - result.Add(new DtCrowdNeighbour(ag, distSqr)); + n = AddNeighbour(ag, distSqr, result, n, maxResult); } - result.Sort((o1, o2) => o1.dist.CompareTo(o2.dist)); - return result.Count; + return n; } private void FindCorners(IList agents, DtCrowdAgentDebugInfo debug) { - _telemetry.Start("findCorners"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.FindCorners); + DtCrowdAgent debugAgent = debug != null ? debug.agent : null; - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } // Find corners for steering - ag.corridor.FindCorners(ref ag.corners, DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]); + ag.ncorners = ag.corridor.FindCorners(ag.corners, DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS, _navQuery, _filters[ag.option.queryFilterType]); // Check to see if the corner after the next corner is directly visible, // and short cut to there. - if ((ag.option.updateFlags & DtCrowdAgentParams.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)].GetPos(); + RcVec3f target = ag.corners[Math.Min(1, ag.ncorners - 1)].pos; ag.corridor.OptimizePathVisibility(target, ag.option.pathOptimizationRange, _navQuery, _filters[ag.option.queryFilterType]); @@ -972,22 +1030,22 @@ private void FindCorners(IList agents, DtCrowdAgentDebugInfo debug } } } - - _telemetry.Stop("findCorners"); } private void TriggerOffMeshConnections(IList agents) { - _telemetry.Start("triggerOffMeshConnections"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.TriggerOffMeshConnections); + + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { continue; } @@ -1001,18 +1059,18 @@ private void TriggerOffMeshConnections(IList agents) // Adjust the path over the off-mesh connection. long[] refs = new long[2]; - if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.corners.Count - 1].GetRef(), refs, ref anim.startPos, + if (ag.corridor.MoveOverOffmeshConnection(ag.corners[ag.ncorners - 1].refs, refs, ref anim.startPos, ref anim.endPos, _navQuery)) { anim.initPos = ag.npos; anim.polyRef = refs[1]; anim.active = true; anim.t = 0.0f; - anim.tmax = (RcVec3f.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 = CrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH; - ag.corners.Clear(); - ag.neis.Clear(); + ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_OFFMESH; + ag.ncorners = 0; + ag.nneis = 0; continue; } else @@ -1021,28 +1079,28 @@ private void TriggerOffMeshConnections(IList agents) } } } - - _telemetry.Stop("triggerOffMeshConnections"); } private void CalculateSteering(IList agents) { - _telemetry.Start("calculateSteering"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.CalculateSteering); + + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE) { continue; } RcVec3f dvel = new RcVec3f(); - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { dvel = ag.targetPos; ag.desiredSpeed = ag.targetPos.Length(); @@ -1050,7 +1108,7 @@ private void CalculateSteering(IList agents) else { // Calculate steering direction. - if ((ag.option.updateFlags & DtCrowdAgentParams.DT_CROWD_ANTICIPATE_TURNS) != 0) + if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_ANTICIPATE_TURNS) != 0) { dvel = ag.CalcSmoothSteerDirection(); } @@ -1064,11 +1122,11 @@ private void CalculateSteering(IList agents) float speedScale = ag.GetDistanceToGoal(slowDownRadius) / slowDownRadius; ag.desiredSpeed = ag.option.maxSpeed; - dvel = dvel.Scale(ag.desiredSpeed * speedScale); + dvel = dvel * (ag.desiredSpeed * speedScale); } // Separation - if ((ag.option.updateFlags & DtCrowdAgentParams.DT_CROWD_SEPARATION) != 0) + if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_SEPARATION) != 0) { float separationDist = ag.option.collisionQueryRange; float invSeparationDist = 1.0f / separationDist; @@ -1077,41 +1135,41 @@ private void CalculateSteering(IList agents) float w = 0; RcVec3f disp = new RcVec3f(); - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; - RcVec3f diff = ag.npos.Subtract(nei.npos); - diff.y = 0; + RcVec3f diff = RcVec3f.Subtract(ag.npos, nei.npos); + diff.Y = 0; - float distSqr = RcVec3f.LenSqr(diff); + float distSqr = diff.LengthSquared(); if (distSqr < 0.00001f) { continue; } - if (distSqr > Sqr(separationDist)) + if (distSqr > RcMath.Sqr(separationDist)) { continue; } - float dist = (float)Math.Sqrt(distSqr); - float weight = separationWeight * (1.0f - Sqr(dist * invSeparationDist)); + float dist = MathF.Sqrt(distSqr); + float weight = separationWeight * (1.0f - RcMath.Sqr(dist * invSeparationDist)); - disp = RcVec3f.Mad(disp, diff, weight / dist); + disp = RcVec.Mad(disp, diff, weight / dist); w += 1.0f; } if (w > 0.0001f) { // Adjust desired velocity. - dvel = RcVec3f.Mad(dvel, disp, 1.0f / w); + dvel = RcVec.Mad(dvel, disp, 1.0f / w); // Clamp desired velocity to desired speed. - float speedSqr = RcVec3f.LenSqr(dvel); - float desiredSqr = Sqr(ag.desiredSpeed); + float speedSqr = dvel.LengthSquared(); + float desiredSqr = RcMath.Sqr(ag.desiredSpeed); if (speedSqr > desiredSqr) { - dvel = dvel.Scale(desiredSqr / speedSqr); + dvel = dvel * (desiredSqr / speedSqr); } } } @@ -1119,27 +1177,27 @@ private void CalculateSteering(IList agents) // Set the desired velocity. ag.dvel = dvel; } - - _telemetry.Stop("calculateSteering"); } private void PlanVelocity(DtCrowdAgentDebugInfo debug, IList agents) { - _telemetry.Start("planVelocity"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.PlanVelocity); + DtCrowdAgent debugAgent = debug != null ? debug.agent : null; - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - if ((ag.option.updateFlags & DtCrowdAgentParams.DT_CROWD_OBSTACLE_AVOIDANCE) != 0) + if ((ag.option.updateFlags & DtCrowdAgentUpdateFlags.DT_CROWD_OBSTACLE_AVOIDANCE) != 0) { _obstacleQuery.Reset(); // Add neighbours as obstacles. - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; _obstacleQuery.AddCircle(nei.npos, nei.option.radius, nei.vel, nei.dvel); @@ -1150,8 +1208,8 @@ private void PlanVelocity(DtCrowdAgentDebugInfo debug, IList agent { RcVec3f[] s = ag.boundary.GetSegment(j); RcVec3f s3 = s[1]; - //Array.Copy(s, 3, s3, 0, 3); - if (DetourCommon.TriArea2D(ag.npos, s[0], s3) < 0.0f) + //RcArrays.Copy(s, 3, s3, 0, 3); + if (DtUtils.TriArea2D(ag.npos, s[0], s3) < 0.0f) { continue; } @@ -1190,35 +1248,35 @@ private void PlanVelocity(DtCrowdAgentDebugInfo debug, IList agent ag.nvel = ag.dvel; } } - - _telemetry.Stop("planVelocity"); } private void Integrate(float dt, IList agents) { - _telemetry.Start("integrate"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.Integrate); + + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } ag.Integrate(dt); } - - _telemetry.Stop("integrate"); } private void HandleCollisions(IList agents) { - _telemetry.Start("handleCollisions"); + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.HandleCollisions); + for (int iter = 0; iter < 4; ++iter) { - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { + var ag = agents[i]; long idx0 = ag.idx; - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } @@ -1227,31 +1285,31 @@ private void HandleCollisions(IList agents) float w = 0; - for (int j = 0; j < ag.neis.Count; ++j) + for (int j = 0; j < ag.nneis; ++j) { DtCrowdAgent nei = ag.neis[j].agent; long idx1 = nei.idx; - RcVec3f diff = ag.npos.Subtract(nei.npos); - diff.y = 0; + RcVec3f diff = RcVec3f.Subtract(ag.npos, nei.npos); + diff.Y = 0; - float dist = RcVec3f.LenSqr(diff); - if (dist > Sqr(ag.option.radius + nei.option.radius)) + float dist = diff.LengthSquared(); + if (dist > RcMath.Sqr(ag.option.radius + nei.option.radius)) { continue; } - dist = (float)Math.Sqrt(dist); + dist = MathF.Sqrt(dist); float pen = (ag.option.radius + nei.option.radius) - dist; if (dist < 0.0001f) { // Agents on top of each other, try to choose diverging separation directions. if (idx0 > idx1) { - diff.Set(-ag.dvel.z, 0, ag.dvel.x); + diff = new RcVec3f(-ag.dvel.Z, 0, ag.dvel.X); } else { - diff.Set(ag.dvel.z, 0, -ag.dvel.x); + diff = new RcVec3f(ag.dvel.Z, 0, -ag.dvel.X); } pen = 0.01f; @@ -1261,7 +1319,7 @@ private void HandleCollisions(IList agents) pen = (1.0f / dist) * (pen * 0.5f) * _config.collisionResolveFactor; } - ag.disp = RcVec3f.Mad(ag.disp, diff, pen); + ag.disp = RcVec.Mad(ag.disp, diff, pen); w += 1.0f; } @@ -1269,30 +1327,31 @@ private void HandleCollisions(IList agents) if (w > 0.0001f) { float iw = 1.0f / w; - ag.disp = ag.disp.Scale(iw); + ag.disp = ag.disp * iw; } } - foreach (DtCrowdAgent ag in agents) + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } - ag.npos = ag.npos.Add(ag.disp); + ag.npos = RcVec3f.Add(ag.npos, ag.disp); } } - - _telemetry.Stop("handleCollisions"); } private void MoveAgents(IList agents) { - _telemetry.Start("moveAgents"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.MoveAgents); + + for (var i = 0; i < agents.Count; i++) { - if (ag.state != CrowdAgentState.DT_CROWDAGENT_STATE_WALKING) + var ag = agents[i]; + if (ag.state != DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING) { continue; } @@ -1303,22 +1362,22 @@ private void MoveAgents(IList agents) ag.npos = ag.corridor.GetPos(); // If not using path, truncate the corridor to just one poly. - if (ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_NONE - || ag.targetState == MoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) + if (ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_NONE + || ag.targetState == DtMoveRequestState.DT_CROWDAGENT_TARGET_VELOCITY) { ag.corridor.Reset(ag.corridor.GetFirstPoly(), ag.npos); ag.partial = false; } } - - _telemetry.Stop("moveAgents"); } private void UpdateOffMeshConnections(IList agents, float dt) { - _telemetry.Start("updateOffMeshConnections"); - foreach (DtCrowdAgent ag in agents) + using var timer = _telemetry.ScopedTimer(DtCrowdTimerLabel.UpdateOffMeshConnections); + + for (var i = 0; i < agents.Count; i++) { + var ag = agents[i]; DtCrowdAgentAnimation anim = ag.animation; if (!anim.active) { @@ -1331,7 +1390,7 @@ private void UpdateOffMeshConnections(IList agents, float dt) // Reset animation anim.active = false; // Prepare agent for walking. - ag.state = CrowdAgentState.DT_CROWDAGENT_STATE_WALKING; + ag.state = DtCrowdAgentState.DT_CROWDAGENT_STATE_WALKING; continue; } @@ -1353,13 +1412,11 @@ private void UpdateOffMeshConnections(IList agents, float dt) ag.vel = RcVec3f.Zero; ag.dvel = RcVec3f.Zero; } - - _telemetry.Stop("updateOffMeshConnections"); } private float Tween(float t, float t0, float t1) { - return Clamp((t - t0) / (t1 - t0), 0.0f, 1.0f); + return Math.Clamp((t - t0) / (t1 - t0), 0.0f, 1.0f); } } } \ No newline at end of file diff --git a/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs b/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs index 81d6957e..608b830f 100644 --- a/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs +++ b/src/DotRecast.Detour.Crowd/DtCrowdAgent.cs @@ -1,7 +1,7 @@ /* Copyright (c) 2009-2010 Mikko Mononen memon@inside.org recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org -DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com +DotRecast Copyright (c) 2023-2024 Choi Ikpil ikpil@naver.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -20,7 +20,7 @@ 3. This notice may not be removed or altered from any source distribution. using System; using System.Collections.Generic; -using DotRecast.Core; +using DotRecast.Core.Numerics; namespace DotRecast.Detour.Crowd { @@ -28,75 +28,53 @@ namespace DotRecast.Detour.Crowd /// @ingroup crowd public class DtCrowdAgent { - public readonly long idx; + public readonly int idx; /// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState) - public CrowdAgentState state; + public DtCrowdAgentState state; - /// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the - /// requested position, else false. + /// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the requested position, else false. public bool partial; /// The path corridor the agent is using. - public DtPathCorridor corridor; + public readonly DtPathCorridor corridor; /// The local boundary data for the agent. - public DtLocalBoundary boundary; + public readonly DtLocalBoundary boundary; /// Time since the agent's path corridor was optimized. public float topologyOptTime; /// The known neighbors of the agent. - public List neis = new List(); + public readonly DtCrowdNeighbour[] neis = new DtCrowdNeighbour[DtCrowdConst.DT_CROWDAGENT_MAX_NEIGHBOURS]; + + /// The number of neighbors. + public int nneis; /// The desired speed. public float desiredSpeed; - public RcVec3f npos = new RcVec3f(); + public RcVec3f npos = new RcVec3f(); // < The current agent position. [(x, y, z)] + public RcVec3f disp = new RcVec3f(); // < A temporary value used to accumulate agent displacement during iterative collision resolution. [(x, y, z)] + public RcVec3f dvel = new RcVec3f(); // < The desired velocity of the agent. Based on the current path, calculated from scratch each frame. [(x, y, z)] + public RcVec3f nvel = new RcVec3f(); // < The desired velocity adjusted by obstacle avoidance, calculated from scratch each frame. [(x, y, z)] + public RcVec3f vel = new RcVec3f(); // < The actual velocity of the agent. The change from nvel -> vel is constrained by max acceleration. [(x, y, z)] - /// < The current agent position. [(x, y, z)] - public RcVec3f disp = new RcVec3f(); - - /// < A temporary value used to accumulate agent displacement during iterative - /// collision resolution. [(x, y, z)] - public RcVec3f dvel = new RcVec3f(); - - /// < The desired velocity of the agent. Based on the current path, calculated - /// from - /// scratch each frame. [(x, y, z)] - public RcVec3f nvel = new RcVec3f(); - - /// < The desired velocity adjusted by obstacle avoidance, calculated from scratch each - /// frame. [(x, y, z)] - public RcVec3f vel = new RcVec3f(); - - /// < The actual velocity of the agent. The change from nvel -> vel is - /// constrained by max acceleration. [(x, y, z)] /// The agent's configuration parameters. public DtCrowdAgentParams option; /// The local path corridor corners for the agent. - public List corners = new List(); - - public MoveRequestState targetState; - - /// < State of the movement request. - public long targetRef; - - /// < Target polyref of the movement request. - public RcVec3f targetPos = new RcVec3f(); - - /// < Target position of the movement request (or velocity in case of - /// DT_CROWDAGENT_TARGET_VELOCITY). - public DtPathQueryResult targetPathQueryResult; - - /// < Path finder query - public bool targetReplan; + public DtStraightPath[] corners = new DtStraightPath[DtCrowdConst.DT_CROWDAGENT_MAX_CORNERS]; - /// < Flag indicating that the current path is being replanned. - public float targetReplanTime; + /// The number of corners. + public int ncorners; - ///