├── .gitignore ├── Assets ├── ProceduralLevelAI.meta ├── ProceduralLevelAI │ ├── Scenes.meta │ ├── Scenes │ │ ├── scene_proceduralLevel.unity │ │ └── scene_proceduralLevel.unity.meta │ ├── Scripts.meta │ └── Scripts │ │ ├── BuildProceduralLevel.cs │ │ └── BuildProceduralLevel.cs.meta ├── Rcdtcs.meta ├── Rcdtcs │ ├── Detour.meta │ ├── Detour │ │ ├── DetourCommon.cs │ │ ├── DetourCommon.cs.meta │ │ ├── DetourNavMesh.cs │ │ ├── DetourNavMesh.cs.meta │ │ ├── DetourNavMeshBuilder.cs │ │ ├── DetourNavMeshBuilder.cs.meta │ │ ├── DetourNavMeshQuery.cs │ │ ├── DetourNavMeshQuery.cs.meta │ │ ├── DetourNode.cs │ │ ├── DetourNode.cs.meta │ │ ├── DetourStatus.cs │ │ └── DetourStatus.cs.meta │ ├── License.txt │ ├── License.txt.meta │ ├── Recast.meta │ └── Recast │ │ ├── Recast.cs │ │ ├── Recast.cs.meta │ │ ├── RecastArea.cs │ │ ├── RecastArea.cs.meta │ │ ├── RecastContour.cs │ │ ├── RecastContour.cs.meta │ │ ├── RecastFilter.cs │ │ ├── RecastFilter.cs.meta │ │ ├── RecastLayers.cs │ │ ├── RecastLayers.cs.meta │ │ ├── RecastMesh.cs │ │ ├── RecastMesh.cs.meta │ │ ├── RecastMeshDetail.cs │ │ ├── RecastMeshDetail.cs.meta │ │ ├── RecastRasterization.cs │ │ ├── RecastRasterization.cs.meta │ │ ├── RecastRegion.cs │ │ └── RecastRegion.cs.meta ├── RcdtcsUnityDemo.meta └── RcdtcsUnityDemo │ ├── Font.meta │ ├── Font │ ├── cour.ttf │ ├── cour.ttf.meta │ ├── courbd.ttf │ ├── courbd.ttf.meta │ ├── courbi.ttf │ ├── courbi.ttf.meta │ ├── couri.ttf │ └── couri.ttf.meta │ ├── Materials.meta │ ├── Materials │ ├── DefaultMaterial.mat │ ├── DefaultMaterial.mat.meta │ ├── VertexColor.mat │ └── VertexColor.mat.meta │ ├── Meshes.meta │ ├── Meshes │ ├── Materials.meta │ ├── Materials │ │ ├── CubeMat.mat │ │ ├── CubeMat.mat.meta │ │ ├── GenericMat.mat │ │ ├── GenericMat.mat.meta │ │ ├── defaultMat.mat │ │ └── defaultMat.mat.meta │ ├── dungeon.obj │ ├── dungeon.obj.meta │ ├── nav_test.obj │ └── nav_test.obj.meta │ ├── RcdtcsUnityDemo.unity │ ├── RcdtcsUnityDemo.unity.meta │ ├── Scripts.meta │ ├── Scripts │ ├── Sample.meta │ ├── Sample │ │ ├── RcdtSampleCamera.cs │ │ ├── RcdtSampleCamera.cs.meta │ │ ├── RcdtcsSampleSoloMeshComponent.cs │ │ ├── RcdtcsSampleSoloMeshComponent.cs.meta │ │ ├── TextSetFloat.cs │ │ └── TextSetFloat.cs.meta │ ├── Utils.meta │ └── Utils │ │ ├── BuildContext.cs │ │ ├── BuildContext.cs.meta │ │ ├── DbgRenderMesh.cs │ │ ├── DbgRenderMesh.cs.meta │ │ ├── RcdtcsDebugRenderUtils.cs │ │ ├── RcdtcsDebugRenderUtils.cs.meta │ │ ├── RcdtcsPathUtils.cs │ │ ├── RcdtcsPathUtils.cs.meta │ │ ├── RcdtcsSystemUtils.cs │ │ └── RcdtcsSystemUtils.cs.meta │ ├── Shaders.meta │ └── Shaders │ ├── VertexColor.shader │ └── VertexColor.shader.meta ├── License.txt ├── ProjectSettings ├── AudioManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshAreas.asset ├── NavMeshLayers.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── TagManager.asset ├── TimeManager.asset ├── UnityAdsSettings.asset └── UnityAnalyticsManager.asset ├── README.md ├── README.md.meta ├── RcdtcsScreenshot.jpg ├── RecastNavU5Fork.sln └── RecastNavU5Fork.userprefs /.gitignore: -------------------------------------------------------------------------------- 1 | /Library 2 | /Temp 3 | /Assembly-CSharp.csproj 4 | /Assembly-CSharp-vs.csproj 5 | /rcdtcs-csharp.sln 6 | /rcdtcs.sln 7 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f1bd468a58fd3a943b680e3d68154b96 3 | folderAsset: yes 4 | timeCreated: 1447793348 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e4625fe17bd939c46988d6137f7fd9a2 3 | folderAsset: yes 4 | timeCreated: 1447793354 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scenes/scene_proceduralLevel.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/ProceduralLevelAI/Scenes/scene_proceduralLevel.unity -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scenes/scene_proceduralLevel.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d7e73023995d7634b9c6084acec9245f 3 | timeCreated: 1447793540 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83e720532f75dcc4287ebdc4f5c6e331 3 | folderAsset: yes 4 | timeCreated: 1447793355 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scripts/BuildProceduralLevel.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | public class BuildProceduralLevel : MonoBehaviour { 6 | 7 | public Transform startPos; 8 | public Transform endPos; 9 | 10 | List vertices = new List(); 11 | List uvs = new List(); 12 | List triangles = new List(); 13 | 14 | void Start () { 15 | 16 | // create basic mesh floor 17 | BuildFloor(); 18 | 19 | // build navmesh 20 | var rcdtcs = GetComponent (); 21 | rcdtcs.RecomputeSystem (); 22 | 23 | // compute path 24 | rcdtcs.m_StartPos = startPos.position; 25 | rcdtcs.m_EndPos = endPos.position; 26 | rcdtcs.RecomputePath(); 27 | 28 | } 29 | 30 | void BuildFloor() 31 | { 32 | var mesh = GetComponent().mesh; 33 | var index = 0; 34 | 35 | // first path 36 | vertices.Add(new Vector3(0,0,0)); 37 | vertices.Add(new Vector3(0,0,5)); 38 | vertices.Add(new Vector3(1,0,5)); 39 | vertices.Add(new Vector3(1,0,0)); 40 | triangles.Add (index+0); 41 | triangles.Add (index+1); 42 | triangles.Add (index+2); 43 | triangles.Add (index+0); 44 | triangles.Add (index+2); 45 | triangles.Add (index+3); 46 | uvs.Add (new Vector2 (0, 0)); 47 | uvs.Add (new Vector2 (0, 1)); 48 | uvs.Add (new Vector2 (1, 1)); 49 | uvs.Add (new Vector2 (1, 0)); 50 | 51 | // turn right 52 | vertices.Add(new Vector3(1,0,4)); 53 | vertices.Add(new Vector3(1,0,5)); 54 | vertices.Add(new Vector3(5,0,5)); 55 | vertices.Add(new Vector3(5,0,4)); 56 | index = vertices.Count-4; 57 | triangles.Add (index+0); 58 | triangles.Add (index+1); 59 | triangles.Add (index+2); 60 | triangles.Add (index+0); 61 | triangles.Add (index+2); 62 | triangles.Add (index+3); 63 | uvs.Add (new Vector2 (0, 0)); 64 | uvs.Add (new Vector2 (0, 1)); 65 | uvs.Add (new Vector2 (1, 1)); 66 | uvs.Add (new Vector2 (1, 0)); 67 | 68 | // right up 69 | vertices.Add(new Vector3(4,0,5)); 70 | vertices.Add(new Vector3(4,0,10)); 71 | vertices.Add(new Vector3(5,0,10)); 72 | vertices.Add(new Vector3(5,0,5)); 73 | index = vertices.Count-4; 74 | triangles.Add (index+0); 75 | triangles.Add (index+1); 76 | triangles.Add (index+2); 77 | triangles.Add (index+0); 78 | triangles.Add (index+2); 79 | triangles.Add (index+3); 80 | uvs.Add (new Vector2 (0, 0)); 81 | uvs.Add (new Vector2 (0, 1)); 82 | uvs.Add (new Vector2 (1, 1)); 83 | uvs.Add (new Vector2 (1, 0)); 84 | 85 | 86 | // turn left 87 | vertices.Add(new Vector3(-5,0,4)); 88 | vertices.Add(new Vector3(-5,0,5)); 89 | vertices.Add(new Vector3(0,0,5)); 90 | vertices.Add(new Vector3(0,0,4)); 91 | index = vertices.Count-4; 92 | triangles.Add (index+0); 93 | triangles.Add (index+1); 94 | triangles.Add (index+2); 95 | triangles.Add (index+0); 96 | triangles.Add (index+2); 97 | triangles.Add (index+3); 98 | uvs.Add (new Vector2 (0, 0)); 99 | uvs.Add (new Vector2 (0, 1)); 100 | uvs.Add (new Vector2 (1, 1)); 101 | uvs.Add (new Vector2 (1, 0)); 102 | 103 | // left up 104 | vertices.Add(new Vector3(-4,0,5)); 105 | vertices.Add(new Vector3(-5,0,5)); 106 | vertices.Add(new Vector3(-5,0,10)); 107 | vertices.Add(new Vector3(-4,0,10)); 108 | index = vertices.Count-4; 109 | triangles.Add (index+0); 110 | triangles.Add (index+1); 111 | triangles.Add (index+2); 112 | triangles.Add (index+0); 113 | triangles.Add (index+2); 114 | triangles.Add (index+3); 115 | uvs.Add (new Vector2 (0, 0)); 116 | uvs.Add (new Vector2 (0, 1)); 117 | uvs.Add (new Vector2 (1, 1)); 118 | uvs.Add (new Vector2 (1, 0)); 119 | 120 | // connect end 121 | vertices.Add(new Vector3(-5,0,9)); 122 | vertices.Add(new Vector3(-5,0,10)); 123 | vertices.Add(new Vector3(4,0,10)); 124 | vertices.Add(new Vector3(5,0,9)); 125 | index = vertices.Count-4; 126 | triangles.Add (index+0); 127 | triangles.Add (index+1); 128 | triangles.Add (index+2); 129 | triangles.Add (index+0); 130 | triangles.Add (index+2); 131 | triangles.Add (index+3); 132 | uvs.Add (new Vector2 (0, 0)); 133 | uvs.Add (new Vector2 (0, 1)); 134 | uvs.Add (new Vector2 (1, 1)); 135 | uvs.Add (new Vector2 (1, 0)); 136 | 137 | // mesh above is too small for default values, so just scaling to get it working 138 | var meshScale = 5; 139 | for (int i = 0; i < vertices.Count; i++) { 140 | vertices[i] *= meshScale; 141 | } 142 | 143 | mesh.vertices = vertices.ToArray(); 144 | mesh.triangles = triangles.ToArray(); 145 | mesh.uv = uvs.ToArray(); 146 | mesh.RecalculateNormals(); 147 | mesh.Optimize (); 148 | 149 | GetComponent().sharedMesh = null; 150 | GetComponent().sharedMesh = mesh; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /Assets/ProceduralLevelAI/Scripts/BuildProceduralLevel.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c8e82902c42b604db09f23c05bb6b02 3 | timeCreated: 1447793373 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Rcdtcs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0f06ec875f6b88f4aaaceb8bed694360 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d6234351b5a27de4f8ef29c7b59ac7da 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourCommon.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99ee650ae20c3d645ba11ecd90ece1c7 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourNavMesh.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bef26ec4dd6f0574e82d1fa613900443 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourNavMeshBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2eec78811a3f1d64081336b1c001b80c 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourNavMeshQuery.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b67da1c4814f9de42a94d83a76c7338a 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using dtStatus = System.UInt32; 4 | using dtNodeIndex = System.UInt16; 5 | #if DT_POLYREF64 6 | using dtPolyRef = System.UInt64; 7 | //using dtTileRef = System.UInt64; 8 | #else 9 | using dtPolyRef = System.UInt32; 10 | //using dtTileRef = System.UInt32; 11 | #endif 12 | 13 | #if DT_POLYREF64 14 | // From Thomas Wang, https://gist.github.com/badboy/6267743 15 | inline uint dtHashRef(dtPolyRef a) 16 | { 17 | a = (~a) + (a << 18); // a = (a << 18) - a - 1; 18 | a = a ^ (a >> 31); 19 | a = a * 21; // a = (a + (a << 2)) + (a << 4); 20 | a = a ^ (a >> 11); 21 | a = a + (a << 6); 22 | a = a ^ (a >> 22); 23 | return (uint)a; 24 | } 25 | #else 26 | public partial class Detour{ 27 | public static uint dtHashRef(dtPolyRef a) 28 | { 29 | a += ~(a<<15); 30 | a ^= (a>>10); 31 | a += (a<<3); 32 | a ^= (a>>6); 33 | a += ~(a<<11); 34 | a ^= (a>>16); 35 | return (uint)a; 36 | } 37 | } 38 | #endif 39 | 40 | public partial class Detour{ 41 | 42 | public enum dtNodeFlags { 43 | DT_NODE_OPEN = 0x01, 44 | DT_NODE_CLOSED = 0x02, 45 | }; 46 | 47 | public const dtNodeIndex DT_NULL_IDX = dtNodeIndex.MaxValue; //(dtNodeIndex)~0; 48 | 49 | public class dtNode { 50 | public float[] pos = new float[3]; //< Position of the node. 51 | public float cost; //< Cost from previous node to current node. 52 | public float total; //< Cost up to the node. 53 | public uint pidx;// : 30; //< Index to parent node. 54 | public byte flags;// : 2; //< Node flags 0/open/closed. 55 | public dtPolyRef id; //< Polygon ref the node corresponds to. 56 | /// 57 | public static int getSizeOf() { 58 | //C# can't guess the sizeof of the float array, let's pretend 59 | return sizeof(float) * (3 + 1 + 1) 60 | + sizeof(uint) 61 | + sizeof(byte) 62 | + sizeof(dtPolyRef); 63 | } 64 | public void dtcsClearFlag(dtNodeFlags flag) { 65 | unchecked { 66 | flags &= (byte)(~flag); 67 | } 68 | } 69 | public void dtcsSetFlag(dtNodeFlags flag) { 70 | flags |= (byte)flag; 71 | } 72 | public bool dtcsTestFlag(dtNodeFlags flag) { 73 | return (flags & (byte)flag) != 0; 74 | } 75 | }; 76 | 77 | 78 | public class dtNodePool{ 79 | private dtNode[] m_nodes; 80 | private dtNodeIndex[] m_first; 81 | private dtNodeIndex[] m_next; 82 | private int m_maxNodes; 83 | private int m_hashSize; 84 | private int m_nodeCount; 85 | 86 | ////////////////////////////////////////////////////////////////////////////////////////// 87 | public dtNodePool(int maxNodes, int hashSize) 88 | 89 | { 90 | m_maxNodes = maxNodes; 91 | m_hashSize = hashSize; 92 | 93 | Debug.Assert(dtNextPow2((uint)m_hashSize) == (uint)m_hashSize); 94 | Debug.Assert(m_maxNodes > 0); 95 | 96 | m_nodes = new dtNode[m_maxNodes]; 97 | dtcsArrayItemsCreate(m_nodes); 98 | m_next = new dtNodeIndex[m_maxNodes]; 99 | m_first = new dtNodeIndex[hashSize]; 100 | 101 | Debug.Assert(m_nodes != null); 102 | Debug.Assert(m_next != null); 103 | Debug.Assert(m_first != null); 104 | 105 | for (int i=0;i= m_maxNodes) 182 | return null; 183 | 184 | i = (dtNodeIndex)m_nodeCount; 185 | m_nodeCount++; 186 | 187 | // Init node 188 | node = m_nodes[i]; 189 | node.pidx = 0; 190 | node.cost = 0; 191 | node.total = 0; 192 | node.id = id; 193 | node.flags = 0; 194 | 195 | m_next[i] = m_first[bucket]; 196 | m_first[bucket] = i; 197 | 198 | return node; 199 | } 200 | } 201 | 202 | 203 | ////////////////////////////////////////////////////////////////////////////////////////// 204 | public class dtNodeQueue{ 205 | private dtNode[] m_heap; 206 | private int m_capacity; 207 | private int m_size; 208 | 209 | public dtNodeQueue(int n) 210 | { 211 | m_capacity = n; 212 | Debug.Assert(m_capacity > 0); 213 | 214 | m_heap = new dtNode[m_capacity+1];//(dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM); 215 | Debug.Assert(m_heap != null); 216 | } 217 | 218 | public void clear() { 219 | m_size = 0; 220 | } 221 | 222 | public dtNode top() { 223 | return m_heap[0]; 224 | } 225 | 226 | public dtNode pop() { 227 | dtNode result = m_heap[0]; 228 | m_size--; 229 | trickleDown(0, m_heap[m_size]); 230 | return result; 231 | } 232 | 233 | public void push(dtNode node) { 234 | m_size++; 235 | bubbleUp(m_size - 1, node); 236 | } 237 | 238 | public void modify(dtNode node) { 239 | for (int i = 0; i < m_size; ++i) { 240 | if (m_heap[i] == node) { 241 | bubbleUp(i, node); 242 | return; 243 | } 244 | } 245 | } 246 | 247 | public bool empty() { 248 | return m_size == 0; 249 | } 250 | 251 | public int getMemUsed() { 252 | return sizeof(int) * 2 + 253 | dtNode.getSizeOf() * (m_capacity + 1); 254 | } 255 | 256 | public int getCapacity() { 257 | return m_capacity; 258 | } 259 | 260 | 261 | public void bubbleUp(int i, dtNode node) 262 | { 263 | int parent = (i-1)/2; 264 | // note: (index > 0) means there is a parent 265 | while ((i > 0) && (m_heap[parent].total > node.total)) 266 | { 267 | m_heap[i] = m_heap[parent]; 268 | i = parent; 269 | parent = (i-1)/2; 270 | } 271 | m_heap[i] = node; 272 | } 273 | 274 | public void trickleDown(int i, dtNode node) 275 | { 276 | int child = (i*2)+1; 277 | while (child < m_size) 278 | { 279 | if (((child+1) < m_size) && 280 | (m_heap[child].total > m_heap[child+1].total)) 281 | { 282 | child++; 283 | } 284 | m_heap[i] = m_heap[child]; 285 | i = child; 286 | child = (i*2)+1; 287 | } 288 | bubbleUp(i, node); 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44452e2f9872a6043a112b11b83be135 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourStatus.cs: -------------------------------------------------------------------------------- 1 | using dtStatus = System.UInt32; 2 | 3 | public static partial class Detour{ 4 | // High level status. 5 | public const uint DT_FAILURE = 1u << 31; // Operation failed. 6 | public const uint DT_SUCCESS = 1u << 30; // Operation succeed. 7 | public const uint DT_IN_PROGRESS = 1u << 29; // Operation still in progress. 8 | 9 | // Detail information for status. 10 | public const uint DT_STATUS_DETAIL_MASK = 0x0ffffff; 11 | public const uint DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized. 12 | public const uint DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version. 13 | public const uint DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory. 14 | public const uint DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid. 15 | public const uint DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results. 16 | public const uint DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search. 17 | public const uint DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess. 18 | 19 | 20 | // Returns true of status is success. 21 | public static bool dtStatusSucceed(dtStatus status) 22 | { 23 | return (status & DT_SUCCESS) != 0; 24 | } 25 | 26 | // Returns true of status is failure. 27 | public static bool dtStatusFailed(dtStatus status) 28 | { 29 | return (status & DT_FAILURE) != 0; 30 | } 31 | 32 | // Returns true of status is in progress. 33 | public static bool dtStatusInProgress(dtStatus status) 34 | { 35 | return (status & DT_IN_PROGRESS) != 0; 36 | } 37 | 38 | // Returns true if specific detail is set. 39 | public static bool dtStatusDetail(dtStatus status, uint detail) 40 | { 41 | return (status & detail) != 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Detour/DetourStatus.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1cbfa06d879554448a4d6f6f88c59cae 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Julien Lallevé 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Assets/Rcdtcs/License.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3bb5e7f441469ab44a3d2268e8a5727f 3 | TextScriptImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e4e328c746070894d9add4ef1ff19f92 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/Recast.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19c6be769ecbcd341ad82c089bd20dcb 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastArea.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | public static partial class Recast { 5 | /// @par 6 | /// 7 | /// Basically, any spans that are closer to a boundary or obstruction than the specified radius 8 | /// are marked as unwalkable. 9 | /// 10 | /// This method is usually called immediately after the heightfield has been built. 11 | /// 12 | /// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius 13 | public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf) { 14 | Debug.Assert(ctx != null, "rcContext is null"); 15 | 16 | int w = chf.width; 17 | int h = chf.height; 18 | 19 | ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA); 20 | 21 | byte[] dist = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); 22 | if (dist == null) { 23 | ctx.log(rcLogCategory.RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' " + chf.spanCount); 24 | return false; 25 | } 26 | 27 | // Init distance. 28 | for (int i=0; i < chf.spanCount; ++i) { 29 | dist[i] = 0xff; 30 | } 31 | // memset(dist, 0xff, sizeof(byte)*chf.spanCount); 32 | 33 | // Mark boundary cells. 34 | for (int y = 0; y < h; ++y) { 35 | for (int x = 0; x < w; ++x) { 36 | rcCompactCell c = chf.cells[x + y * w]; 37 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 38 | if (chf.areas[i] == RC_NULL_AREA) { 39 | dist[i] = 0; 40 | } else { 41 | rcCompactSpan s = chf.spans[i]; 42 | int nc = 0; 43 | for (int dir = 0; dir < 4; ++dir) { 44 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { 45 | int nx = x + rcGetDirOffsetX(dir); 46 | int ny = y + rcGetDirOffsetY(dir); 47 | int nidx = (int)chf.cells[nx + ny * w].index + rcGetCon(s, dir); 48 | if (chf.areas[nidx] != RC_NULL_AREA) { 49 | nc++; 50 | } 51 | } 52 | } 53 | // At least one missing neighbour. 54 | if (nc != 4) 55 | dist[i] = 0; 56 | } 57 | } 58 | } 59 | } 60 | 61 | byte nd = 0; 62 | 63 | // Pass 1 64 | for (int y = 0; y < h; ++y) { 65 | for (int x = 0; x < w; ++x) { 66 | rcCompactCell c = chf.cells[x + y * w]; 67 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 68 | rcCompactSpan s = chf.spans[i]; 69 | 70 | if (rcGetCon(s, 0) != RC_NOT_CONNECTED) { 71 | // (-1,0) 72 | int ax = x + rcGetDirOffsetX(0); 73 | int ay = y + rcGetDirOffsetY(0); 74 | int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0); 75 | rcCompactSpan aSpan = chf.spans[ai]; 76 | nd = (byte)Math.Min((int)dist[ai] + 2, 255); 77 | if (nd < dist[i]) 78 | dist[i] = nd; 79 | 80 | // (-1,-1) 81 | if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED) { 82 | int aax = ax + rcGetDirOffsetX(3); 83 | int aay = ay + rcGetDirOffsetY(3); 84 | int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 3); 85 | nd = (byte)Math.Min((int)dist[aai] + 3, 255); 86 | if (nd < dist[i]) 87 | dist[i] = nd; 88 | } 89 | } 90 | if (rcGetCon(s, 3) != RC_NOT_CONNECTED) { 91 | // (0,-1) 92 | int ax = x + rcGetDirOffsetX(3); 93 | int ay = y + rcGetDirOffsetY(3); 94 | int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3); 95 | rcCompactSpan aSpan = chf.spans[ai]; 96 | nd = (byte)Math.Min((int)dist[ai] + 2, 255); 97 | if (nd < dist[i]) 98 | dist[i] = nd; 99 | 100 | // (1,-1) 101 | if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED) { 102 | int aax = ax + rcGetDirOffsetX(2); 103 | int aay = ay + rcGetDirOffsetY(2); 104 | int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 2); 105 | nd = (byte)Math.Min((int)dist[aai] + 3, 255); 106 | if (nd < dist[i]) 107 | dist[i] = nd; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | // Pass 2 115 | for (int y = h - 1; y >= 0; --y) { 116 | for (int x = w - 1; x >= 0; --x) { 117 | rcCompactCell c = chf.cells[x + y * w]; 118 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 119 | rcCompactSpan s = chf.spans[i]; 120 | 121 | if (rcGetCon(s, 2) != RC_NOT_CONNECTED) { 122 | // (1,0) 123 | int ax = x + rcGetDirOffsetX(2); 124 | int ay = y + rcGetDirOffsetY(2); 125 | int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 2); 126 | rcCompactSpan aSpan = chf.spans[ai]; 127 | nd = (byte)Math.Min((int)dist[ai] + 2, 255); 128 | if (nd < dist[i]) 129 | dist[i] = nd; 130 | 131 | // (1,1) 132 | if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED) { 133 | int aax = ax + rcGetDirOffsetX(1); 134 | int aay = ay + rcGetDirOffsetY(1); 135 | int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 1); 136 | nd = (byte)Math.Min((int)dist[aai] + 3, 255); 137 | if (nd < dist[i]) 138 | dist[i] = nd; 139 | } 140 | } 141 | if (rcGetCon(s, 1) != RC_NOT_CONNECTED) { 142 | // (0,1) 143 | int ax = x + rcGetDirOffsetX(1); 144 | int ay = y + rcGetDirOffsetY(1); 145 | int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 1); 146 | rcCompactSpan aSpan = chf.spans[ai]; 147 | nd = (byte)Math.Min((int)dist[ai] + 2, 255); 148 | if (nd < dist[i]) 149 | dist[i] = nd; 150 | 151 | // (-1,1) 152 | if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED) { 153 | int aax = ax + rcGetDirOffsetX(0); 154 | int aay = ay + rcGetDirOffsetY(0); 155 | int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 0); 156 | nd = (byte)Math.Min((int)dist[aai] + 3, 255); 157 | if (nd < dist[i]) 158 | dist[i] = nd; 159 | } 160 | } 161 | } 162 | } 163 | } 164 | 165 | byte thr = (byte)(radius * 2); 166 | for (int i = 0; i < chf.spanCount; ++i) 167 | if (dist[i] < thr) 168 | chf.areas[i] = RC_NULL_AREA; 169 | 170 | ctx.stopTimer(rcTimerLabel.RC_TIMER_ERODE_AREA); 171 | 172 | return true; 173 | } 174 | 175 | static void insertSort(byte[] a, int n) { 176 | int i, j; 177 | for (i = 1; i < n; i++) { 178 | byte value = a[i]; 179 | for (j = i - 1; j >= 0 && a[j] > value; j--) 180 | a[j + 1] = a[j]; 181 | a[j + 1] = value; 182 | } 183 | } 184 | 185 | /// @par 186 | /// 187 | /// This filter is usually applied after applying area id's using functions 188 | /// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea. 189 | /// 190 | /// @see rcCompactHeightfield 191 | public static bool rcMedianFilterWalkableArea(rcContext ctx, rcCompactHeightfield chf) { 192 | Debug.Assert(ctx != null, "rcContext is null"); 193 | 194 | int w = chf.width; 195 | int h = chf.height; 196 | 197 | ctx.startTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA); 198 | 199 | byte[] areas = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); 200 | if (areas == null) { 201 | ctx.log(rcLogCategory.RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' " + chf.spanCount); 202 | return false; 203 | } 204 | 205 | // Init distance. 206 | for (int i = 0; i < chf.spanCount; ++i) { 207 | areas[i] = 0xff; 208 | } 209 | //memset(areas, 0xff, sizeof(byte)*chf.spanCount); 210 | 211 | for (int y = 0; y < h; ++y) { 212 | for (int x = 0; x < w; ++x) { 213 | rcCompactCell c = chf.cells[x + y * w]; 214 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 215 | rcCompactSpan s = chf.spans[i]; 216 | if (chf.areas[i] == RC_NULL_AREA) { 217 | areas[i] = chf.areas[i]; 218 | continue; 219 | } 220 | 221 | byte[] nei = new byte[9]; 222 | for (int j = 0; j < 9; ++j) 223 | nei[j] = chf.areas[i]; 224 | 225 | for (int dir = 0; dir < 4; ++dir) { 226 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { 227 | int ax = x + rcGetDirOffsetX(dir); 228 | int ay = y + rcGetDirOffsetY(dir); 229 | int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, dir); 230 | if (chf.areas[ai] != RC_NULL_AREA) 231 | nei[dir * 2 + 0] = chf.areas[ai]; 232 | 233 | rcCompactSpan aSpan = chf.spans[ai]; 234 | int dir2 = (dir + 1) & 0x3; 235 | if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) { 236 | int ax2 = ax + rcGetDirOffsetX(dir2); 237 | int ay2 = ay + rcGetDirOffsetY(dir2); 238 | int ai2 = (int)chf.cells[ax2 + ay2 * w].index + rcGetCon(aSpan, dir2); 239 | if (chf.areas[ai2] != RC_NULL_AREA) 240 | nei[dir * 2 + 1] = chf.areas[ai2]; 241 | } 242 | } 243 | } 244 | insertSort(nei, 9); 245 | areas[i] = nei[4]; 246 | } 247 | } 248 | } 249 | 250 | chf.areas = areas; 251 | //memcpy(chf.areas, areas, sizeof(byte)*chf.spanCount); 252 | 253 | //rcFree(areas); 254 | 255 | ctx.stopTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA); 256 | 257 | return true; 258 | } 259 | 260 | /// @par 261 | /// 262 | /// The value of spacial parameters are in world units. 263 | /// 264 | /// @see rcCompactHeightfield, rcMedianFilterWalkableArea 265 | public static void rcMarkBoxArea(rcContext ctx, float[] bmin, float[] bmax, byte areaId, 266 | rcCompactHeightfield chf) { 267 | Debug.Assert(ctx != null, "rcContext is null"); 268 | 269 | ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA); 270 | 271 | int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs); 272 | int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch); 273 | int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs); 274 | int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs); 275 | int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch); 276 | int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs); 277 | 278 | if (maxx < 0) return; 279 | if (minx >= chf.width) return; 280 | if (maxz < 0) return; 281 | if (minz >= chf.height) return; 282 | 283 | if (minx < 0) minx = 0; 284 | if (maxx >= chf.width) maxx = chf.width - 1; 285 | if (minz < 0) minz = 0; 286 | if (maxz >= chf.height) maxz = chf.height - 1; 287 | 288 | for (int z = minz; z <= maxz; ++z) { 289 | for (int x = minx; x <= maxx; ++x) { 290 | rcCompactCell c = chf.cells[x + z * chf.width]; 291 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 292 | rcCompactSpan s = chf.spans[i]; 293 | if ((int)s.y >= miny && (int)s.y <= maxy) { 294 | if (chf.areas[i] != RC_NULL_AREA) 295 | chf.areas[i] = areaId; 296 | } 297 | } 298 | } 299 | } 300 | 301 | ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA); 302 | 303 | } 304 | 305 | 306 | public static bool pointInPoly(int nvert, float[] verts, float[] p) { 307 | bool c = false; 308 | int i = 0; 309 | int j = 0; 310 | for (i = 0, j = nvert - 1; i < nvert; j = i++) { 311 | int viStart = i * 3; 312 | int vjStart = j * 3; 313 | if (((verts[viStart + 2] > p[2]) != (verts[vjStart + 2] > p[2])) && 314 | (p[0] < (verts[vjStart + 0] - verts[viStart + 0]) * (p[2] - verts[viStart + 2]) / (verts[vjStart + 2] - verts[viStart + 2]) + verts[viStart + 0])) { 315 | c = !c; 316 | } 317 | } 318 | return c; 319 | } 320 | 321 | /// @par 322 | /// 323 | /// The value of spacial parameters are in world units. 324 | /// 325 | /// The y-values of the polygon vertices are ignored. So the polygon is effectively 326 | /// projected onto the xz-plane at @p hmin, then extruded to @p hmax. 327 | /// 328 | /// @see rcCompactHeightfield, rcMedianFilterWalkableArea 329 | public static void rcMarkConvexPolyArea(rcContext ctx, float[] verts, int nverts, 330 | float hmin, float hmax, byte areaId, 331 | rcCompactHeightfield chf) { 332 | Debug.Assert(ctx != null, "rcContext is null"); 333 | 334 | ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA); 335 | 336 | float[] bmin = new float[3]; 337 | float[] bmax = new float[3]; 338 | rcVcopy(bmin, verts); 339 | rcVcopy(bmax, verts); 340 | for (int i = 1; i < nverts; ++i) { 341 | int vStart = i * 3; 342 | rcVmin(bmin, 0, verts, vStart); 343 | rcVmax(bmax, 0, verts, vStart); 344 | } 345 | bmin[1] = hmin; 346 | bmax[1] = hmax; 347 | 348 | int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs); 349 | int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch); 350 | int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs); 351 | int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs); 352 | int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch); 353 | int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs); 354 | 355 | if (maxx < 0) return; 356 | if (minx >= chf.width) return; 357 | if (maxz < 0) return; 358 | if (minz >= chf.height) return; 359 | 360 | if (minx < 0) minx = 0; 361 | if (maxx >= chf.width) maxx = chf.width - 1; 362 | if (minz < 0) minz = 0; 363 | if (maxz >= chf.height) maxz = chf.height - 1; 364 | 365 | 366 | // TODO: Optimize. 367 | for (int z = minz; z <= maxz; ++z) { 368 | for (int x = minx; x <= maxx; ++x) { 369 | rcCompactCell c = chf.cells[x + z * chf.width]; 370 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 371 | rcCompactSpan s = chf.spans[i]; 372 | if (chf.areas[i] == RC_NULL_AREA) 373 | continue; 374 | if ((int)s.y >= miny && (int)s.y <= maxy) { 375 | float[] p = new float[3]; 376 | p[0] = chf.bmin[0] + (x + 0.5f) * chf.cs; 377 | p[1] = 0; 378 | p[2] = chf.bmin[2] + (z + 0.5f) * chf.cs; 379 | 380 | if (pointInPoly(nverts, verts, p)) { 381 | chf.areas[i] = areaId; 382 | } 383 | } 384 | } 385 | } 386 | } 387 | 388 | ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA); 389 | } 390 | 391 | static int rcOffsetPoly(float[] verts, int nverts, float offset, 392 | float[] outVerts, int maxOutVerts) { 393 | const float MITER_LIMIT = 1.20f; 394 | 395 | int n = 0; 396 | 397 | for (int i = 0; i < nverts; i++) { 398 | int a = (i + nverts - 1) % nverts; 399 | int b = i; 400 | int c = (i + 1) % nverts; 401 | int vaStart = a * 3; 402 | int vbStart = b * 3; 403 | int vcStart = c * 3; 404 | float dx0 = verts[vbStart + 0] - verts[vaStart + 0]; 405 | float dy0 = verts[vbStart + 2] - verts[vaStart + 2]; 406 | float d0 = dx0 * dx0 + dy0 * dy0; 407 | if (d0 > 1e-6f) { 408 | d0 = 1.0f / (float)Math.Sqrt(d0); 409 | dx0 *= d0; 410 | dy0 *= d0; 411 | } 412 | float dx1 = verts[vcStart + 0] - verts[vbStart + 0]; 413 | float dy1 = verts[vcStart + 2] - verts[vbStart + 2]; 414 | float d1 = dx1 * dx1 + dy1 * dy1; 415 | if (d1 > 1e-6f) { 416 | d1 = 1.0f / (float)Math.Sqrt(d1); 417 | dx1 *= d1; 418 | dy1 *= d1; 419 | } 420 | float dlx0 = -dy0; 421 | float dly0 = dx0; 422 | float dlx1 = -dy1; 423 | float dly1 = dx1; 424 | float cross = dx1 * dy0 - dx0 * dy1; 425 | float dmx = (dlx0 + dlx1) * 0.5f; 426 | float dmy = (dly0 + dly1) * 0.5f; 427 | float dmr2 = dmx * dmx + dmy * dmy; 428 | bool bevel = dmr2 * MITER_LIMIT * MITER_LIMIT < 1.0f; 429 | if (dmr2 > 1e-6f) { 430 | float scale = 1.0f / dmr2; 431 | dmx *= scale; 432 | dmy *= scale; 433 | } 434 | 435 | if (bevel && cross < 0.0f) { 436 | if (n + 2 >= maxOutVerts) 437 | return 0; 438 | float d = (1.0f - (dx0 * dx1 + dy0 * dy1)) * 0.5f; 439 | outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx0 + dx0 * d) * offset; 440 | outVerts[n * 3 + 1] = verts[vbStart + 1]; 441 | outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly0 + dy0 * d) * offset; 442 | n++; 443 | outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx1 - dx1 * d) * offset; 444 | outVerts[n * 3 + 1] = verts[vbStart + 1]; 445 | outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly1 - dy1 * d) * offset; 446 | n++; 447 | } else { 448 | if (n + 1 >= maxOutVerts) 449 | return 0; 450 | outVerts[n * 3 + 0] = verts[vbStart + 0] - dmx * offset; 451 | outVerts[n * 3 + 1] = verts[vbStart + 1]; 452 | outVerts[n * 3 + 2] = verts[vbStart + 2] - dmy * offset; 453 | n++; 454 | } 455 | } 456 | 457 | return n; 458 | } 459 | 460 | 461 | /// @par 462 | /// 463 | /// The value of spacial parameters are in world units. 464 | /// 465 | /// @see rcCompactHeightfield, rcMedianFilterWalkableArea 466 | static public void rcMarkCylinderArea(rcContext ctx, float[] pos, 467 | float r, float h, byte areaId, 468 | rcCompactHeightfield chf) { 469 | Debug.Assert(ctx != null, "rcContext is null"); 470 | 471 | ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA); 472 | 473 | float[] bmin = new float[3]; 474 | float[] bmax = new float[3]; 475 | bmin[0] = pos[0] - r; 476 | bmin[1] = pos[1]; 477 | bmin[2] = pos[2] - r; 478 | bmax[0] = pos[0] + r; 479 | bmax[1] = pos[1] + h; 480 | bmax[2] = pos[2] + r; 481 | float r2 = r * r; 482 | 483 | int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs); 484 | int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch); 485 | int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs); 486 | int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs); 487 | int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch); 488 | int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs); 489 | 490 | if (maxx < 0) return; 491 | if (minx >= chf.width) return; 492 | if (maxz < 0) return; 493 | if (minz >= chf.height) return; 494 | 495 | if (minx < 0) minx = 0; 496 | if (maxx >= chf.width) maxx = chf.width - 1; 497 | if (minz < 0) minz = 0; 498 | if (maxz >= chf.height) maxz = chf.height - 1; 499 | 500 | 501 | for (int z = minz; z <= maxz; ++z) { 502 | for (int x = minx; x <= maxx; ++x) { 503 | rcCompactCell c = chf.cells[x + z * chf.width]; 504 | for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { 505 | rcCompactSpan s = chf.spans[i]; 506 | 507 | if (chf.areas[i] == RC_NULL_AREA) 508 | continue; 509 | 510 | if ((int)s.y >= miny && (int)s.y <= maxy) { 511 | float sx = chf.bmin[0] + (x + 0.5f) * chf.cs; 512 | float sz = chf.bmin[2] + (z + 0.5f) * chf.cs; 513 | float dx = sx - pos[0]; 514 | float dz = sz - pos[2]; 515 | 516 | if (dx * dx + dz * dz < r2) { 517 | chf.areas[i] = areaId; 518 | } 519 | } 520 | } 521 | } 522 | } 523 | 524 | ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA); 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastArea.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2b5a97173d7fd084c95b0cfdf5a00a60 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastContour.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | public static partial class Recast{ 6 | static int getCornerHeight(int x, int y, int i, int dir, 7 | rcCompactHeightfield chf, 8 | ref bool isBorderVertex) 9 | { 10 | rcCompactSpan s = chf.spans[i]; 11 | int ch = (int)s.y; 12 | int dirp = (dir+1) & 0x3; 13 | 14 | uint[] regs = new uint[] {0,0,0,0}; 15 | 16 | // Combine region and area codes in order to prevent 17 | // border vertices which are in between two areas to be removed. 18 | regs[0] = (uint)( chf.spans[i].reg | (chf.areas[i] << 16) ); 19 | 20 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) 21 | { 22 | int ax = x + rcGetDirOffsetX(dir); 23 | int ay = y + rcGetDirOffsetY(dir); 24 | int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); 25 | rcCompactSpan aSpan = chf.spans[ai]; 26 | ch = Math.Max(ch, (int)aSpan.y); 27 | regs[1] = (uint)( chf.spans[ai].reg | (chf.areas[ai] << 16) ); 28 | if (rcGetCon(aSpan, dirp) != RC_NOT_CONNECTED) 29 | { 30 | int ax2 = ax + rcGetDirOffsetX(dirp); 31 | int ay2 = ay + rcGetDirOffsetY(dirp); 32 | int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dirp); 33 | rcCompactSpan as2 = chf.spans[ai2]; 34 | ch = Math.Max(ch, (int)as2.y); 35 | regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16)); 36 | } 37 | } 38 | if (rcGetCon(s, dirp) != RC_NOT_CONNECTED) 39 | { 40 | int ax = x + rcGetDirOffsetX(dirp); 41 | int ay = y + rcGetDirOffsetY(dirp); 42 | int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp); 43 | rcCompactSpan aSpan = chf.spans[ai]; 44 | ch = Math.Max(ch, (int)aSpan.y); 45 | regs[3] = (uint)(chf.spans[ai].reg | (chf.areas[ai] << 16)); 46 | if (rcGetCon(aSpan, dir) != RC_NOT_CONNECTED) 47 | { 48 | int ax2 = ax + rcGetDirOffsetX(dir); 49 | int ay2 = ay + rcGetDirOffsetY(dir); 50 | int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dir); 51 | rcCompactSpan as2 = chf.spans[ai2]; 52 | ch = Math.Max(ch, (int)as2.y); 53 | regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16)); 54 | } 55 | } 56 | 57 | // Check if the vertex is special edge vertex, these vertices will be removed later. 58 | for (int j = 0; j < 4; ++j) 59 | { 60 | int a = j; 61 | int b = (j+1) & 0x3; 62 | int c = (j+2) & 0x3; 63 | int d = (j+3) & 0x3; 64 | 65 | // The vertex is a border vertex there are two same exterior cells in a row, 66 | // followed by two interior cells and none of the regions are out of bounds. 67 | bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b]; 68 | bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0; 69 | bool intsSameArea = (regs[c]>>16) == (regs[d]>>16); 70 | bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; 71 | if (twoSameExts && twoInts && intsSameArea && noZeros) 72 | { 73 | isBorderVertex = true; 74 | break; 75 | } 76 | } 77 | 78 | return ch; 79 | } 80 | 81 | public static void walkContour(int x, int y, int i, 82 | rcCompactHeightfield chf, 83 | byte[] flags, List points) 84 | { 85 | // Choose the first non-connected edge 86 | byte dir = 0; 87 | while ((flags[i] & (1 << dir)) == 0) 88 | dir++; 89 | 90 | byte startDir = dir; 91 | int starti = i; 92 | 93 | byte area = chf.areas[i]; 94 | 95 | int iter = 0; 96 | while (++iter < 40000) 97 | { 98 | if ((flags[i] & (1 << dir)) != 0) 99 | { 100 | // Choose the edge corner 101 | bool isBorderVertex = false; 102 | bool isAreaBorder = false; 103 | int px = x; 104 | int py = getCornerHeight(x, y, i, dir, chf,ref isBorderVertex); 105 | int pz = y; 106 | switch(dir) 107 | { 108 | case 0: pz++; break; 109 | case 1: px++; pz++; break; 110 | case 2: px++; break; 111 | } 112 | int r = 0; 113 | rcCompactSpan s = chf.spans[i]; 114 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) 115 | { 116 | int ax = x + rcGetDirOffsetX(dir); 117 | int ay = y + rcGetDirOffsetY(dir); 118 | int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); 119 | r = (int)chf.spans[ai].reg; 120 | if (area != chf.areas[ai]) 121 | isAreaBorder = true; 122 | } 123 | if (isBorderVertex) 124 | r |= RC_BORDER_VERTEX; 125 | if (isAreaBorder) 126 | r |= RC_AREA_BORDER; 127 | points.Add(px); 128 | points.Add(py); 129 | points.Add(pz); 130 | points.Add(r); 131 | 132 | flags[i] &= (byte)( ~(1 << dir) ); // Remove visited edges 133 | dir = (byte)( (dir+1) & 0x3); // Rotate CW 134 | } 135 | else 136 | { 137 | int ni = -1; 138 | int nx = x + rcGetDirOffsetX(dir); 139 | int ny = y + rcGetDirOffsetY(dir); 140 | rcCompactSpan s = chf.spans[i]; 141 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) 142 | { 143 | rcCompactCell nc = chf.cells[nx+ny*chf.width]; 144 | ni = (int)nc.index + rcGetCon(s, dir); 145 | } 146 | if (ni == -1) 147 | { 148 | // Should not happen. 149 | return; 150 | } 151 | x = nx; 152 | y = ny; 153 | i = ni; 154 | dir = (byte)((dir+3) & 0x3); // Rotate CCW 155 | } 156 | 157 | if (starti == i && startDir == dir) 158 | { 159 | break; 160 | } 161 | } 162 | } 163 | 164 | public static float distancePtSeg(int x, int z, 165 | int px, int pz, 166 | int qx, int qz) 167 | { 168 | /* float pqx = (float)(qx - px); 169 | float pqy = (float)(qy - py); 170 | float pqz = (float)(qz - pz); 171 | float dx = (float)(x - px); 172 | float dy = (float)(y - py); 173 | float dz = (float)(z - pz); 174 | float d = pqx*pqx + pqy*pqy + pqz*pqz; 175 | float t = pqx*dx + pqy*dy + pqz*dz; 176 | if (d > 0) 177 | t /= d; 178 | if (t < 0) 179 | t = 0; 180 | else if (t > 1) 181 | t = 1; 182 | 183 | dx = px + t*pqx - x; 184 | dy = py + t*pqy - y; 185 | dz = pz + t*pqz - z; 186 | 187 | return dx*dx + dy*dy + dz*dz;*/ 188 | 189 | float pqx = (float)(qx - px); 190 | float pqz = (float)(qz - pz); 191 | float dx = (float)(x - px); 192 | float dz = (float)(z - pz); 193 | float d = pqx*pqx + pqz*pqz; 194 | float t = pqx*dx + pqz*dz; 195 | if (d > 0) 196 | t /= d; 197 | if (t < 0) 198 | t = 0; 199 | else if (t > 1) 200 | t = 1; 201 | 202 | dx = px + t*pqx - x; 203 | dz = pz + t*pqz - z; 204 | 205 | return dx*dx + dz*dz; 206 | } 207 | 208 | public static void simplifyContour(List points, List simplified, 209 | float maxError, int maxEdgeLen, int buildFlags) 210 | { 211 | // Add initial points. 212 | bool hasConnections = false; 213 | for (int i = 0; i < points.Count; i += 4) 214 | { 215 | if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0) 216 | { 217 | hasConnections = true; 218 | break; 219 | } 220 | } 221 | 222 | if (hasConnections) 223 | { 224 | // The contour has some portals to other regions. 225 | // Add a new point to every location where the region changes. 226 | for (int i = 0, ni = points.Count /4; i < ni; ++i) 227 | { 228 | int ii = (i+1) % ni; 229 | bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK); 230 | bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER); 231 | if (differentRegs || areaBorders) 232 | { 233 | simplified.Add(points[i*4+0]); 234 | simplified.Add(points[i*4+1]); 235 | simplified.Add(points[i*4+2]); 236 | simplified.Add(i); 237 | } 238 | } 239 | } 240 | 241 | if (simplified.Count == 0) 242 | { 243 | // If there is no connections at all, 244 | // create some initial points for the simplification process. 245 | // Find lower-left and upper-right vertices of the contour. 246 | int llx = points[0]; 247 | int lly = points[1]; 248 | int llz = points[2]; 249 | int lli = 0; 250 | int urx = points[0]; 251 | int ury = points[1]; 252 | int urz = points[2]; 253 | int uri = 0; 254 | for (int i = 0; i < points.Count; i += 4) 255 | { 256 | int x = points[i+0]; 257 | int y = points[i+1]; 258 | int z = points[i+2]; 259 | if (x < llx || (x == llx && z < llz)) 260 | { 261 | llx = x; 262 | lly = y; 263 | llz = z; 264 | lli = i/4; 265 | } 266 | if (x > urx || (x == urx && z > urz)) 267 | { 268 | urx = x; 269 | ury = y; 270 | urz = z; 271 | uri = i/4; 272 | } 273 | } 274 | simplified.Add(llx); 275 | simplified.Add(lly); 276 | simplified.Add(llz); 277 | simplified.Add(lli); 278 | 279 | simplified.Add(urx); 280 | simplified.Add(ury); 281 | simplified.Add(urz); 282 | simplified.Add(uri); 283 | } 284 | 285 | // Add points until all raw points are within 286 | // error tolerance to the simplified shape. 287 | int pn = points.Count/4; 288 | for (int i = 0; i < simplified.Count/4; ) 289 | { 290 | int ii = (i+1) % (simplified.Count/4); 291 | 292 | int ax = simplified[i*4+0]; 293 | int az = simplified[i*4+2]; 294 | int ai = simplified[i*4+3]; 295 | 296 | int bx = simplified[ii*4+0]; 297 | int bz = simplified[ii*4+2]; 298 | int bi = simplified[ii*4+3]; 299 | 300 | // Find maximum deviation from the segment. 301 | float maxd = 0; 302 | int maxi = -1; 303 | int ci, cinc, endi; 304 | 305 | // Traverse the segment in lexilogical order so that the 306 | // max deviation is calculated similarly when traversing 307 | // opposite segments. 308 | if (bx > ax || (bx == ax && bz > az)) 309 | { 310 | cinc = 1; 311 | ci = (ai+cinc) % pn; 312 | endi = bi; 313 | } 314 | else 315 | { 316 | cinc = pn-1; 317 | ci = (bi+cinc) % pn; 318 | endi = ai; 319 | } 320 | 321 | // Tessellate only outer edges or edges between areas. 322 | if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || 323 | (points[ci*4+3] & RC_AREA_BORDER) != 0) 324 | { 325 | while (ci != endi) 326 | { 327 | float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz); 328 | if (d > maxd) 329 | { 330 | maxd = d; 331 | maxi = ci; 332 | } 333 | ci = (ci+cinc) % pn; 334 | } 335 | } 336 | 337 | 338 | // If the max deviation is larger than accepted error, 339 | // add new point, else continue to next segment. 340 | if (maxi != -1 && maxd > (maxError*maxError)) 341 | { 342 | // Add space for the new point. 343 | //simplified.resize(simplified.Count+4); 344 | rccsResizeList(simplified, simplified.Count + 4); 345 | int n = simplified.Count/4; 346 | for (int j = n-1; j > i; --j) 347 | { 348 | simplified[j*4+0] = simplified[(j-1)*4+0]; 349 | simplified[j*4+1] = simplified[(j-1)*4+1]; 350 | simplified[j*4+2] = simplified[(j-1)*4+2]; 351 | simplified[j*4+3] = simplified[(j-1)*4+3]; 352 | } 353 | // Add the point. 354 | simplified[(i+1)*4+0] = points[maxi*4+0]; 355 | simplified[(i+1)*4+1] = points[maxi*4+1]; 356 | simplified[(i+1)*4+2] = points[maxi*4+2]; 357 | simplified[(i+1)*4+3] = maxi; 358 | } 359 | else 360 | { 361 | ++i; 362 | } 363 | } 364 | 365 | // Split too long edges. 366 | if (maxEdgeLen > 0 && (buildFlags & (int)(rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES|rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES)) != 0) 367 | { 368 | for (int i = 0; i < simplified.Count/4; ) 369 | { 370 | int ii = (i+1) % (simplified.Count/4); 371 | 372 | int ax = simplified[i*4+0]; 373 | int az = simplified[i*4+2]; 374 | int ai = simplified[i*4+3]; 375 | 376 | int bx = simplified[ii*4+0]; 377 | int bz = simplified[ii*4+2]; 378 | int bi = simplified[ii*4+3]; 379 | 380 | // Find maximum deviation from the segment. 381 | int maxi = -1; 382 | int ci = (ai+1) % pn; 383 | 384 | // Tessellate only outer edges or edges between areas. 385 | bool tess = false; 386 | // Wall edges. 387 | if ((buildFlags & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES) != 0 && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0) 388 | tess = true; 389 | // Edges between areas. 390 | if ((buildFlags & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES) != 0 && (points[ci*4+3] & RC_AREA_BORDER) != 0) 391 | tess = true; 392 | 393 | if (tess) 394 | { 395 | int dx = bx - ax; 396 | int dz = bz - az; 397 | if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen) 398 | { 399 | // Round based on the segments in lexilogical order so that the 400 | // max tesselation is consistent regardles in which direction 401 | // segments are traversed. 402 | int n = bi < ai ? (bi+pn - ai) : (bi - ai); 403 | if (n > 1) 404 | { 405 | if (bx > ax || (bx == ax && bz > az)) 406 | maxi = (ai + n/2) % pn; 407 | else 408 | maxi = (ai + (n+1)/2) % pn; 409 | } 410 | } 411 | } 412 | 413 | // If the max deviation is larger than accepted error, 414 | // add new point, else continue to next segment. 415 | if (maxi != -1) 416 | { 417 | // Add space for the new point. 418 | rccsResizeList(simplified, simplified.Count + 4); 419 | int n = simplified.Count/4; 420 | for (int j = n-1; j > i; --j) 421 | { 422 | simplified[j*4+0] = simplified[(j-1)*4+0]; 423 | simplified[j*4+1] = simplified[(j-1)*4+1]; 424 | simplified[j*4+2] = simplified[(j-1)*4+2]; 425 | simplified[j*4+3] = simplified[(j-1)*4+3]; 426 | } 427 | // Add the point. 428 | simplified[(i+1)*4+0] = points[maxi*4+0]; 429 | simplified[(i+1)*4+1] = points[maxi*4+1]; 430 | simplified[(i+1)*4+2] = points[maxi*4+2]; 431 | simplified[(i+1)*4+3] = maxi; 432 | } 433 | else 434 | { 435 | ++i; 436 | } 437 | } 438 | } 439 | 440 | for (int i = 0; i < simplified.Count/4; ++i) 441 | { 442 | // The edge vertex flag is take from the current raw point, 443 | // and the neighbour region is take from the next raw point. 444 | int ai = (simplified[i*4+3]+1) % pn; 445 | int bi = simplified[i*4+3]; 446 | simplified[i*4+3] = (points[ai*4+3] & (RC_CONTOUR_REG_MASK|RC_AREA_BORDER)) | (points[bi*4+3] & RC_BORDER_VERTEX); 447 | } 448 | 449 | } 450 | 451 | public static void removeDegenerateSegments(List simplified) 452 | { 453 | // Remove adjacent vertices which are equal on xz-plane, 454 | // or else the triangulator will get confused. 455 | for (int i = 0; i < simplified.Count/4; ++i) 456 | { 457 | int ni = i+1; 458 | if (ni >= (simplified.Count/4)) 459 | ni = 0; 460 | 461 | if (simplified[i*4+0] == simplified[ni*4+0] && 462 | simplified[i*4+2] == simplified[ni*4+2]) 463 | { 464 | // Degenerate segment, remove. 465 | for (int j = i; j < simplified.Count/4-1; ++j) 466 | { 467 | simplified[j*4+0] = simplified[(j+1)*4+0]; 468 | simplified[j*4+1] = simplified[(j+1)*4+1]; 469 | simplified[j*4+2] = simplified[(j+1)*4+2]; 470 | simplified[j*4+3] = simplified[(j+1)*4+3]; 471 | } 472 | //simplified.Capacity = (simplified.Count-4); 473 | rccsResizeList(simplified, simplified.Count - 4); 474 | } 475 | } 476 | } 477 | 478 | public static int calcAreaOfPolygon2D(int[] verts, int nverts) 479 | { 480 | int area = 0; 481 | for (int i = 0, j = nverts-1; i < nverts; j=i++) 482 | { 483 | int viStart = i * 4; 484 | int vjStart = j * 4; 485 | area += verts[viStart + 0] * verts[vjStart + 2] - verts[vjStart + 0] * verts[viStart + 2]; 486 | } 487 | return (area+1) / 2; 488 | } 489 | 490 | public static bool ileft(int[] a, int[] b, int[] c) 491 | { 492 | return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; 493 | } 494 | 495 | 496 | public static bool ileft(int[] a,int aStart, int[] b, int bStart, int[] c, int cStart) { 497 | return (b[bStart + 0] - a[aStart + 0]) * (c[cStart + 2] - a[aStart + 2]) - (c[cStart + 0] - a[aStart + 0]) * (b[bStart + 2] - a[aStart + 2]) <= 0; 498 | } 499 | 500 | public static void getClosestIndices(int[] vertsa, int nvertsa, 501 | int[] vertsb, int nvertsb, 502 | ref int ia, ref int ib) 503 | { 504 | int closestDist = 0xfffffff; 505 | ia = -1; 506 | ib = -1; 507 | for (int i = 0; i < nvertsa; ++i) 508 | { 509 | int i_n = (i+1) % nvertsa; 510 | int ip = (i+nvertsa-1) % nvertsa; 511 | int vaStart = i * 4; 512 | int vanStart = i_n * 4; 513 | int vapStart = ip * 4; 514 | 515 | for (int j = 0; j < nvertsb; ++j) 516 | { 517 | int vbStart = j * 4; 518 | // vb must be "infront" of va. 519 | if (ileft(vertsa,vapStart,vertsa,vaStart,vertsb,vbStart) && ileft(vertsa,vaStart,vertsa,vanStart,vertsb,vbStart)) 520 | { 521 | int dx = vertsb[vbStart+0] - vertsa[vaStart + 0]; 522 | int dz = vertsb[vbStart+2] - vertsa[vaStart+2]; 523 | int d = dx*dx + dz*dz; 524 | if (d < closestDist) 525 | { 526 | ia = i; 527 | ib = j; 528 | closestDist = d; 529 | } 530 | } 531 | } 532 | } 533 | } 534 | 535 | public static bool mergeContours(ref rcContour ca, ref rcContour cb, int ia, int ib) 536 | { 537 | int maxVerts = ca.nverts + cb.nverts + 2; 538 | int[] verts = new int[maxVerts * 4];//(int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); 539 | if (verts == null) 540 | return false; 541 | 542 | int nv = 0; 543 | 544 | // Copy contour A. 545 | for (int i = 0; i <= ca.nverts; ++i) 546 | { 547 | //int* dst = &verts[nv*4]; 548 | int dstIndex = nv*4; 549 | int srcIndex = ((ia+i)%ca.nverts)*4; 550 | for (int j=0;i<4;++i){ 551 | verts[dstIndex + j] = ca.verts[srcIndex + j]; 552 | } 553 | nv++; 554 | } 555 | 556 | // Copy contour B 557 | for (int i = 0; i <= cb.nverts; ++i) 558 | { 559 | int dstIndex = nv*4; 560 | int srcIndex = ((ib+i)%cb.nverts)*4; 561 | //int* dst = &verts[nv*4]; 562 | //const int* src = &cb.verts[((ib+i)%cb.nverts)*4]; 563 | for (int j=0;j<4;++j){ 564 | verts[dstIndex + j] = cb.verts[srcIndex + j]; 565 | } 566 | nv++; 567 | } 568 | 569 | ca.verts = verts; 570 | ca.nverts = nv; 571 | 572 | cb.verts = null; 573 | cb.nverts = 0; 574 | 575 | return true; 576 | } 577 | 578 | /// @par 579 | /// 580 | /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen 581 | /// parameters control how closely the simplified contours will match the raw contours. 582 | /// 583 | /// Simplified contours are generated such that the vertices for portals between areas match up. 584 | /// (They are considered mandatory vertices.) 585 | /// 586 | /// Setting @p maxEdgeLength to zero will disabled the edge length feature. 587 | /// 588 | /// See the #rcConfig documentation for more information on the configuration parameters. 589 | /// 590 | /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig 591 | public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf, 592 | float maxError, int maxEdgeLen, 593 | rcContourSet cset, int buildFlags) 594 | { 595 | Debug.Assert(ctx != null, "rcContext is null"); 596 | 597 | int w = chf.width; 598 | int h = chf.height; 599 | int borderSize = chf.borderSize; 600 | 601 | ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS); 602 | 603 | rcVcopy(cset.bmin, chf.bmin); 604 | rcVcopy(cset.bmax, chf.bmax); 605 | if (borderSize > 0) 606 | { 607 | // If the heightfield was build with bordersize, remove the offset. 608 | float pad = borderSize*chf.cs; 609 | cset.bmin[0] += pad; 610 | cset.bmin[2] += pad; 611 | cset.bmax[0] -= pad; 612 | cset.bmax[2] -= pad; 613 | } 614 | cset.cs = chf.cs; 615 | cset.ch = chf.ch; 616 | cset.width = chf.width - chf.borderSize*2; 617 | cset.height = chf.height - chf.borderSize*2; 618 | cset.borderSize = chf.borderSize; 619 | 620 | int maxContours = Math.Max((int)chf.maxRegions, 8); 621 | //cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); 622 | cset.conts = new rcContour[maxContours]; 623 | //if (cset.conts == null) 624 | // return false; 625 | cset.nconts = 0; 626 | 627 | //rcScopedDelete flags = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); 628 | byte[] flags = new byte[chf.spanCount]; 629 | if (flags == null) 630 | { 631 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount); 632 | return false; 633 | } 634 | 635 | ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); 636 | 637 | // Mark boundaries. 638 | for (int y = 0; y < h; ++y) 639 | { 640 | for (int x = 0; x < w; ++x) 641 | { 642 | rcCompactCell c = chf.cells[x+y*w]; 643 | for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) 644 | { 645 | byte res = 0; 646 | rcCompactSpan s = chf.spans[i]; 647 | if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0) 648 | { 649 | flags[i] = 0; 650 | continue; 651 | } 652 | for (int dir = 0; dir < 4; ++dir) 653 | { 654 | ushort r = 0; 655 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) 656 | { 657 | int ax = x + rcGetDirOffsetX(dir); 658 | int ay = y + rcGetDirOffsetY(dir); 659 | int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); 660 | r = chf.spans[ai].reg; 661 | } 662 | if (r == chf.spans[i].reg) 663 | res |= (byte)(1 << dir); 664 | } 665 | flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges. 666 | } 667 | } 668 | } 669 | 670 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); 671 | 672 | //List verts(256); 673 | List verts = new List(); 674 | verts.Capacity = 256; 675 | //List simplified(64); 676 | List simplified = new List(); 677 | simplified.Capacity = 64; 678 | 679 | for (int y = 0; y < h; ++y) 680 | { 681 | for (int x = 0; x < w; ++x) 682 | { 683 | rcCompactCell c = chf.cells[x+y*w]; 684 | for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) 685 | { 686 | if (flags[i] == 0 || flags[i] == 0xf) 687 | { 688 | flags[i] = 0; 689 | continue; 690 | } 691 | ushort reg = chf.spans[i].reg; 692 | if (reg == 0 || (reg & RC_BORDER_REG) != 0) { 693 | continue; 694 | } 695 | byte area = chf.areas[i]; 696 | 697 | //verts.resize(0); 698 | //simplified.resize(0); 699 | verts.Clear(); 700 | simplified.Clear(); 701 | 702 | ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); 703 | walkContour(x, y, i, chf, flags, verts); 704 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE); 705 | 706 | ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY); 707 | simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); 708 | removeDegenerateSegments(simplified); 709 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY); 710 | 711 | 712 | // Store region.contour remap info. 713 | // Create contour. 714 | if (simplified.Count/4 >= 3) 715 | { 716 | if (cset.nconts >= maxContours) 717 | { 718 | // Allocate more contours. 719 | // This can happen when there are tiny holes in the heightfield. 720 | int oldMax = maxContours; 721 | maxContours *= 2; 722 | rcContour[] newConts = new rcContour[maxContours];// (rcContour*)rcAlloc(sizeof(rcContour) * maxContours, RC_ALLOC_PERM); 723 | for (int j = 0; j < cset.nconts; ++j) 724 | { 725 | newConts[j] = cset.conts[j]; 726 | // Reset source pointers to prevent data deletion. 727 | cset.conts[j].verts = null; 728 | cset.conts[j].rverts = null; 729 | } 730 | //rcFree(cset.conts); 731 | cset.conts = newConts; 732 | 733 | ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " + oldMax + " to "+ maxContours); 734 | } 735 | 736 | int contId = cset.nconts; 737 | cset.nconts++; 738 | rcContour cont = cset.conts[contId]; 739 | 740 | cont.nverts = simplified.Count/4; 741 | cont.verts = new int[cont.nverts * 4]; //(int*)rcAlloc(sizeof(int)*cont.nverts*4, RC_ALLOC_PERM); 742 | if (cont.verts == null) 743 | { 744 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts); 745 | return false; 746 | } 747 | //memcpy(cont.verts, &simplified[0], sizeof(int)*cont.nverts*4); 748 | for (int j = 0; j < cont.nverts * 4; ++j) { 749 | cont.verts[j] = simplified[j]; 750 | } 751 | if (borderSize > 0) 752 | { 753 | // If the heightfield was build with bordersize, remove the offset. 754 | for (int j = 0; j < cont.nverts; ++j) 755 | { 756 | //int* v = &cont.verts[j*4]; 757 | cont.verts[j * 4] -= borderSize; 758 | cont.verts[j*4 + 2] -= borderSize; 759 | //v[0] -= borderSize; 760 | //v[2] -= borderSize; 761 | } 762 | } 763 | 764 | cont.nrverts = verts.Count/4; 765 | cont.rverts = new int[cont.nrverts * 4];//(int*)rcAlloc(sizeof(int)*cont.nrverts*4, RC_ALLOC_PERM); 766 | if (cont.rverts == null) 767 | { 768 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts); 769 | return false; 770 | } 771 | //memcpy(cont.rverts, &verts[0], sizeof(int)*cont.nrverts*4); 772 | for (int j = 0; j < cont.nrverts * 4; ++j) { 773 | cont.rverts[j] = verts[j]; 774 | } 775 | if (borderSize > 0) 776 | { 777 | // If the heightfield was build with bordersize, remove the offset. 778 | for (int j = 0; j < cont.nrverts; ++j) 779 | { 780 | //int* v = &cont.rverts[j*4]; 781 | cont.rverts[j * 4] -= borderSize; 782 | cont.rverts[j * 4 + 2] -= borderSize; 783 | } 784 | } 785 | 786 | /* cont.cx = cont.cy = cont.cz = 0; 787 | for (int i = 0; i < cont.nverts; ++i) 788 | { 789 | cont.cx += cont.verts[i*4+0]; 790 | cont.cy += cont.verts[i*4+1]; 791 | cont.cz += cont.verts[i*4+2]; 792 | } 793 | cont.cx /= cont.nverts; 794 | cont.cy /= cont.nverts; 795 | cont.cz /= cont.nverts;*/ 796 | 797 | cont.reg = reg; 798 | cont.area = area; 799 | 800 | cset.conts[contId] = cont; 801 | } 802 | } 803 | } 804 | } 805 | 806 | // Check and merge droppings. 807 | // Sometimes the previous algorithms can fail and create several contours 808 | // per area. This pass will try to merge the holes into the main region. 809 | for (int i = 0; i < cset.nconts; ++i) 810 | { 811 | rcContour cont = cset.conts[i]; 812 | // Check if the contour is would backwards. 813 | if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) 814 | { 815 | // Find another contour which has the same region ID. 816 | int mergeIdx = -1; 817 | for (int j = 0; j < cset.nconts; ++j) 818 | { 819 | if (i == j) continue; 820 | if (cset.conts[j].nverts != 0 && cset.conts[j].reg == cont.reg) 821 | { 822 | // Make sure the polygon is correctly oriented. 823 | if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts) != 0) 824 | { 825 | mergeIdx = j; 826 | break; 827 | } 828 | } 829 | } 830 | if (mergeIdx == -1) 831 | { 832 | ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour " + i); 833 | } 834 | else 835 | { 836 | rcContour mcont = cset.conts[mergeIdx]; 837 | // Merge by closest points. 838 | int ia = 0, ib = 0; 839 | getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib); 840 | if (ia == -1 || ib == -1) 841 | { 842 | ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for " + i + " and " + mergeIdx); 843 | continue; 844 | } 845 | if (!mergeContours(ref mcont,ref cont, ia, ib)) 846 | { 847 | ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx); 848 | continue; 849 | } 850 | cset.conts[mergeIdx] = mcont; 851 | cset.conts[i] = cont; 852 | } 853 | } 854 | } 855 | 856 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS); 857 | 858 | return true; 859 | } 860 | } -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastContour.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 511b9d991b6725a4b8e81ea576e85e4b 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System; 3 | 4 | public static partial class Recast{ 5 | /// @par 6 | /// 7 | /// Allows the formation of walkable regions that will flow over low lying 8 | /// objects such as curbs, and up structures such as stairways. 9 | /// 10 | /// Two neighboring spans are walkable if: rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb 11 | /// 12 | /// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call 13 | /// #rcFilterLedgeSpans after calling this filter. 14 | /// 15 | /// @see rcHeightfield, rcConfig 16 | public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid) 17 | { 18 | Debug.Assert(ctx != null, "rcContext is null"); 19 | 20 | ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); 21 | 22 | int w = solid.width; 23 | int h = solid.height; 24 | 25 | for (int y = 0; y < h; ++y) 26 | { 27 | for (int x = 0; x < w; ++x) 28 | { 29 | rcSpan ps = null; 30 | bool previousWalkable = false; 31 | byte previousArea = RC_NULL_AREA; 32 | 33 | for (rcSpan s = solid.spans[x + y*w]; s != null; ps = s, s = s.next) 34 | { 35 | bool walkable = s.area != RC_NULL_AREA; 36 | // If current span is not walkable, but there is walkable 37 | // span just below it, mark the span above it walkable too. 38 | if (!walkable && previousWalkable) 39 | { 40 | if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb){ 41 | s.area = previousArea; 42 | } 43 | } 44 | // Copy walkable flag so that it cannot propagate 45 | // past multiple non-walkable objects. 46 | previousWalkable = walkable; 47 | previousArea = s.area; 48 | } 49 | } 50 | } 51 | 52 | ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES); 53 | } 54 | 55 | /// @par 56 | /// 57 | /// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb 58 | /// from the current span's maximum. 59 | /// This method removes the impact of the overestimation of conservative voxelization 60 | /// so the resulting mesh will not have regions hanging in the air over ledges. 61 | /// 62 | /// A span is a ledge if: rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb 63 | /// 64 | /// @see rcHeightfield, rcConfig 65 | public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb, 66 | rcHeightfield solid) 67 | { 68 | Debug.Assert(ctx != null, "rcContext is null"); 69 | 70 | ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); 71 | 72 | int w = solid.width; 73 | int h = solid.height; 74 | int MAX_HEIGHT = 0xffff; 75 | 76 | // Mark border spans. 77 | for (int y = 0; y < h; ++y) 78 | { 79 | for (int x = 0; x < w; ++x) 80 | { 81 | for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next) 82 | { 83 | // Skip non walkable spans. 84 | if (s.area == RC_NULL_AREA){ 85 | continue; 86 | } 87 | 88 | int bot = (int)(s.smax); 89 | int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; 90 | 91 | // Find neighbours minimum height. 92 | int minh = MAX_HEIGHT; 93 | 94 | // Min and max height of accessible neighbours. 95 | int asmin = s.smax; 96 | int asmax = s.smax; 97 | 98 | for (int dir = 0; dir < 4; ++dir) 99 | { 100 | int dx = x + rcGetDirOffsetX(dir); 101 | int dy = y + rcGetDirOffsetY(dir); 102 | // Skip neighbours which are out of bounds. 103 | if (dx < 0 || dy < 0 || dx >= w || dy >= h) 104 | { 105 | minh = Math.Min(minh, -walkableClimb - bot); 106 | continue; 107 | } 108 | 109 | // From minus infinity to the first span. 110 | rcSpan ns = solid.spans[dx + dy*w]; 111 | int nbot = -walkableClimb; 112 | int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT; 113 | // Skip neightbour if the gap between the spans is too small. 114 | if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) 115 | minh = Math.Min(minh, nbot - bot); 116 | 117 | // Rest of the spans. 118 | for (ns = solid.spans[dx + dy*w]; ns != null; ns = ns.next) 119 | { 120 | nbot = (int)ns.smax; 121 | ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT; 122 | // Skip neightbour if the gap between the spans is too small. 123 | if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight) 124 | { 125 | minh = Math.Min(minh, nbot - bot); 126 | 127 | // Find min/max accessible neighbour height. 128 | if (Math.Abs(nbot - bot) <= walkableClimb) 129 | { 130 | if (nbot < asmin) asmin = nbot; 131 | if (nbot > asmax) asmax = nbot; 132 | } 133 | 134 | } 135 | } 136 | } 137 | 138 | // The current span is close to a ledge if the drop to any 139 | // neighbour span is less than the walkableClimb. 140 | if (minh < -walkableClimb){ 141 | s.area = RC_NULL_AREA; 142 | } 143 | 144 | // If the difference between all neighbours is too large, 145 | // we are at steep slope, mark the span as ledge. 146 | if ((asmax - asmin) > walkableClimb) 147 | { 148 | s.area = RC_NULL_AREA; 149 | } 150 | } 151 | } 152 | } 153 | 154 | ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER); 155 | } 156 | 157 | /// @par 158 | /// 159 | /// For this filter, the clearance above the span is the distance from the span's 160 | /// maximum to the next higher span's minimum. (Same grid column.) 161 | /// 162 | /// @see rcHeightfield, rcConfig 163 | public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid) 164 | { 165 | Debug.Assert(ctx != null, "rcContext is null"); 166 | 167 | ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); 168 | 169 | int w = solid.width; 170 | int h = solid.height; 171 | int MAX_HEIGHT = 0xffff; 172 | 173 | // Remove walkable flag from spans which do not have enough 174 | // space above them for the agent to stand there. 175 | for (int y = 0; y < h; ++y) 176 | { 177 | for (int x = 0; x < w; ++x) 178 | { 179 | for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next) 180 | { 181 | int bot = (int)(s.smax); 182 | int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT; 183 | if ((top - bot) <= walkableHeight) { 184 | s.area = RC_NULL_AREA; 185 | } 186 | } 187 | } 188 | } 189 | 190 | ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE); 191 | } 192 | } -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastFilter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e0e937af5d275b543bc05da774129d13 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastLayers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | public static partial class Recast{ 5 | 6 | const int RC_MAX_LAYERS = RC_NOT_CONNECTED; 7 | const int RC_MAX_NEIS = 16; 8 | 9 | public class rcLayerRegion 10 | { 11 | public byte[] layers = new byte[RC_MAX_LAYERS]; 12 | public byte[] neis = new byte[RC_MAX_NEIS]; 13 | public ushort ymin; 14 | public ushort ymax; 15 | public byte layerId; // Layer ID 16 | public byte nlayers; // Layer count 17 | public byte nneis; // Neighbour count 18 | public byte baseFlag; // Flag indicating if the region is hte base of merged regions. 19 | }; 20 | 21 | 22 | public static void addUnique(byte[] a,ref byte an, byte v) 23 | { 24 | int n = (int)an; 25 | for (int i = 0; i < n; ++i){ 26 | if (a[i] == v){ 27 | return; 28 | } 29 | } 30 | a[an] = v; 31 | an++; 32 | } 33 | 34 | public static bool contains(byte[] a, byte an, byte v) 35 | { 36 | int n = (int)an; 37 | for (int i = 0; i < n; ++i){ 38 | if (a[i] == v){ 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | public static bool overlapRange( ushort amin, ushort amax, 46 | ushort bmin, ushort bmax) 47 | { 48 | return (amin > bmax || amax < bmin) ? false : true; 49 | } 50 | 51 | 52 | 53 | public class rcLayerSweepSpan 54 | { 55 | public ushort ns; // number samples 56 | public byte id; // region id 57 | public byte nei; // neighbour id 58 | }; 59 | 60 | /// @par 61 | /// 62 | /// See the #rcConfig documentation for more information on the configuration parameters. 63 | /// 64 | /// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig 65 | public static bool rcBuildHeightfieldLayers(rcContext ctx, rcCompactHeightfield chf, 66 | int borderSize, int walkableHeight, 67 | rcHeightfieldLayerSet lset) 68 | { 69 | Debug.Assert(ctx != null, "rcContext is null"); 70 | 71 | ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); 72 | 73 | int w = chf.width; 74 | int h = chf.height; 75 | 76 | //rcScopedDelete srcReg = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP); 77 | byte[] srcReg = new byte[chf.spanCount]; 78 | if (srcReg == null) 79 | { 80 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' " + chf.spanCount); 81 | return false; 82 | } 83 | //memset(srcReg,0xff,sizeof(byte)*chf.spanCount); 84 | for (int i=0;i sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); 90 | rcLayerSweepSpan[] sweeps = new rcLayerSweepSpan[nsweeps]; 91 | if (sweeps == null) 92 | { 93 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' " + nsweeps); 94 | return false; 95 | } 96 | 97 | 98 | // Partition walkable area into monotone regions. 99 | int[] prevCount = new int[256]; 100 | byte regId = 0; 101 | 102 | for (int y = borderSize; y < h-borderSize; ++y) 103 | { 104 | //memset to 0 is done by C# alloc 105 | //memset(prevCount,0,sizeof(int)*regId); 106 | 107 | byte sweepId = 0; 108 | 109 | for (int x = borderSize; x < w-borderSize; ++x) 110 | { 111 | rcCompactCell c = chf.cells[x+y*w]; 112 | 113 | for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) 114 | { 115 | rcCompactSpan s = chf.spans[i]; 116 | if (chf.areas[i] == RC_NULL_AREA) continue; 117 | 118 | byte sid = 0xff; 119 | 120 | // -x 121 | if (rcGetCon(s, 0) != RC_NOT_CONNECTED) 122 | { 123 | int ax = x + rcGetDirOffsetX(0); 124 | int ay = y + rcGetDirOffsetY(0); 125 | int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); 126 | if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff) 127 | sid = srcReg[ai]; 128 | } 129 | 130 | if (sid == 0xff) 131 | { 132 | sid = sweepId++; 133 | sweeps[sid].nei = (byte)0xff; 134 | sweeps[sid].ns = 0; 135 | } 136 | 137 | // -y 138 | if (rcGetCon(s,3) != RC_NOT_CONNECTED) 139 | { 140 | int ax = x + rcGetDirOffsetX(3); 141 | int ay = y + rcGetDirOffsetY(3); 142 | int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); 143 | byte nr = srcReg[ai]; 144 | if (nr != 0xff) 145 | { 146 | // Set neighbour when first valid neighbour is encoutered. 147 | if (sweeps[sid].ns == 0) 148 | sweeps[sid].nei = nr; 149 | 150 | if (sweeps[sid].nei == nr) 151 | { 152 | // Update existing neighbour 153 | sweeps[sid].ns++; 154 | prevCount[nr]++; 155 | } 156 | else 157 | { 158 | // This is hit if there is nore than one neighbour. 159 | // Invalidate the neighbour. 160 | sweeps[sid].nei = 0xff; 161 | } 162 | } 163 | } 164 | 165 | srcReg[i] = sid; 166 | } 167 | } 168 | 169 | // Create unique ID. 170 | for (int i = 0; i < sweepId; ++i) 171 | { 172 | // If the neighbour is set and there is only one continuous connection to it, 173 | // the sweep will be merged with the previous one, else new region is created. 174 | if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns) 175 | { 176 | sweeps[i].id = sweeps[i].nei; 177 | } 178 | else 179 | { 180 | if (regId == 255) 181 | { 182 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow."); 183 | return false; 184 | } 185 | sweeps[i].id = regId++; 186 | } 187 | } 188 | 189 | // Remap local sweep ids to region ids. 190 | for (int x = borderSize; x < w-borderSize; ++x) 191 | { 192 | rcCompactCell c = chf.cells[x+y*w]; 193 | for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) 194 | { 195 | if (srcReg[i] != 0xff) 196 | srcReg[i] = sweeps[srcReg[i]].id; 197 | } 198 | } 199 | } 200 | 201 | // Allocate and init layer regions. 202 | int nregs = (int)regId; 203 | //rcScopedDelete regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP); 204 | rcLayerRegion[] regs = new rcLayerRegion[nregs]; 205 | if (regs == null) 206 | { 207 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' " + nregs); 208 | return false; 209 | } 210 | //memset(regs, 0, sizeof(rcLayerRegion)*nregs); 211 | for (int i = 0; i < nregs; ++i) 212 | { 213 | regs[i].layerId = 0xff; 214 | regs[i].ymin = 0xffff; 215 | regs[i].ymax = 0; 216 | } 217 | 218 | // Find region neighbours and overlapping regions. 219 | for (int y = 0; y < h; ++y) 220 | { 221 | for (int x = 0; x < w; ++x) 222 | { 223 | rcCompactCell c = chf.cells[x+y*w]; 224 | 225 | byte[] lregs = new byte[RC_MAX_LAYERS]; 226 | int nlregs = 0; 227 | 228 | for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) 229 | { 230 | rcCompactSpan s = chf.spans[i]; 231 | byte ri = srcReg[i]; 232 | if (ri == 0xff){ 233 | continue; 234 | } 235 | 236 | regs[ri].ymin = Math.Min(regs[ri].ymin, s.y); 237 | regs[ri].ymax = Math.Max(regs[ri].ymax, s.y); 238 | 239 | // Collect all region layers. 240 | if (nlregs < RC_MAX_LAYERS) 241 | lregs[nlregs++] = ri; 242 | 243 | // Update neighbours 244 | for (int dir = 0; dir < 4; ++dir) 245 | { 246 | if (rcGetCon(s, dir) != RC_NOT_CONNECTED) 247 | { 248 | int ax = x + rcGetDirOffsetX(dir); 249 | int ay = y + rcGetDirOffsetY(dir); 250 | int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); 251 | byte rai = srcReg[ai]; 252 | if (rai != 0xff && rai != ri){ 253 | addUnique(regs[ri].neis,ref regs[ri].nneis, rai); 254 | } 255 | } 256 | } 257 | 258 | } 259 | 260 | // Update overlapping regions. 261 | for (int i = 0; i < nlregs-1; ++i) 262 | { 263 | for (int j = i+1; j < nlregs; ++j) 264 | { 265 | if (lregs[i] != lregs[j]) 266 | { 267 | rcLayerRegion ri = regs[lregs[i]]; 268 | rcLayerRegion rj = regs[lregs[j]]; 269 | addUnique(ri.layers,ref ri.nlayers, lregs[j]); 270 | addUnique(rj.layers,ref rj.nlayers, lregs[i]); 271 | } 272 | } 273 | } 274 | 275 | } 276 | } 277 | 278 | // Create 2D layers from regions. 279 | byte layerId = 0; 280 | 281 | const int MAX_STACK = 64; 282 | byte[] stack = new byte[MAX_STACK]; 283 | int nstack = 0; 284 | 285 | for (int i = 0; i < nregs; ++i) 286 | { 287 | rcLayerRegion root = regs[i]; 288 | // Skip alreadu visited. 289 | if (root.layerId != 0xff){ 290 | continue; 291 | } 292 | 293 | // Start search. 294 | root.layerId = layerId; 295 | root.baseFlag = 1; 296 | 297 | nstack = 0; 298 | stack[nstack++] = (byte)i; 299 | 300 | while (nstack != 0) 301 | { 302 | // Pop front 303 | rcLayerRegion reg = regs[stack[0]]; 304 | nstack--; 305 | for (int j = 0; j < nstack; ++j){ 306 | stack[j] = stack[j+1]; 307 | } 308 | 309 | int nneis = (int)reg.nneis; 310 | for (int j = 0; j < nneis; ++j) 311 | { 312 | byte nei = reg.neis[j]; 313 | rcLayerRegion regn = regs[nei]; 314 | // Skip already visited. 315 | if (regn.layerId != 0xff){ 316 | continue; 317 | } 318 | // Skip if the neighbour is overlapping root region. 319 | if (contains(root.layers, root.nlayers, nei)){ 320 | continue; 321 | } 322 | // Skip if the height range would become too large. 323 | int ymin = Math.Min(root.ymin, regn.ymin); 324 | int ymax = Math.Max(root.ymax, regn.ymax); 325 | if ((ymax - ymin) >= 255){ 326 | continue; 327 | } 328 | 329 | if (nstack < MAX_STACK) 330 | { 331 | // Deepen 332 | stack[nstack++] = (byte)nei; 333 | 334 | // Mark layer id 335 | regn.layerId = layerId; 336 | // Merge current layers to root. 337 | for (int k = 0; k < regn.nlayers; ++k){ 338 | addUnique(root.layers,ref root.nlayers, regn.layers[k]); 339 | } 340 | root.ymin = Math.Min(root.ymin, regn.ymin); 341 | root.ymax = Math.Max(root.ymax, regn.ymax); 342 | } 343 | } 344 | } 345 | 346 | layerId++; 347 | } 348 | 349 | // Merge non-overlapping regions that are close in height. 350 | ushort mergeHeight = (ushort)(walkableHeight * 4); 351 | 352 | for (int i = 0; i < nregs; ++i) 353 | { 354 | rcLayerRegion ri = regs[i]; 355 | if (ri.baseFlag == 0){ 356 | continue; 357 | } 358 | 359 | byte newId = ri.layerId; 360 | 361 | for (;;) 362 | { 363 | byte oldId = 0xff; 364 | 365 | for (int j = 0; j < nregs; ++j) 366 | { 367 | if (i == j){ 368 | continue; 369 | } 370 | rcLayerRegion rj = regs[j]; 371 | if (rj.baseFlag == 0){ 372 | continue; 373 | } 374 | 375 | // Skip if teh regions are not close to each other. 376 | if (!overlapRange(ri.ymin, 377 | (ushort)(ri.ymax + mergeHeight), 378 | rj.ymin, 379 | (ushort)(rj.ymax + mergeHeight))){ 380 | continue; 381 | } 382 | // Skip if the height range would become too large. 383 | int ymin = Math.Min(ri.ymin, rj.ymin); 384 | int ymax = Math.Max(ri.ymax, rj.ymax); 385 | if ((ymax - ymin) >= 255){ 386 | continue; 387 | } 388 | 389 | // Make sure that there is no overlap when mergin 'ri' and 'rj'. 390 | bool overlap = false; 391 | // Iterate over all regions which have the same layerId as 'rj' 392 | for (int k = 0; k < nregs; ++k) 393 | { 394 | if (regs[k].layerId != rj.layerId) 395 | continue; 396 | // Check if region 'k' is overlapping region 'ri' 397 | // Index to 'regs' is the same as region id. 398 | if (contains(ri.layers,ri.nlayers, (byte)k)) 399 | { 400 | overlap = true; 401 | break; 402 | } 403 | } 404 | // Cannot merge of regions overlap. 405 | if (overlap) 406 | continue; 407 | 408 | // Can merge i and j. 409 | oldId = rj.layerId; 410 | break; 411 | } 412 | 413 | // Could not find anything to merge with, stop. 414 | if (oldId == 0xff) 415 | break; 416 | 417 | // Merge 418 | for (int j = 0; j < nregs; ++j) 419 | { 420 | rcLayerRegion rj = regs[j]; 421 | if (rj.layerId == oldId) 422 | { 423 | rj.baseFlag = 0; 424 | // Remap layerIds. 425 | rj.layerId = newId; 426 | // Add overlaid layers from 'rj' to 'ri'. 427 | for (int k = 0; k < rj.nlayers; ++k){ 428 | addUnique(ri.layers,ref ri.nlayers, rj.layers[k]); 429 | } 430 | // Update heigh bounds. 431 | ri.ymin = Math.Min(ri.ymin, rj.ymin); 432 | ri.ymax = Math.Max(ri.ymax, rj.ymax); 433 | } 434 | } 435 | } 436 | } 437 | 438 | // Compact layerIds 439 | byte[] remap = new byte[256]; 440 | //memset(remap, 0, 256); 441 | 442 | // Find number of unique layers. 443 | layerId = 0; 444 | for (int i = 0; i < nregs; ++i){ 445 | remap[regs[i].layerId] = 1; 446 | } 447 | for (int i = 0; i < 256; ++i) 448 | { 449 | if (remap[i] != 0){ 450 | remap[i] = layerId++; 451 | } 452 | else{ 453 | remap[i] = 0xff; 454 | } 455 | } 456 | // Remap ids. 457 | for (int i = 0; i < nregs; ++i){ 458 | regs[i].layerId = remap[regs[i].layerId]; 459 | } 460 | 461 | // No layers, return empty. 462 | if (layerId == 0) 463 | { 464 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); 465 | return true; 466 | } 467 | 468 | // Create layers. 469 | Debug.Assert(lset.layers == null,"Assert lset.layers == 0"); 470 | 471 | int lw = w - borderSize*2; 472 | int lh = h - borderSize*2; 473 | 474 | // Build contracted bbox for layers. 475 | float[] bmin = new float[3]; 476 | float[] bmax = new float[3]; 477 | rcVcopy(bmin, chf.bmin); 478 | rcVcopy(bmax, chf.bmax); 479 | bmin[0] += borderSize*chf.cs; 480 | bmin[2] += borderSize*chf.cs; 481 | bmax[0] -= borderSize*chf.cs; 482 | bmax[2] -= borderSize*chf.cs; 483 | 484 | lset.nlayers = (int)layerId; 485 | 486 | //lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM); 487 | lset.layers = new rcHeightfieldLayer[lset.nlayers]; 488 | if (lset.layers == null) 489 | { 490 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' " + lset.nlayers); 491 | return false; 492 | } 493 | //memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers); 494 | 495 | 496 | // Store layers. 497 | for (int i = 0; i < lset.nlayers; ++i) 498 | { 499 | byte curId = (byte)i; 500 | 501 | // Allocate memory for the current layer. 502 | rcHeightfieldLayer layer = lset.layers[i]; 503 | //memset(layer, 0, sizeof(rcHeightfieldLayer)); 504 | 505 | int gridSize = sizeof(byte)*lw*lh; 506 | 507 | layer.heights = new byte[gridSize];//(byte*)rcAlloc(gridSize, RC_ALLOC_PERM); 508 | if (layer.heights == null) 509 | { 510 | ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' " + gridSize); 511 | return false; 512 | } 513 | //memset(layer.heights, 0xff, gridSize); 514 | for (int j=0;j hmin) 613 | layer.heights[idx] = Math.Max(layer.heights[idx], (byte)(aSpan.y - hmin)); 614 | } 615 | // Valid connection mask 616 | if (chf.areas[ai] != RC_NULL_AREA && lid == alid) 617 | { 618 | int nx = ax - borderSize; 619 | int ny = ay - borderSize; 620 | if (nx >= 0 && ny >= 0 && nx < lw && ny < lh) 621 | con |= (byte)(1< layer.maxx) 632 | layer.minx = layer.maxx = 0; 633 | if (layer.miny > layer.maxy) 634 | layer.miny = layer.maxy = 0; 635 | } 636 | 637 | ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS); 638 | 639 | return true; 640 | } 641 | } -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastLayers.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c80e7c90676f244d9da625ac00a9dc5 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastMesh.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aea094c9539222c459490fa34fc670c6 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastMeshDetail.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c1f600c7e8989846ada0cab453f2ec2 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastRasterization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | public static partial class Recast{ 5 | static bool overlapBounds(float[] amin, float[] amax, float[] bmin, float[] bmax) 6 | { 7 | bool overlap = true; 8 | overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; 9 | overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; 10 | overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; 11 | return overlap; 12 | } 13 | 14 | static bool overlapInterval(ushort amin, ushort amax, 15 | ushort bmin, ushort bmax) 16 | { 17 | if (amax < bmin) return false; 18 | if (amin > bmax) return false; 19 | return true; 20 | } 21 | 22 | 23 | static rcSpan allocSpan(rcHeightfield hf) 24 | { 25 | // If running out of memory, allocate new page and update the freelist. 26 | if (hf.freelist == null || hf.freelist.next == null) 27 | { 28 | // Create new page. 29 | // Allocate memory for the new pool. 30 | //rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); 31 | rcSpanPool pool = new rcSpanPool(); 32 | if (pool == null) 33 | return null; 34 | pool.next = null; 35 | // Add the pool into the list of pools. 36 | pool.next = hf.pools; 37 | hf.pools = pool; 38 | // Add new items to the free list. 39 | rcSpan freelist = hf.freelist; 40 | //rcSpan head = pool.items[0]; 41 | //rcSpan it = pool.items[RC_SPANS_PER_POOL]; 42 | int itIndex = RC_SPANS_PER_POOL; 43 | do 44 | { 45 | --itIndex; 46 | pool.items[itIndex].next = freelist; 47 | freelist = pool.items[itIndex]; 48 | } 49 | while (itIndex != 0); 50 | hf.freelist = pool.items[itIndex]; 51 | } 52 | 53 | // Pop item from in front of the free list. 54 | rcSpan it = hf.freelist; 55 | hf.freelist = hf.freelist.next; 56 | return it; 57 | } 58 | 59 | static void freeSpan(rcHeightfield hf, rcSpan ptr) 60 | { 61 | if (ptr == null) { 62 | return; 63 | } 64 | // Add the node in front of the free list. 65 | ptr.next = hf.freelist; 66 | hf.freelist = ptr; 67 | } 68 | 69 | static void addSpan(rcHeightfield hf, int x, int y, 70 | ushort smin, ushort smax, 71 | byte area, int flagMergeThr) 72 | { 73 | 74 | int idx = x + y*hf.width; 75 | 76 | rcSpan s = allocSpan(hf); 77 | s.smin = smin; 78 | s.smax = smax; 79 | s.area = area; 80 | s.next = null; 81 | 82 | // Empty cell, add the first span. 83 | if (hf.spans[idx] == null) 84 | { 85 | hf.spans[idx] = s; 86 | return; 87 | } 88 | rcSpan prev = null; 89 | rcSpan cur = hf.spans[idx]; 90 | 91 | // Insert and merge spans. 92 | while (cur != null) 93 | { 94 | if (cur.smin > s.smax) 95 | { 96 | // Current span is further than the new span, break. 97 | break; 98 | } 99 | else if (cur.smax < s.smin) 100 | { 101 | // Current span is before the new span advance. 102 | prev = cur; 103 | cur = cur.next; 104 | } 105 | else 106 | { 107 | // Merge spans. 108 | if (cur.smin < s.smin) 109 | s.smin = cur.smin; 110 | if (cur.smax > s.smax) 111 | s.smax = cur.smax; 112 | 113 | // Merge flags. 114 | if (Math.Abs((int)s.smax - (int)cur.smax) <= flagMergeThr){ 115 | s.area = Math.Max(s.area, cur.area); 116 | } 117 | 118 | // Remove current span. 119 | rcSpan next = cur.next; 120 | freeSpan(hf, cur); 121 | if (prev != null) 122 | prev.next = next; 123 | else 124 | hf.spans[idx] = next; 125 | cur = next; 126 | } 127 | } 128 | 129 | // Insert new span. 130 | if (prev != null) 131 | { 132 | s.next = prev.next; 133 | prev.next = s; 134 | } 135 | else 136 | { 137 | s.next = hf.spans[idx]; 138 | hf.spans[idx] = s; 139 | } 140 | } 141 | 142 | /// @par 143 | /// 144 | /// The span addition can be set to favor flags. If the span is merged to 145 | /// another span and the new @p smax is within @p flagMergeThr units 146 | /// from the existing span, the span flags are merged. 147 | /// 148 | /// @see rcHeightfield, rcSpan. 149 | static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y, 150 | ushort smin, ushort smax, 151 | byte area, int flagMergeThr) 152 | { 153 | // Debug.Assert(ctx != null, "rcContext is null"); 154 | addSpan(hf, x,y, smin, smax, area, flagMergeThr); 155 | } 156 | 157 | // divides a convex polygons into two convex polygons on both sides of a line 158 | static void dividePoly(float[] _in, int nin, 159 | float[] out1, ref int nout1, 160 | float[] out2, ref int nout2, 161 | float x, int axis) 162 | { 163 | float[] d = new float[12]; 164 | for (int i = 0; i < nin; ++i){ 165 | d[i] = x - _in[i*3+axis]; 166 | } 167 | 168 | int m = 0, n = 0; 169 | for (int i = 0, j = nin-1; i < nin; j=i, ++i) 170 | { 171 | bool ina = d[j] >= 0; 172 | bool inb = d[i] >= 0; 173 | if (ina != inb) 174 | { 175 | float s = d[j] / (d[j] - d[i]); 176 | out1[m*3+0] = _in[j*3+0] + (_in[i*3+0] - _in[j*3+0])*s; 177 | out1[m*3+1] = _in[j*3+1] + (_in[i*3+1] - _in[j*3+1])*s; 178 | out1[m*3+2] = _in[j*3+2] + (_in[i*3+2] - _in[j*3+2])*s; 179 | rcVcopy(out2, n*3, out1, m*3); 180 | m++; 181 | n++; 182 | // add the i'th point to the right polygon. Do NOT add points that are on the dividing line 183 | // since these were already added above 184 | if (d[i] > 0) 185 | { 186 | rcVcopy(out1,m*3, _in, i*3); 187 | m++; 188 | } 189 | else if (d[i] < 0) 190 | { 191 | rcVcopy(out2,n*3, _in, i*3); 192 | n++; 193 | } 194 | } 195 | else // same side 196 | { 197 | // add the i'th point to the right polygon. Addition is done even for points on the dividing line 198 | if (d[i] >= 0) 199 | { 200 | rcVcopy(out1, m*3, _in, i*3); 201 | m++; 202 | if (d[i] != 0) 203 | continue; 204 | } 205 | rcVcopy(out2, n*3, _in, i*3); 206 | n++; 207 | } 208 | } 209 | 210 | nout1 = m; 211 | nout2 = n; 212 | } 213 | 214 | 215 | 216 | static void rasterizeTri(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, 217 | byte area, rcHeightfield hf, 218 | float[] bmin, float[] bmax, 219 | float cs, float ics, float ich, 220 | int flagMergeThr) 221 | { 222 | int w = hf.width; 223 | int h = hf.height; 224 | float[] tmin = new float[3]; 225 | float[] tmax = new float[3]; 226 | float by = bmax[1] - bmin[1]; 227 | 228 | // Calculate the bounding box of the triangle. 229 | rcVcopy(tmin, 0, v0, v0Start); 230 | rcVcopy(tmax, 0, v0, v0Start); 231 | rcVmin(tmin, 0, v1, v1Start); 232 | rcVmin(tmin, 0, v2, v2Start); 233 | rcVmax(tmax, 0, v1, v1Start); 234 | rcVmax(tmax, 0, v2, v2Start); 235 | 236 | // If the triangle does not touch the bbox of the heightfield, skip the triagle. 237 | if (!overlapBounds(bmin, bmax, tmin, tmax)) 238 | return; 239 | 240 | // Calculate the footprint of the triangle on the grid's y-axis 241 | int y0 = (int)((tmin[2] - bmin[2])*ics); 242 | int y1 = (int)((tmax[2] - bmin[2])*ics); 243 | y0 = rcClamp(y0, 0, h-1); 244 | y1 = rcClamp(y1, 0, h-1); 245 | 246 | // Clip the triangle into all grid cells it touches. 247 | //float[] buf = new float[7*3*4]; 248 | 249 | float[] _in = new float[7*3]; 250 | float[] inrow = new float[7*3]; 251 | float[] p1 = new float[7*3]; 252 | float[] p2 = new float[7*3]; 253 | 254 | rcVcopy(_in,0 , v0, v0Start); 255 | rcVcopy(_in,1*3, v1, v1Start); 256 | rcVcopy(_in,2*3, v2, v2Start); 257 | 258 | int nvrow = 0; 259 | int nvIn = 3; 260 | 261 | for (int y = y0; y <= y1; ++y) 262 | { 263 | // Clip polygon to row. Store the remaining polygon as well 264 | float cz = bmin[2] + y*cs; 265 | dividePoly(_in, nvIn, inrow, ref nvrow, p1, ref nvIn, cz+cs, 2); 266 | //rcSwap(_in, p1); 267 | float[] tmp = _in; 268 | _in = p1; 269 | p1 = tmp; 270 | 271 | if (nvrow < 3) 272 | continue; 273 | 274 | // find the horizontal bounds in the row 275 | float minX = inrow[0], maxX = inrow[0]; 276 | for (int i=1; i inrow[i*3]) minX = inrow[i*3]; 279 | if (maxX < inrow[i*3]) maxX = inrow[i*3]; 280 | } 281 | int x0 = (int)((minX - bmin[0])*ics); 282 | int x1 = (int)((maxX - bmin[0])*ics); 283 | x0 = rcClamp(x0, 0, w-1); 284 | x1 = rcClamp(x1, 0, w-1); 285 | 286 | int nv = 0; 287 | int nv2 = nvrow; 288 | 289 | for (int x = x0; x <= x1; ++x) 290 | { 291 | // Clip polygon to column. store the remaining polygon as well 292 | float cx = bmin[0] + x*cs; 293 | dividePoly(inrow, nv2, p1, ref nv, p2, ref nv2, cx+cs, 0); 294 | //rcSwap(inrow, p2); 295 | tmp = inrow; 296 | inrow = p2; 297 | p2 = tmp; 298 | if (nv < 3) { 299 | continue; 300 | } 301 | 302 | // Calculate min and max of the span. 303 | float smin = p1[1], smax = p1[1]; 304 | for (int i = 1; i < nv; ++i) 305 | { 306 | smin = Math.Min(smin, p1[i*3+1]); 307 | smax = Math.Max(smax, p1[i*3+1]); 308 | } 309 | smin -= bmin[1]; 310 | smax -= bmin[1]; 311 | // Skip the span if it is outside the heightfield bbox 312 | if (smax < 0.0f) continue; 313 | if (smin > by) continue; 314 | // Clamp the span to the heightfield bbox. 315 | if (smin < 0.0f) smin = 0; 316 | if (smax > by) smax = by; 317 | 318 | // Snap the span to the heightfield height grid. 319 | ushort ismin = (ushort)rcClamp((int)Math.Floor(smin * ich), 0, RC_SPAN_MAX_HEIGHT); 320 | ushort ismax = (ushort)rcClamp((int)Math.Ceiling(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); 321 | 322 | addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); 323 | } 324 | } 325 | } 326 | 327 | /// @par 328 | /// 329 | /// No spans will be added if the triangle does not overlap the heightfield grid. 330 | /// 331 | /// @see rcHeightfield 332 | public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start, 333 | byte area, rcHeightfield solid, 334 | int flagMergeThr) 335 | { 336 | Debug.Assert(ctx != null, "rcContext is null"); 337 | 338 | ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 339 | 340 | float ics = 1.0f/solid.cs; 341 | float ich = 1.0f/solid.ch; 342 | rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); 343 | 344 | ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 345 | } 346 | 347 | /// @par 348 | /// 349 | /// Spans will only be added for triangles that overlap the heightfield grid. 350 | /// 351 | /// @see rcHeightfield 352 | public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv, 353 | int[] tris, byte[] areas, int nt, 354 | rcHeightfield solid, int flagMergeThr) 355 | { 356 | Debug.Assert(ctx != null, "rcContext is null"); 357 | 358 | ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 359 | 360 | float ics = 1.0f/solid.cs; 361 | float ich = 1.0f/solid.ch; 362 | // Rasterize triangles. 363 | for (int i = 0; i < nt; ++i) 364 | { 365 | int v0Start = tris[i*3+0]*3; 366 | int v1Start = tris[i*3+1]*3; 367 | int v2Start = tris[i*3+2]*3; 368 | // Rasterize. 369 | rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); 370 | } 371 | 372 | ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 373 | } 374 | 375 | /// @par 376 | /// 377 | /// Spans will only be added for triangles that overlap the heightfield grid. 378 | /// 379 | /// @see rcHeightfield 380 | public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv, 381 | ushort[] tris, byte[] areas, int nt, 382 | rcHeightfield solid, int flagMergeThr) 383 | { 384 | Debug.Assert(ctx != null, "rcContext is null"); 385 | 386 | ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 387 | 388 | float ics = 1.0f/solid.cs; 389 | float ich = 1.0f/solid.ch; 390 | // Rasterize triangles. 391 | for (int i = 0; i < nt; ++i) 392 | { 393 | int v0Start = tris[i*3+0]*3; 394 | int v1Start = tris[i*3+1]*3; 395 | int v2Start = tris[i*3+2]*3; 396 | 397 | // Rasterize. 398 | rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); 399 | } 400 | 401 | ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 402 | } 403 | 404 | /// @par 405 | /// 406 | /// Spans will only be added for triangles that overlap the heightfield grid. 407 | /// 408 | /// @see rcHeightfield 409 | public static void rcRasterizeTriangles(rcContext ctx, float[] verts, byte[] areas, int nt, 410 | rcHeightfield solid, int flagMergeThr) 411 | { 412 | Debug.Assert(ctx != null, "rcContext is null"); 413 | 414 | ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 415 | 416 | float ics = 1.0f/solid.cs; 417 | float ich = 1.0f/solid.ch; 418 | // Rasterize triangles. 419 | for (int i = 0; i < nt; ++i) 420 | { 421 | int v0Start = (i*3+0)*3; 422 | int v1Start = (i*3+1)*3; 423 | int v2Start = (i*3+2)*3; 424 | // Rasterize. 425 | rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); 426 | } 427 | 428 | ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES); 429 | } 430 | } -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastRasterization.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c3fb35ef0204f78459e5756ac0bc3d4b 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rcdtcs/Recast/RecastRegion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83ce4581a3022524784f7127e89435a1 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 632cec901b70bdb4383c65217b6cdba3 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4467dd036445ff34f8e5a2db3ee7ad4b 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/cour.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Font/cour.ttf -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/cour.ttf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e055b911acd045845b0055075fbfa299 3 | TrueTypeFontImporter: 4 | serializedVersion: 2 5 | fontSize: 16 6 | forceTextureCase: -2 7 | characterSpacing: 1 8 | characterPadding: 0 9 | includeFontData: 1 10 | use2xBehaviour: 0 11 | fontNames: [] 12 | customCharacters: 13 | fontRenderingMode: 0 14 | userData: 15 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/courbd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Font/courbd.ttf -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/courbd.ttf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ae8490697ee8bd46a3e4b57a33f3ba0 3 | TrueTypeFontImporter: 4 | serializedVersion: 2 5 | fontSize: 16 6 | forceTextureCase: -2 7 | characterSpacing: 1 8 | characterPadding: 0 9 | includeFontData: 1 10 | use2xBehaviour: 0 11 | fontNames: [] 12 | customCharacters: 13 | fontRenderingMode: 0 14 | userData: 15 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/courbi.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Font/courbi.ttf -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/courbi.ttf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec4fb7947672d0e41b02a4458813519f 3 | TrueTypeFontImporter: 4 | serializedVersion: 2 5 | fontSize: 16 6 | forceTextureCase: -2 7 | characterSpacing: 1 8 | characterPadding: 0 9 | includeFontData: 1 10 | use2xBehaviour: 0 11 | fontNames: [] 12 | customCharacters: 13 | fontRenderingMode: 0 14 | userData: 15 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/couri.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Font/couri.ttf -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Font/couri.ttf.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5fac190e31159be489b0f6baf66f36cc 3 | TrueTypeFontImporter: 4 | serializedVersion: 2 5 | fontSize: 16 6 | forceTextureCase: -2 7 | characterSpacing: 1 8 | characterPadding: 0 9 | includeFontData: 1 10 | use2xBehaviour: 0 11 | fontNames: [] 12 | customCharacters: 13 | fontRenderingMode: 0 14 | userData: 15 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1073159cb51a8a24bb21bf14946a4230 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Materials/DefaultMaterial.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Materials/DefaultMaterial.mat -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Materials/DefaultMaterial.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7121719c601814803bd03cc7e4dd9ca2 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Materials/VertexColor.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Materials/VertexColor.mat -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Materials/VertexColor.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fbc661430acd38947829e2ec65ca9fee 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3d04f68959a60384ea0ec7026da2ff70 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3694513c4af04144a0bf0c5eb761a99 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/CubeMat.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Meshes/Materials/CubeMat.mat -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/CubeMat.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e04dec677632714bb3358ee51afbab8 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/GenericMat.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Meshes/Materials/GenericMat.mat -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/GenericMat.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d01f17505a04764dabf000b5f451b43 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/defaultMat.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/Meshes/Materials/defaultMat.mat -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/Materials/defaultMat.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 836c807ee1ede82458cb7623f096994a 3 | NativeFormatImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/dungeon.obj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f52c0f792f9196448b13a8e0ab8d7f89 3 | ModelImporter: 4 | serializedVersion: 15 5 | fileIDToRecycleName: 6 | 100000: default 7 | 100002: //RootNode 8 | 400000: default 9 | 400002: //RootNode 10 | 2300000: default 11 | 3300000: default 12 | 4300000: default 13 | 9500000: //RootNode 14 | materials: 15 | importMaterials: 1 16 | materialName: 0 17 | materialSearch: 1 18 | animations: 19 | legacyGenerateAnimations: 4 20 | bakeSimulation: 0 21 | optimizeGameObjects: 0 22 | animationCompression: 1 23 | animationRotationError: .5 24 | animationPositionError: .5 25 | animationScaleError: .5 26 | animationWrapMode: 0 27 | extraExposedTransformPaths: [] 28 | clipAnimations: [] 29 | isReadable: 1 30 | meshes: 31 | lODScreenPercentages: [] 32 | globalScale: 1 33 | meshCompression: 0 34 | addColliders: 0 35 | importBlendShapes: 1 36 | swapUVChannels: 0 37 | generateSecondaryUV: 0 38 | useFileUnits: 1 39 | optimizeMeshForGPU: 1 40 | weldVertices: 1 41 | secondaryUVAngleDistortion: 8 42 | secondaryUVAreaDistortion: 15.000001 43 | secondaryUVHardAngle: 88 44 | secondaryUVPackMargin: 4 45 | tangentSpace: 46 | normalSmoothAngle: 60 47 | splitTangentsAcrossUV: 1 48 | normalImportMode: 0 49 | tangentImportMode: 1 50 | importAnimation: 1 51 | copyAvatar: 0 52 | humanDescription: 53 | human: [] 54 | skeleton: [] 55 | armTwist: .5 56 | foreArmTwist: .5 57 | upperLegTwist: .5 58 | legTwist: .5 59 | armStretch: .0500000007 60 | legStretch: .0500000007 61 | feetSpacing: 0 62 | rootMotionBoneName: 63 | lastHumanDescriptionAvatarSource: {instanceID: 0} 64 | animationType: 2 65 | userData: 66 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Meshes/nav_test.obj.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f87041290526ac4eafd96606fd70a94 3 | ModelImporter: 4 | serializedVersion: 15 5 | fileIDToRecycleName: 6 | 100000: Cube 7 | 100002: Generic 8 | 100004: //RootNode 9 | 400000: Cube 10 | 400002: Generic 11 | 400004: //RootNode 12 | 2300000: Cube 13 | 2300002: Generic 14 | 3300000: Cube 15 | 3300002: Generic 16 | 4300000: Cube 17 | 4300002: Generic 18 | 9500000: //RootNode 19 | materials: 20 | importMaterials: 1 21 | materialName: 0 22 | materialSearch: 1 23 | animations: 24 | legacyGenerateAnimations: 4 25 | bakeSimulation: 0 26 | optimizeGameObjects: 0 27 | animationCompression: 1 28 | animationRotationError: .5 29 | animationPositionError: .5 30 | animationScaleError: .5 31 | animationWrapMode: 0 32 | extraExposedTransformPaths: [] 33 | clipAnimations: [] 34 | isReadable: 1 35 | meshes: 36 | lODScreenPercentages: [] 37 | globalScale: 1 38 | meshCompression: 0 39 | addColliders: 0 40 | importBlendShapes: 1 41 | swapUVChannels: 0 42 | generateSecondaryUV: 0 43 | useFileUnits: 1 44 | optimizeMeshForGPU: 1 45 | weldVertices: 1 46 | secondaryUVAngleDistortion: 8 47 | secondaryUVAreaDistortion: 15.000001 48 | secondaryUVHardAngle: 88 49 | secondaryUVPackMargin: 4 50 | tangentSpace: 51 | normalSmoothAngle: 60 52 | splitTangentsAcrossUV: 1 53 | normalImportMode: 0 54 | tangentImportMode: 1 55 | importAnimation: 1 56 | copyAvatar: 0 57 | humanDescription: 58 | human: [] 59 | skeleton: [] 60 | armTwist: .5 61 | foreArmTwist: .5 62 | upperLegTwist: .5 63 | legTwist: .5 64 | armStretch: .0500000007 65 | legStretch: .0500000007 66 | feetSpacing: 0 67 | rootMotionBoneName: 68 | lastHumanDescriptionAvatarSource: {instanceID: 0} 69 | animationType: 2 70 | userData: 71 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/RcdtcsUnityDemo.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/Assets/RcdtcsUnityDemo/RcdtcsUnityDemo.unity -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/RcdtcsUnityDemo.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6bbf4a6545b95bf4a84906085d17b335 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ca60d1bc561660643918bbd84a154938 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfe5dc001407fae44962b9a36b138e10 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/RcdtSampleCamera.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | //Basic sample camera controls 4 | public class RcdtSampleCamera : MonoBehaviour { 5 | float m_YAngle = 0.0f; 6 | float m_XAngle = 0.0f; 7 | 8 | void Start () { 9 | m_YAngle = transform.rotation.eulerAngles.y; 10 | m_XAngle = - transform.rotation.eulerAngles.x; 11 | } 12 | 13 | void Update () { 14 | if (Input.GetButton("Camera Rotate")){ 15 | float sensitivity = 8.0f; 16 | 17 | m_YAngle += Input.GetAxis("Mouse X") * sensitivity; 18 | m_XAngle += Input.GetAxis("Mouse Y") * sensitivity; 19 | 20 | m_XAngle = Mathf.Clamp(m_XAngle, -80.0f, 80.0f); 21 | 22 | Quaternion xQuaternion = Quaternion.AngleAxis(m_XAngle, Vector3.left); 23 | Quaternion yQuaternion = Quaternion.AngleAxis(m_YAngle, Vector3.up); 24 | 25 | transform.rotation = Quaternion.identity * yQuaternion * xQuaternion; 26 | } 27 | 28 | float h = Input.GetAxis("Horizontal"); 29 | float v = Input.GetAxis("Vertical"); 30 | 31 | if (Mathf.Abs(v) > Mathf.Epsilon){ 32 | transform.localPosition += transform.forward * Time.deltaTime * 8.0f * v; 33 | } 34 | if (Mathf.Abs(h) > Mathf.Epsilon){ 35 | transform.localPosition += transform.right * Time.deltaTime * - 8.0f * - h; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/RcdtSampleCamera.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 677e464208634d04387dee3a840c09c2 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/RcdtcsSampleSoloMeshComponent.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | using UnityEngine.EventSystems; 4 | using System.IO; 5 | using System.Threading; 6 | 7 | //All-in one sample component that demonstrates all tested Rcdtcs features. 8 | public class RcdtcsSampleSoloMeshComponent : MonoBehaviour { 9 | 10 | //Public settings managed by UI 11 | //Parameters to use for the rc/dt navmesh computing 12 | public RcdtcsUnityUtils.RecastMeshParams m_NavMeshParams = new RcdtcsUnityUtils.RecastMeshParams(); 13 | //Set to true to ask for navmesh computing, will revert to false when call is made 14 | public bool m_dbg_ComputeMesh = true; 15 | //Wether navmesh computing should be done in a separate thread or in a blocking call 16 | public bool m_Threaded = true; 17 | 18 | //Debug/Testing Rendering 19 | public enum PathType { Straight, Smooth }; 20 | 21 | public PathType m_PathType = PathType.Smooth; 22 | private PathType m_ComputedPathType = PathType.Smooth; 23 | 24 | public enum DebugView { None, PolyMesh, NavMesh, DetailMesh, ContourSet, RawContourSet }; 25 | public DebugView m_DebugView = DebugView.None; 26 | private DebugView m_CurrentDebugView = DebugView.None; 27 | 28 | //Debug Rendering Material (expects a shader that supports vertex colors) 29 | public Material m_DebugRenderMeshMaterial = null; 30 | 31 | //Dynamic UI elements 32 | //A Text element using a world space camera, with settings enabling 1:1 world positioning 33 | public Text m_3DTextTemplate = null; 34 | //Displays the build time next to the build button 35 | public Text m_BuildTimeUIText = null; 36 | //The log window's text 37 | public Text m_LogUIText = null; 38 | //The top level element of the log window to hide/show 39 | public RectTransform m_LogWindowRoot = null; 40 | 41 | public RectTransform[] m_RaycastBlockingUI; 42 | 43 | //Private variables 44 | private bool m_ShowLog = false; 45 | 46 | //Computing thread when Threaded is true 47 | private Thread m_Thread = null; 48 | //A volatile function accessed by both thread to communicate on task completion 49 | private volatile bool m_ThreadLock = false; 50 | //An internal watch variable to catch threadLock freeing 51 | private bool m_ThreadWatch = false; 52 | 53 | //Self contained nav mesh interface 54 | private RcdtcsUnityUtils.SystemHelper m_System = new RcdtcsUnityUtils.SystemHelper(); 55 | 56 | //Helper classes for path finding, works with a SystemHelper 57 | private RcdtcsUnityUtils.StraightPath m_StraightPath = null; 58 | private RcdtcsUnityUtils.SmoothPath m_SmoothPath = null; 59 | 60 | //Helper classes to display debug geometries in the game world 61 | private DbgRenderMesh m_DbgRenderMesh = new DbgRenderMesh(); 62 | private DbgRenderMesh m_DbgPathRenderer = new DbgRenderMesh(); 63 | 64 | //Path finding test picking positions, Changed to public for testing 65 | [HideInInspector] public Vector3 m_StartPos = Vector3.zero; 66 | [HideInInspector] public Vector3 m_EndPos = Vector3.zero; 67 | 68 | public void Start(){ 69 | 70 | 71 | if (m_DebugRenderMeshMaterial == null) { 72 | Shader vertexColor = Shader.Find("Custom/VertexColorShader"); 73 | m_DebugRenderMeshMaterial = new Material(vertexColor); 74 | } 75 | 76 | m_DbgRenderMesh.CreateGameObject("Dbg Render Mesh",m_DebugRenderMeshMaterial); 77 | m_DbgPathRenderer.CreateGameObject("Dbg Path Renderer Mesh",m_DebugRenderMeshMaterial); 78 | 79 | m_DbgRenderMesh.SetLabelTemplate(m_3DTextTemplate); 80 | m_DbgPathRenderer.SetLabelTemplate(m_3DTextTemplate); 81 | } 82 | 83 | public bool ThreadIsBusy(){ 84 | return m_ThreadWatch; 85 | } 86 | 87 | private void ComputeSystemThread() { 88 | m_System.ComputeSystem(); 89 | m_ThreadLock = false; 90 | } 91 | 92 | public void OnComputeComplete(){ 93 | if (m_BuildTimeUIText != null) { 94 | m_BuildTimeUIText.text = m_System.m_ctx.getAccumulatedTime(Recast.rcTimerLabel.RC_TIMER_TOTAL) + " ms"; 95 | } 96 | RecomputePath(); 97 | //Force refresh debug renderer by invalidating watch 98 | m_CurrentDebugView = DebugView.None; 99 | } 100 | 101 | public void RecomputeSystem() { 102 | 103 | if (ThreadIsBusy()){ 104 | return; 105 | } 106 | 107 | m_System.SetNavMeshParams(m_NavMeshParams); 108 | m_System.ClearComputedData(); 109 | m_System.ClearMesh(); 110 | 111 | MeshFilter mf = GetComponent(); 112 | if (mf == null) { 113 | return; 114 | } 115 | 116 | Mesh mesh = mf.mesh; 117 | if (mesh == null) { 118 | return; 119 | } 120 | 121 | m_System.AddMesh(mesh, transform.gameObject); 122 | 123 | if (m_Threaded){ 124 | m_ThreadWatch = true; 125 | m_ThreadLock = true; 126 | m_Thread = new Thread(ComputeSystemThread); 127 | m_Thread.Start(); 128 | }else{ 129 | m_System.ComputeSystem(); 130 | OnComputeComplete(); 131 | } 132 | 133 | } 134 | 135 | public void UpdateLog() { 136 | if (!m_ShowLog) { 137 | return; 138 | } 139 | 140 | if (ThreadIsBusy()) { 141 | return; 142 | } 143 | 144 | if (m_System != null && m_System.m_ctx != null && m_System.m_ctx.m_Dirty && m_LogUIText != null) { 145 | m_LogUIText.text = m_System.m_ctx.dumpLog(); 146 | m_System.m_ctx.m_Dirty = false; 147 | 148 | #if UNITY_STANDALONE 149 | string filename = Application.persistentDataPath + "/log.txt"; 150 | StreamWriter stream = File.CreateText(filename); 151 | if (stream != null) { 152 | string fullLog = m_System.GetNavMeshParams().ToString() + "\n" + m_LogUIText.text; 153 | stream.Write(fullLog); 154 | stream.Close(); 155 | Debug.Log("Log output saved " + filename); 156 | } 157 | #endif 158 | } 159 | 160 | } 161 | 162 | public void Update(){ 163 | UpdateThreadWatch(); 164 | 165 | if (ThreadIsBusy()){ 166 | return; 167 | } 168 | 169 | SystemUpdate(); 170 | DebugViewUpdate(); 171 | PathDemoUpdate(); 172 | UpdateLog(); 173 | } 174 | 175 | public void UpdateThreadWatch(){ 176 | //Sync with compute thread by reading m_ThreadLock, to see if it's done 177 | if (m_ThreadWatch && !m_ThreadLock) { 178 | OnComputeComplete(); 179 | m_ThreadWatch = false; 180 | } 181 | } 182 | 183 | public void SystemUpdate() { 184 | if (m_dbg_ComputeMesh) { 185 | RecomputeSystem(); 186 | m_dbg_ComputeMesh = false; 187 | } 188 | } 189 | 190 | public void DebugViewUpdate() { 191 | if (m_CurrentDebugView != m_DebugView) { 192 | m_CurrentDebugView = m_DebugView; 193 | m_DbgRenderMesh.Clear(); 194 | 195 | switch (m_CurrentDebugView) { 196 | case DebugView.PolyMesh: 197 | RcdtcsUnityUtils.ShowRecastNavmesh(m_DbgRenderMesh, m_System.m_pmesh, m_System.m_cfg); 198 | break; 199 | case DebugView.NavMesh: 200 | RcdtcsUnityUtils.ShowTilePolyDetails(m_DbgRenderMesh, m_System.m_navMesh, 0); 201 | break; 202 | case DebugView.DetailMesh: 203 | RcdtcsUnityUtils.ShowRecastDetailMesh(m_DbgRenderMesh, m_System.m_dmesh); 204 | break; 205 | case DebugView.None: 206 | m_DbgRenderMesh.Rebuild(); 207 | break; 208 | case DebugView.ContourSet: 209 | RcdtcsUnityUtils.ShowContours(m_DbgRenderMesh, m_System.m_cset); 210 | break; 211 | case DebugView.RawContourSet: 212 | RcdtcsUnityUtils.ShowRawContours(m_DbgRenderMesh, m_System.m_cset); 213 | break; 214 | } 215 | } 216 | } 217 | 218 | 219 | 220 | public void PathDemoUpdate() { 221 | bool mainClick = Input.GetButtonDown("Place Start Point"); //Input.GetMouseButtonDown(0); 222 | bool altClick = Input.GetButtonDown("Place End Point"); 223 | if (mainClick || altClick){ 224 | 225 | //Don't raycast through new UI stuff 226 | if (EventSystem.current.IsPointerOverGameObject()){ 227 | return; 228 | } 229 | 230 | Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); 231 | RaycastHit hit; 232 | Debug.DrawRay(ray.origin, ray.direction * 1000.0f, Color.red, 5.0f); 233 | if (Physics.Raycast (ray.origin, ray.direction, out hit, 1000.0f)) { 234 | if (altClick){ 235 | m_EndPos = hit.point; 236 | Debug.Log ("Set End Point " + hit.point); 237 | }else if (mainClick){ 238 | m_StartPos = hit.point; 239 | Debug.Log ("Set Start Point " + hit.point); 240 | } 241 | RecomputePath(); 242 | } 243 | } 244 | if (m_PathType != m_ComputedPathType) { 245 | RecomputePath(); 246 | } 247 | } 248 | 249 | // changed to public for testing 250 | public void RecomputePath(){ 251 | if (m_EndPos != Vector3.zero && m_StartPos != Vector3.zero){ 252 | m_DbgPathRenderer.Clear(); 253 | m_ComputedPathType = m_PathType; 254 | if (m_PathType == PathType.Smooth){ 255 | m_SmoothPath = RcdtcsUnityUtils.ComputeSmoothPath(m_System.m_navQuery, m_StartPos, m_EndPos); 256 | m_DbgPathRenderer.AddPath(m_SmoothPath.m_smoothPath, m_SmoothPath.m_nsmoothPath, 1.25f, Color.black, Color.white); 257 | }else if (m_PathType == PathType.Straight){ 258 | m_StraightPath = RcdtcsUnityUtils.ComputeStraightPath(m_System.m_navQuery, m_StartPos, m_EndPos); 259 | m_DbgPathRenderer.AddPath(m_StraightPath.m_straightPath, m_StraightPath.m_straightPathCount, 1.25f, Color.black, Color.white); 260 | } 261 | m_DbgPathRenderer.Rebuild(); 262 | } 263 | } 264 | 265 | //UI interface 266 | private void UI_SetDebugView(DebugView debugView) { 267 | m_DebugView = debugView; 268 | } 269 | public void UI_ShowNavMesh(bool toggle) { 270 | if (toggle) 271 | UI_SetDebugView(DebugView.NavMesh); 272 | } 273 | public void UI_ShowPolyMesh(bool toggle) { 274 | if (toggle) 275 | UI_SetDebugView(DebugView.PolyMesh); 276 | } 277 | public void UI_ShowDetailMesh(bool toggle) { 278 | if (toggle) 279 | UI_SetDebugView(DebugView.DetailMesh); 280 | } 281 | public void UI_ShowContours(bool toggle) { 282 | if (toggle) 283 | UI_SetDebugView(DebugView.ContourSet); 284 | } 285 | public void UI_ShowRawContours(bool toggle) { 286 | if (toggle) 287 | UI_SetDebugView(DebugView.RawContourSet); 288 | } 289 | public void UI_ShowNone(bool toggle) { 290 | if (toggle) 291 | UI_SetDebugView(DebugView.None); 292 | } 293 | 294 | private void UI_SetPath(PathType pathType) { 295 | m_PathType = pathType; 296 | } 297 | public void UI_UseSmoothPath(bool toggle) { 298 | if (toggle) 299 | UI_SetPath(PathType.Smooth); 300 | } 301 | public void UI_UseStraightPath(bool toggle) { 302 | if (toggle) 303 | UI_SetPath(PathType.Straight); 304 | } 305 | 306 | public void UI_Params_SetCellSize(float sliderValue){ 307 | m_NavMeshParams.m_cellSize = sliderValue; 308 | } 309 | public void UI_Params_SetCellHeight(float sliderValue){ 310 | m_NavMeshParams.m_cellHeight = sliderValue; 311 | } 312 | public void UI_Params_SetAgentHeight(float sliderValue){ 313 | m_NavMeshParams.m_agentHeight = sliderValue; 314 | } 315 | public void UI_Params_SetAgentRadius(float sliderValue) { 316 | m_NavMeshParams.m_agentRadius = sliderValue; 317 | } 318 | public void UI_Params_SetAgentMaxClimb(float sliderValue){ 319 | m_NavMeshParams.m_agentMaxClimb = sliderValue; 320 | } 321 | public void UI_Params_SetAgentMaxSlope(float sliderValue) { 322 | m_NavMeshParams.m_agentMaxSlope = sliderValue; 323 | } 324 | 325 | public void UI_Params_SetRegionMinSize(float sliderValue) { 326 | m_NavMeshParams.m_regionMinSize = sliderValue; 327 | } 328 | public void UI_Params_SetRegionMergeSize(float sliderValue) { 329 | m_NavMeshParams.m_regionMergeSize = sliderValue; 330 | } 331 | 332 | public void UP_Params_SetMonotonePartitioning(bool toggleValue) { 333 | m_NavMeshParams.m_monotonePartitioning = toggleValue; 334 | } 335 | 336 | public void UI_Params_SetEdgeMaxLen(float sliderValue) { 337 | m_NavMeshParams.m_edgeMaxLen = sliderValue; 338 | } 339 | public void UI_Params_SetEdgeMaxError(float sliderValue) { 340 | m_NavMeshParams.m_edgeMaxError = sliderValue; 341 | } 342 | 343 | public void UI_Params_SetVertsPerPoly(float sliderValue) { 344 | m_NavMeshParams.m_vertsPerPoly = sliderValue; 345 | } 346 | public void UI_Params_SetDetailSampleDist(float sliderValue) { 347 | m_NavMeshParams.m_detailSampleDist = sliderValue; 348 | } 349 | public void UI_Params_SetSampleMaxError(float sliderValue) { 350 | m_NavMeshParams.m_detailSampleMaxError = sliderValue; 351 | } 352 | 353 | public void UI_SetThreaded(bool threaded){ 354 | if (threaded){ 355 | m_Threaded = true; 356 | } 357 | } 358 | public void UI_SetBlocking(bool blocking){ 359 | if (blocking){ 360 | m_Threaded = false; 361 | } 362 | } 363 | 364 | public void UI_RebuildSystem() { 365 | RecomputeSystem(); 366 | } 367 | 368 | public void UI_ShowLog(bool log) { 369 | m_ShowLog = log; 370 | if (m_ShowLog) { 371 | UpdateLog(); 372 | } 373 | if (m_LogWindowRoot != null) { 374 | m_LogWindowRoot.gameObject.SetActive(log); 375 | } 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/RcdtcsSampleSoloMeshComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38fcc500fcec84e16a55c714ba261f15 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/TextSetFloat.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | 4 | //Automatic function for a slider to control text 5 | public class TextSetFloat : MonoBehaviour { 6 | 7 | private Text m_Text; 8 | public bool m_AsInt; 9 | 10 | void Start () { 11 | m_Text = GetComponent(); 12 | Slider slider = GetComponentInParent(); 13 | if (slider != null) { 14 | Set(slider.value); 15 | } 16 | } 17 | 18 | public void Set(float val){ 19 | if (m_Text != null){ 20 | if (m_AsInt){ 21 | m_Text.text = ((int) val).ToString(); 22 | }else{ 23 | m_Text.text = val.ToString("0.00"); 24 | } 25 | 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Sample/TextSetFloat.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c8e2fc2502d4132459255d4dd7f67d2a 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa08a771ef0cd8e4a88327a71d5489b1 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/BuildContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Text; 4 | 5 | //Example implementation of rcContext 6 | public partial class Recast{ 7 | 8 | /// Recast build context. 9 | public class BuildContext : rcContext 10 | { 11 | private Stopwatch[] m_Stopwatches = new Stopwatch[(int)rcTimerLabel.RC_MAX_TIMERS]; 12 | 13 | public bool m_Dirty = false; 14 | 15 | private class LogMessage{ 16 | public rcLogCategory m_Category; 17 | public string m_Message; 18 | 19 | public LogMessage(rcLogCategory category, string msg){ 20 | m_Category = category; 21 | m_Message = msg; 22 | } 23 | } 24 | 25 | const int MAX_MESSAGES = 1000; 26 | List m_Messages = new List(); 27 | 28 | public BuildContext() 29 | { 30 | for (int i = 0; i < (int)rcTimerLabel.RC_MAX_TIMERS; ++i) { 31 | m_Stopwatches[i] = new Stopwatch(); 32 | } 33 | resetTimers(); 34 | } 35 | 36 | 37 | protected override void doResetLog() 38 | { 39 | m_Messages.Clear(); 40 | } 41 | 42 | protected override void doLog(rcLogCategory category, string msg) 43 | { 44 | m_Messages.Add(new LogMessage(category, msg)); 45 | if (m_Messages.Count > MAX_MESSAGES){ 46 | m_Messages.RemoveAt(0); 47 | } 48 | 49 | m_Dirty = true; 50 | } 51 | 52 | protected override void doResetTimers() 53 | { 54 | for (int i = 0; i < (int)rcTimerLabel.RC_MAX_TIMERS; ++i) 55 | m_Stopwatches[i].Reset(); 56 | } 57 | 58 | protected override void doStartTimer(rcTimerLabel label) 59 | { 60 | m_Stopwatches[(int)label].Start(); 61 | } 62 | 63 | protected override void doStopTimer(rcTimerLabel label) 64 | { 65 | m_Stopwatches[(int)label].Stop(); 66 | } 67 | 68 | protected override long doGetAccumulatedTime(rcTimerLabel label) 69 | { 70 | return m_Stopwatches[(int)label].ElapsedMilliseconds; 71 | } 72 | 73 | protected override double doGetAccumulatedTimeHiResolution(rcTimerLabel label) { 74 | 75 | if (!Stopwatch.IsHighResolution) { 76 | return -1.0; 77 | } 78 | 79 | if (m_Stopwatches[(int)label].IsRunning) { 80 | UnityEngine.Debug.LogError("Stopwatch " + label.ToString() + " was not stopped prior to output"); 81 | } 82 | 83 | double seconds = (double)m_Stopwatches[(int)label].ElapsedTicks / (double)Stopwatch.Frequency; 84 | 85 | return seconds * 1000.0; 86 | } 87 | 88 | 89 | private void logLine(rcTimerLabel label, string name, double pc, int level = 0) 90 | { 91 | double t = getAccumulatedTimeHiResolution(label); 92 | if (t <= 0.0f) 93 | return; 94 | string raster = ""; 95 | 96 | if (level > 0) { 97 | raster += new string(' ', 4*level); 98 | } 99 | raster += name + ": "; 100 | if (raster.Length < 25) { 101 | raster += new string(' ', 25 - raster.Length); 102 | } 103 | 104 | int secondColumn = 40; 105 | if (level != 0) { 106 | raster += ""; 107 | secondColumn += 12; 108 | } 109 | raster += string.Format("{0:N2}", t) + "ms "; 110 | if (raster.Length < secondColumn) { 111 | raster += new string(' ', secondColumn - raster.Length); 112 | } 113 | raster += "(" + string.Format("{0:N1}", t * pc) + "%)"; 114 | if (level != 0) { 115 | raster += ""; 116 | } 117 | log(rcLogCategory.RC_LOG_PROGRESS, raster); 118 | 119 | m_PercentCheck += t * pc; 120 | m_SumCheck += t; 121 | } 122 | 123 | private double m_PercentCheck = 0.0; 124 | private double m_SumCheck = 0.0; 125 | public void logBuildTimes() 126 | { 127 | double totalTime = getAccumulatedTimeHiResolution(rcTimerLabel.RC_TIMER_TOTAL); 128 | double pc = 100.0 / totalTime; 129 | 130 | m_PercentCheck = 0.0; 131 | m_SumCheck = 0.0; 132 | 133 | log(rcLogCategory.RC_LOG_PROGRESS, "Build Times"); 134 | logLine(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES, "- Rasterize", pc); 135 | logLine(rcTimerLabel.RC_TIMER_BUILD_COMPACTHEIGHTFIELD, "- Build Compact", pc); 136 | logLine(rcTimerLabel.RC_TIMER_FILTER_BORDER, "- Filter Border", pc); 137 | logLine(rcTimerLabel.RC_TIMER_FILTER_WALKABLE, "- Filter Walkable", pc); 138 | logLine(rcTimerLabel.RC_TIMER_ERODE_AREA, "- Erode Area", pc); 139 | logLine(rcTimerLabel.RC_TIMER_MEDIAN_AREA, "- Median Area", pc); 140 | logLine(rcTimerLabel.RC_TIMER_MARK_BOX_AREA, "- Mark Box Area", pc); 141 | logLine(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA, "- Mark Convex Area", pc); 142 | logLine(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA, "- Mark Cylinder Area", pc); 143 | logLine(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD, "- Build Distance Field", pc); 144 | logLine(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_DIST, "- Distance", pc, 1); 145 | logLine(rcTimerLabel.RC_TIMER_BUILD_DISTANCEFIELD_BLUR, "- Blur", pc, 1); 146 | logLine(rcTimerLabel.RC_TIMER_BUILD_REGIONS, "- Build Regions", pc); 147 | logLine(rcTimerLabel.RC_TIMER_BUILD_REGIONS_WATERSHED, "- Watershed", pc, 1); 148 | logLine(rcTimerLabel.RC_TIMER_BUILD_REGIONS_EXPAND, "- Expand", pc, 2); 149 | logLine(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FLOOD, "- Find Basins", pc, 2); 150 | logLine(rcTimerLabel.RC_TIMER_BUILD_REGIONS_FILTER, "- Filter", pc, 1); 151 | logLine(rcTimerLabel.RC_TIMER_BUILD_LAYERS, "- Build Layers", pc); 152 | logLine(rcTimerLabel.RC_TIMER_BUILD_CONTOURS, "- Build Contours", pc); 153 | logLine(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE, "- Trace", pc, 1); 154 | logLine(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY, "- Simplify", pc, 1); 155 | logLine(rcTimerLabel.RC_TIMER_BUILD_POLYMESH, "- Build Polymesh", pc); 156 | logLine(rcTimerLabel.RC_TIMER_BUILD_POLYMESHDETAIL, "- Build Polymesh Detail", pc); 157 | logLine(rcTimerLabel.RC_TIMER_MERGE_POLYMESH, "- Merge Polymeshes", pc); 158 | logLine(rcTimerLabel.RC_TIMER_MERGE_POLYMESHDETAIL, "- Merge Polymesh Details", pc); 159 | log(rcLogCategory.RC_LOG_PROGRESS, "=== TOTAL: " + totalTime + " ms ==="); 160 | } 161 | 162 | public string dumpLog() 163 | { 164 | StringBuilder sb = new StringBuilder(); 165 | 166 | foreach(LogMessage msg in m_Messages){ 167 | sb.AppendLine(msg.m_Category.ToString() + ": " + msg.m_Message); 168 | } 169 | return sb.ToString(); 170 | } 171 | 172 | public int getLogCount() 173 | { 174 | return m_Messages.Count; 175 | } 176 | }; 177 | } -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/BuildContext.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 06b72287dd753804b8c370f5ca56ffb8 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/DbgRenderMesh.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections.Generic; 3 | using UnityEngine.UI; 4 | 5 | //Helper classes for procedural geometry rendering 6 | public class DbgRenderTriangle{ 7 | public Vector3[] m_Verts = new Vector3[3]; 8 | public Color[] m_Colors = new Color[3] { Color.white, Color.white, Color.white}; 9 | 10 | public DbgRenderTriangle(Vector3 a, Vector3 b, Vector3 c, Color color){ 11 | m_Verts[0] = a; 12 | m_Verts[1] = b; 13 | m_Verts[2] = c; 14 | for (int i=0;i m_Triangles = new List(); 32 | 33 | private GameObject m_GameObject = null; 34 | private MeshFilter m_MeshFilter = null; 35 | private MeshRenderer m_MeshRenderer = null; 36 | private Mesh m_Mesh = null; 37 | private Text m_LabelTemplate; 38 | private List m_Labels = new List(); 39 | 40 | private bool m_BoundsComputed = false; 41 | private Vector3 m_Min = Vector3.zero; 42 | private Vector3 m_Max = Vector3.zero; 43 | private Vector3 m_BBCenter = Vector3.zero; 44 | 45 | //Set this if you want to use labels 46 | public void SetLabelTemplate(Text text){ 47 | m_LabelTemplate = text; 48 | } 49 | 50 | public static Vector3 GetBoundingBoxCenter(List tris){ 51 | return GetBoundingBoxCenter(tris, 0, tris.Count); 52 | } 53 | 54 | public static Vector3 GetBoundingBoxCenter(List tris, int startIndex, int endIndex){ 55 | Vector3 min = Vector3.zero; 56 | Vector3 max = Vector3.zero; 57 | return GetBoundingBoxCenter(tris, startIndex, endIndex, ref min, ref max); 58 | } 59 | 60 | public static Vector3 GetBoundingBoxCenter(List tris, int firstIndex, int lastIndex, ref Vector3 min, ref Vector3 max){ 61 | min = new Vector3( float.MaxValue, float.MaxValue, float.MaxValue); 62 | max = new Vector3( float.MinValue, float.MinValue, float.MinValue); 63 | for (int i=firstIndex; i 0){ 75 | return min + (max - min) / 2.0f; 76 | } 77 | return Vector3.zero; 78 | } 79 | 80 | public Vector3 GetBoundingBoxCenter(int startIndex, int endIndex){ 81 | Vector3 min = Vector3.zero; 82 | Vector3 max = Vector3.zero; 83 | return GetBoundingBoxCenter(m_Triangles, startIndex, endIndex, ref min, ref max); 84 | } 85 | 86 | public Vector3 GetBoundingBoxTop(int startIndex, int endIndex){ 87 | Vector3 min = Vector3.zero; 88 | Vector3 max = Vector3.zero; 89 | Vector3 bbCenter = GetBoundingBoxCenter(m_Triangles, startIndex, endIndex, ref min, ref max); 90 | return new Vector3(bbCenter.x, max.y, bbCenter.z); 91 | } 92 | 93 | public int GetTriangleCount(){ 94 | return m_Triangles.Count; 95 | } 96 | 97 | private void ComputeBounds(){ 98 | m_BBCenter = GetBoundingBoxCenter(m_Triangles,0, m_Triangles.Count, ref m_Min, ref m_Max); 99 | m_BoundsComputed = true; 100 | } 101 | 102 | public void LabelBoundingBoxTop(string textString){ 103 | if (!m_BoundsComputed){ 104 | ComputeBounds(); 105 | } 106 | if (m_BoundsComputed){ 107 | Vector3 bbTop = new Vector3(m_BBCenter.x, m_Max.y, m_BBCenter.z); 108 | AddLabel(bbTop, textString); 109 | }else{ 110 | Debug.LogError("Error, could not find center of render mesh"); 111 | } 112 | } 113 | 114 | public void LabelCenter(string textString){ 115 | if (!m_BoundsComputed){ 116 | ComputeBounds(); 117 | } 118 | if (m_BoundsComputed){ 119 | AddLabel(m_BBCenter, textString); 120 | }else{ 121 | Debug.LogError("Error, could not find center of render mesh"); 122 | } 123 | } 124 | 125 | public void AddLabel(Vector3 pos, string textString){ 126 | if (m_LabelTemplate){ 127 | Text text= Object.Instantiate( m_LabelTemplate ) as Text; 128 | text.rectTransform.parent = m_LabelTemplate.transform.parent; 129 | text.rectTransform.position = pos; 130 | //text.rectTransform.anchoredPosition = ( pos /*+ Vector3.up * .15f*/ ); 131 | text.gameObject.SetActive(true); 132 | text.text = textString; 133 | m_Labels.Add(text); 134 | }else{ 135 | Debug.LogError("Error, could not find center of render mesh"); 136 | } 137 | } 138 | 139 | private void RenderTo(Mesh mesh){ 140 | mesh.Clear(); 141 | 142 | int triCount = m_Triangles.Count; 143 | 144 | Vector3[] verts = new Vector3[3 * triCount]; 145 | int[] tris = new int[3* triCount]; 146 | Color[] colors = new Color[3*triCount]; 147 | 148 | for (int i=0;i vertices, Color color){ 184 | int vertCount = vertices.Count; 185 | 186 | for (int i=2;i < vertCount;++i){ 187 | AddTriangle( new DbgRenderTriangle ( vertices[0], vertices[i - 1], vertices[i], color )); 188 | } 189 | } 190 | 191 | public void AddQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d, bool twoSided, Color color){ 192 | // A B C + A C D 193 | AddTriangle( new DbgRenderTriangle (a,b,c,color) ); 194 | AddTriangle( new DbgRenderTriangle (a,c,d,color) ); 195 | if (twoSided){ 196 | AddTriangle( new DbgRenderTriangle (b,a,d,color) ); 197 | AddTriangle( new DbgRenderTriangle (b,d,c,color) ); 198 | } 199 | } 200 | 201 | public void AddQuad(Vector3 a, Vector3 b, Vector3 c, Vector3 d, bool twoSided, Color colora, Color colorb, Color colorc, Color colord){ 202 | // A B C + A C D 203 | AddTriangle( new DbgRenderTriangle (a,b,c,colora,colorb,colorc) ); 204 | AddTriangle( new DbgRenderTriangle (a,c,d,colora,colorc,colord) ); 205 | if (twoSided){ 206 | AddTriangle( new DbgRenderTriangle (b,a,d,colorb,colora,colord) ); 207 | AddTriangle( new DbgRenderTriangle (b,d,c,colorb,colord,colorc) ); 208 | } 209 | } 210 | 211 | public void AddPath( float[] path, int vertCount, float size, Color col1, Color col2){ 212 | if (path != null && path.Length > 3){ 213 | for (int i = 1; i < vertCount;++i) { 214 | int v = i * 3; 215 | Vector3 a = new Vector3( path[v-3], path[v-2], path[v-1]); 216 | Vector3 b = new Vector3( path[v+0], path[v+1], path[v+2]); 217 | AddVerticalQuad(a,b,size,i%2==1 ? col1 : col2); 218 | } 219 | } 220 | } 221 | 222 | public void AddVerticalQuad(Vector3 start, Vector3 end, float size, Color col){ 223 | AddQuad(start + Vector3.up * size, end + Vector3.up * size, end, start, true, col); 224 | } 225 | 226 | public void AddVerticalQuad(Vector3 start, Vector3 end, float size, Color colstart, Color colend){ 227 | AddQuad(start + Vector3.up * size, end + Vector3.up * size, end, start, true, colstart, colend, colend, colstart); 228 | } 229 | 230 | public void AddPath( Vector3[] path, int vertCount, float size, Color col1, Color col2){ 231 | for (int i=1;i().material = material; 274 | return m_GameObject; 275 | } 276 | 277 | public void DestroyGameObject() { 278 | if (m_GameObject != null) { 279 | Clear(); 280 | Rebuild(); 281 | 282 | GameObject.Destroy(m_GameObject); 283 | m_MeshFilter = null; 284 | m_MeshRenderer = null; 285 | m_Mesh = null; 286 | m_GameObject = null; 287 | } 288 | } 289 | 290 | private void Setup(GameObject target){ 291 | m_GameObject = target; 292 | 293 | m_MeshFilter = target.GetComponent(); 294 | if (m_MeshFilter == null){ 295 | m_MeshFilter = target.AddComponent(); 296 | } 297 | m_MeshRenderer = target.GetComponent(); 298 | if (m_MeshRenderer == null){ 299 | m_MeshRenderer = target.AddComponent(); 300 | } 301 | m_Mesh = new Mesh(); 302 | m_MeshFilter.mesh = m_Mesh; 303 | m_BoundsComputed = false; 304 | } 305 | } 306 | 307 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/DbgRenderMesh.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cbf4f64f0586072489130d534517d8b0 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/RcdtcsDebugRenderUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | //Various functions to render computed Recast/Detour data 4 | public static partial class RcdtcsUnityUtils{ 5 | 6 | public static float VaryColorComponent(float comp) { 7 | float smallDelta = 1.0f / 20.0f; 8 | if (comp > 0.5f) { 9 | smallDelta = -smallDelta; 10 | } 11 | return smallDelta + comp; 12 | } 13 | 14 | public static Color VaryColor(Color color) { 15 | return new Color( 16 | VaryColorComponent(color.r), 17 | VaryColorComponent(color.g), 18 | VaryColorComponent(color.b)); 19 | } 20 | 21 | private const int c_RandomSeed = 9; 22 | 23 | public static void ShowRecastNavmesh(DbgRenderMesh renderMesh, Recast.rcPolyMesh pmesh, Recast.rcConfig config){ 24 | 25 | renderMesh.Clear(); 26 | 27 | UnityEngine.Random.seed = c_RandomSeed; 28 | 29 | int npolys = pmesh.npolys; 30 | int nvp = pmesh.nvp; 31 | int[] tri = new int[3]; 32 | Vector3[] verts = new Vector3[3]; 33 | for (int i = 0; i < npolys; ++i) 34 | { 35 | int pIndex = i * nvp * 2; 36 | Color col = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); 37 | for (int j = 2; j < nvp; ++j) 38 | { 39 | if ( pmesh.polys[pIndex + j] == Recast.RC_MESH_NULL_IDX) 40 | break; 41 | tri[0] = pmesh.polys[pIndex]; 42 | tri[1] = pmesh.polys[pIndex + j - 1]; 43 | tri[2] = pmesh.polys[pIndex + j]; 44 | 45 | for (int k=0;k<3;++k){ 46 | int vIndex = tri[k]*3; 47 | verts[k].x = config.bmin[0] + pmesh.verts[vIndex + 0]*pmesh.cs; 48 | verts[k].y = config.bmin[1] + (pmesh.verts[vIndex + 1]+1)*pmesh.ch + 0.1f; 49 | verts[k].z = config.bmin[2] + pmesh.verts[vIndex + 2]*pmesh.cs; 50 | } 51 | col = VaryColor(col); 52 | renderMesh.AddTriangle(new DbgRenderTriangle(verts[0], verts[1], verts[2], col)); 53 | } 54 | } 55 | renderMesh.Rebuild(); 56 | } 57 | 58 | public static void ShowRecastDetailMesh(DbgRenderMesh renderMesh, Recast.rcPolyMeshDetail dmesh ){ 59 | 60 | renderMesh.Clear(); 61 | 62 | UnityEngine.Random.seed = c_RandomSeed; 63 | 64 | int nmeshes = dmesh.nmeshes; 65 | for (int i = 0; i < nmeshes; ++i) { 66 | 67 | Color col = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); 68 | 69 | uint bverts = dmesh.meshes[i*4]; 70 | uint btris = dmesh.meshes[i*4 + 2]; 71 | uint ntris = dmesh.meshes[i*4 + 3];; 72 | uint trisStart = btris*4; 73 | for (uint j = 0; j < ntris; ++j) 74 | { 75 | Vector3[] verts = new Vector3[3]; 76 | for (int k=0;k<3;++k){ 77 | int vertStart = (int) (bverts+ dmesh.tris[trisStart + j*4 + k ]) * 3 ; 78 | verts[k].x = dmesh.verts[vertStart + 0]; 79 | verts[k].y = dmesh.verts[vertStart + 1]; 80 | verts[k].z = dmesh.verts[vertStart + 2]; 81 | } 82 | col = VaryColor(col); 83 | renderMesh.AddTriangle(new DbgRenderTriangle( 84 | verts[0] 85 | , verts[1] 86 | , verts[2] 87 | , col)); 88 | } 89 | } 90 | renderMesh.Rebuild(); 91 | } 92 | 93 | public static void ShowRawContours(DbgRenderMesh renderMesh, Recast.rcContourSet cset){ 94 | 95 | 96 | renderMesh.Clear(); 97 | 98 | UnityEngine.Random.seed = c_RandomSeed; 99 | 100 | float[] orig = cset.bmin; 101 | float cs = cset.cs; 102 | float ch = cset.ch; 103 | 104 | for (int i = 0; i < cset.nconts; ++i) 105 | { 106 | Recast.rcContour c = cset.conts[i]; 107 | 108 | if (c.nrverts == 0){ 109 | continue; 110 | } 111 | 112 | Color col = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); 113 | 114 | int triStartIndex = renderMesh.GetTriangleCount(); 115 | 116 | Vector3 start = new Vector3(orig[0] + c.rverts[0]*cs, 117 | orig[1] + (c.rverts[1]+1+(i&1))*ch, 118 | orig[2] + c.rverts[2]*cs); 119 | Vector3 a = Vector3.zero; 120 | Vector3 b = start; 121 | for (int j = 1; j < c.nrverts; ++j) 122 | { 123 | a = b; 124 | int vStart = j*4; 125 | b = new Vector3(orig[0] + c.rverts[vStart + 0]*cs, 126 | orig[1] + (c.rverts[vStart + 1]+1+(i&1))*ch, 127 | orig[2] + c.rverts[vStart + 2]*cs); 128 | if (j > 0){ 129 | renderMesh.AddVerticalQuad(a, b, 0.5f, col); 130 | } 131 | } 132 | // Loop last segment. 133 | renderMesh.AddVerticalQuad(b, start, 0.5f, col); 134 | 135 | int triEndIndex = renderMesh.GetTriangleCount(); 136 | 137 | Vector3 labelPos = renderMesh.GetBoundingBoxTop(triStartIndex, triEndIndex); 138 | 139 | renderMesh.AddLabel(labelPos, "contour " + i + "\nReg: " + c.reg + "\nnrverts:" + c.nrverts + "\nnverts:" + c.nverts); 140 | } 141 | /* 142 | dd->end(); 143 | 144 | dd->begin(DU_DRAW_POINTS, 2.0f); 145 | 146 | for (int i = 0; i < cset.nconts; ++i) 147 | { 148 | const rcContour& c = cset.conts[i]; 149 | unsigned int color = duDarkenCol(duIntToCol(c.reg, a)); 150 | 151 | for (int j = 0; j < c.nrverts; ++j) 152 | { 153 | const int* v = &c.rverts[j*4]; 154 | float off = 0; 155 | unsigned int colv = color; 156 | if (v[3] & RC_BORDER_VERTEX) 157 | { 158 | colv = duRGBA(255,255,255,a); 159 | off = ch*2; 160 | } 161 | 162 | float fx = orig[0] + v[0]*cs; 163 | float fy = orig[1] + (v[1]+1+(i&1))*ch + off; 164 | float fz = orig[2] + v[2]*cs; 165 | dd->vertex(fx,fy,fz, colv); 166 | } 167 | } 168 | dd->end(); 169 | */ 170 | renderMesh.Rebuild(); 171 | } 172 | 173 | public static void ShowContours(DbgRenderMesh renderMesh, Recast.rcContourSet cset) 174 | { 175 | renderMesh.Clear(); 176 | 177 | UnityEngine.Random.seed = c_RandomSeed; 178 | 179 | float[] orig = cset.bmin; 180 | float cs = cset.cs; 181 | float ch = cset.ch; 182 | 183 | for (int i = 0; i < cset.nconts; ++i) 184 | { 185 | Recast.rcContour c = cset.conts[i]; 186 | if (c.nverts == 0) 187 | continue; 188 | 189 | Color col = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); 190 | Color bcol = VaryColor(col); 191 | 192 | for (int j = 0, k = c.nverts-1; j < c.nverts; k=j++) 193 | { 194 | int vaStart = k*4; 195 | int vbStart = j*4; 196 | Color segCol = ((c.verts[vaStart + 3] & Recast.RC_AREA_BORDER) != 0) ? bcol : col; 197 | 198 | Vector3 start = new Vector3(orig[0] + c.verts[vaStart + 0]*cs, 199 | orig[1] + (c.verts[vaStart + 1]+1+(i&1))*ch, 200 | orig[2] + c.verts[vaStart + 2]*cs); 201 | Vector3 end = new Vector3(orig[0] + c.verts[vbStart + 0]*cs, 202 | orig[1] + (c.verts[vbStart + 1]+1+(i&1))*ch, 203 | orig[2] + c.verts[vbStart + 2]*cs); 204 | 205 | renderMesh.AddVerticalQuad(start, end, 0.5f, segCol); 206 | } 207 | } 208 | 209 | /* 210 | dd->begin(DU_DRAW_POINTS, 3.0f); 211 | 212 | for (int i = 0; i < cset.nconts; ++i) 213 | { 214 | const rcContour& c = cset.conts[i]; 215 | unsigned int color = duDarkenCol(duIntToCol(c.reg, a)); 216 | for (int j = 0; j < c.nverts; ++j) 217 | { 218 | const int* v = &c.verts[j*4]; 219 | float off = 0; 220 | unsigned int colv = color; 221 | if (v[3] & RC_BORDER_VERTEX) 222 | { 223 | colv = duRGBA(255,255,255,a); 224 | off = ch*2; 225 | } 226 | 227 | float fx = orig[0] + v[0]*cs; 228 | float fy = orig[1] + (v[1]+1+(i&1))*ch + off; 229 | float fz = orig[2] + v[2]*cs; 230 | dd->vertex(fx,fy,fz, colv); 231 | } 232 | } 233 | */ 234 | renderMesh.Rebuild(); 235 | } 236 | 237 | public static void ShowTilePolyDetails(DbgRenderMesh renderMesh, Detour.dtNavMesh navMesh, int tileId) { 238 | 239 | renderMesh.Clear(); 240 | 241 | UnityEngine.Random.seed = c_RandomSeed; 242 | 243 | if (navMesh == null) { 244 | renderMesh.Rebuild(); 245 | return; 246 | } 247 | 248 | Detour.dtMeshTile tile = navMesh.getTile(tileId); 249 | 250 | if (tile == null) { 251 | Debug.LogError("RcdtcsUnityUtils.ShowTilePolyDetails : Tile " + tileId + " does not exist."); 252 | return; 253 | } 254 | 255 | int detailMeshCount = tile.detailMeshes.Length; 256 | for (int i = 0; i < detailMeshCount; ++i) { 257 | Detour.dtPolyDetail pd = tile.detailMeshes[i]; 258 | Detour.dtPoly poly = tile.polys[i]; 259 | 260 | Color col = new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); 261 | 262 | for (int j = 0; j < pd.triCount; ++j) { 263 | int tStart = (int)(pd.triBase + j) * 4; 264 | int[] vStarts = new int[3]; 265 | float[][] vSrc = new float[3][]; 266 | for (int k = 0; k < 3; ++k) { 267 | byte tk = tile.detailTris[tStart + k]; 268 | byte vCount = poly.vertCount; 269 | if (tk < vCount) { 270 | vStarts[k] = poly.verts[tk] * 3; 271 | vSrc[k] = tile.verts; 272 | } else { 273 | vStarts[k] = (int)(pd.vertBase + (tk - vCount)) * 3; 274 | vSrc[k] = tile.detailVerts; 275 | } 276 | } 277 | Vector3 a = new Vector3(vSrc[0][vStarts[0] + 0], vSrc[0][vStarts[0] + 1], vSrc[0][vStarts[0] + 2]); 278 | Vector3 b = new Vector3(vSrc[1][vStarts[1] + 0], vSrc[1][vStarts[1] + 1], vSrc[1][vStarts[1] + 2]); 279 | Vector3 c = new Vector3(vSrc[2][vStarts[2] + 0], vSrc[2][vStarts[2] + 1], vSrc[2][vStarts[2] + 2]); 280 | 281 | col = VaryColor(col); 282 | renderMesh.AddTriangle(new DbgRenderTriangle(a, b, c, col)); 283 | } 284 | } 285 | renderMesh.Rebuild(); 286 | } 287 | } -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/RcdtcsDebugRenderUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4845c9ee961d0ae4caf7403b1832cdbb 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RcdtcsUnityDemo/Scripts/Utils/RcdtcsPathUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System; 3 | 4 | using dtStatus = System.UInt32; 5 | #if DT_POLYREF64 6 | using dtPolyRef = System.UInt64; 7 | #else 8 | //typedef uint dtPolyRef; 9 | using dtPolyRef = System.UInt32; 10 | #endif 11 | 12 | //High level interface for placing path finding requests to a System Helper. 13 | public static partial class RcdtcsUnityUtils{ 14 | 15 | public class StraightPath { 16 | public const int MAX_POLYS = 256; 17 | 18 | public float[] m_straightPath = new float[MAX_POLYS*3]; 19 | public byte[] m_straightPathFlags = new byte[MAX_POLYS]; 20 | public dtPolyRef[] m_straightPathPolys = new dtPolyRef[MAX_POLYS]; 21 | public dtPolyRef[] m_RawPathPolys = new dtPolyRef[MAX_POLYS]; 22 | public int m_straightPathCount = 0; 23 | public int m_straightPathOptions = 0; 24 | public int m_RawPathLength = 0; 25 | } 26 | 27 | public class SmoothPath { 28 | public const int MAX_POLYS = 256; 29 | public const int MAX_SMOOTH = 2048; 30 | 31 | public int m_nsmoothPath = 0; 32 | public float[] m_smoothPath = new float[MAX_SMOOTH*3]; 33 | } 34 | 35 | public class PathHelper { 36 | 37 | Vector3[] m_Path = null; 38 | float[] m_Distances = null; 39 | float m_Length = 0.0f; 40 | 41 | public void Init(float[] path, int pointCount) { 42 | m_Path = new Vector3[pointCount]; 43 | m_Distances = new float[pointCount]; 44 | m_Length = 0.0f; 45 | int coordCount = pointCount * 3; 46 | for (int i = 0; i < coordCount; i += 3) { 47 | int v = i / 3; 48 | m_Path[v].x = path[i]; 49 | m_Path[v].y = path[i+1]; 50 | m_Path[v].z = path[i+2]; 51 | if (i > 0) { 52 | float d = Vector3.Distance(m_Path[v - 1], m_Path[v]); 53 | m_Distances[v - 1] = d; 54 | m_Length += d; 55 | } 56 | } 57 | } 58 | 59 | public Vector3 GetPos(float d) { 60 | if (m_Path == null || m_Path.Length == 0) { 61 | return Vector3.zero; 62 | } 63 | if (d == 0.0f) { 64 | return m_Path[0]; 65 | } 66 | else if (d >= m_Length) { 67 | return m_Path[m_Path.Length - 1]; 68 | } else { 69 | for (int i = 0; i < m_Path.Length - 1; ++i) { 70 | float segmentD = m_Distances[i]; 71 | if (d < segmentD) { 72 | float ratio = d / segmentD; 73 | return Vector3.Lerp(m_Path[i], m_Path[i+1], ratio); 74 | } else { 75 | d -= segmentD; 76 | } 77 | } 78 | } 79 | return m_Path[0]; 80 | } 81 | 82 | public float GetLength() { 83 | return m_Length; 84 | } 85 | } 86 | 87 | 88 | static bool inRange(float[] v1, int v1Start, float[] v2, int v2Start, float r, float h) 89 | { 90 | float dx = v2[v2Start + 0] - v1[v1Start + 0]; 91 | float dy = v2[v2Start + 1] - v1[v1Start + 1]; 92 | float dz = v2[v2Start + 2] - v1[v1Start + 2]; 93 | return (dx*dx + dz*dz) < r*r && Mathf.Abs(dy) < h; 94 | } 95 | 96 | static bool getSteerTarget(Detour.dtNavMeshQuery navQuery, float[] startPos, float[] endPos, 97 | float minTargetDist, 98 | dtPolyRef[] path, int pathSize, 99 | float[] steerPos, ref byte steerPosFlag, ref dtPolyRef steerPosRef, 100 | ref float[] outPoints, ref int outPointCount) 101 | { 102 | // Find steer target. 103 | const int MAX_STEER_POINTS = 3; 104 | float[] steerPath = new float[MAX_STEER_POINTS*3]; 105 | byte[] steerPathFlags = new byte[MAX_STEER_POINTS]; 106 | dtPolyRef[] steerPathPolys = new dtPolyRef[MAX_STEER_POINTS]; 107 | int nsteerPath = 0; 108 | navQuery.findStraightPath(startPos, endPos, path, pathSize, 109 | steerPath, steerPathFlags, steerPathPolys, ref nsteerPath, MAX_STEER_POINTS, 0); 110 | if (nsteerPath == 0) 111 | return false; 112 | 113 | //if (outPoints && outPointCount) 114 | //{ 115 | outPointCount = nsteerPath; 116 | for (int i = 0; i < nsteerPath; ++i){ 117 | Detour.dtVcopy(outPoints,i*3, steerPath,i*3); 118 | } 119 | //} 120 | 121 | 122 | // Find vertex far enough to steer to. 123 | int ns = 0; 124 | while (ns < nsteerPath) 125 | { 126 | // Stop at Off-Mesh link or when point is further than slop away. 127 | if ((steerPathFlags[ns] & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 || 128 | !inRange(steerPath, ns*3, startPos, 0, minTargetDist, 1000.0f)) 129 | break; 130 | ns++; 131 | } 132 | // Failed to find good point to steer to. 133 | if (ns >= nsteerPath) 134 | return false; 135 | 136 | Detour.dtVcopy(steerPos, 0, steerPath,ns*3); 137 | steerPos[1] = startPos[1]; 138 | steerPosFlag = steerPathFlags[ns]; 139 | steerPosRef = steerPathPolys[ns]; 140 | 141 | return true; 142 | } 143 | 144 | static bool getSteerTarget(Detour.dtNavMeshQuery navQuery, float[] startPos, float[] endPos, 145 | float minTargetDist, 146 | dtPolyRef[] path, int pathSize, 147 | float[] steerPos, ref byte steerPosFlag, ref dtPolyRef steerPosRef) 148 | { 149 | // Find steer target. 150 | const int MAX_STEER_POINTS = 3; 151 | float[] steerPath = new float[MAX_STEER_POINTS*3]; 152 | byte[] steerPathFlags = new byte[MAX_STEER_POINTS]; 153 | dtPolyRef[] steerPathPolys = new dtPolyRef[MAX_STEER_POINTS]; 154 | int nsteerPath = 0; 155 | navQuery.findStraightPath(startPos, endPos, path, pathSize, 156 | steerPath, steerPathFlags, steerPathPolys, ref nsteerPath, MAX_STEER_POINTS, 0); 157 | if (nsteerPath == 0) 158 | return false; 159 | 160 | // Find vertex far enough to steer to. 161 | int ns = 0; 162 | while (ns < nsteerPath) 163 | { 164 | // Stop at Off-Mesh link or when point is further than slop away. 165 | if ((steerPathFlags[ns] & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 || 166 | !inRange(steerPath, ns*3, startPos, 0, minTargetDist, 1000.0f)) 167 | break; 168 | ns++; 169 | } 170 | // Failed to find good point to steer to. 171 | if (ns >= nsteerPath) 172 | return false; 173 | 174 | Detour.dtVcopy(steerPos, 0, steerPath,ns*3); 175 | steerPos[1] = startPos[1]; 176 | steerPosFlag = steerPathFlags[ns]; 177 | steerPosRef = steerPathPolys[ns]; 178 | 179 | return true; 180 | } 181 | 182 | static int fixupCorridor(dtPolyRef[] path, int npath, int maxPath, 183 | dtPolyRef[] visited, int nvisited) 184 | { 185 | int furthestPath = -1; 186 | int furthestVisited = -1; 187 | 188 | // Find furthest common polygon. 189 | for (int i = npath-1; i >= 0; --i) 190 | { 191 | bool found = false; 192 | for (int j = nvisited-1; j >= 0; --j) 193 | { 194 | if (path[i] == visited[j]) 195 | { 196 | furthestPath = i; 197 | furthestVisited = j; 198 | found = true; 199 | } 200 | } 201 | if (found) 202 | break; 203 | } 204 | 205 | // If no intersection found just return current path. 206 | if (furthestPath == -1 || furthestVisited == -1) 207 | return npath; 208 | 209 | // Concatenate paths. 210 | 211 | // Adjust beginning of the buffer to include the visited. 212 | int req = nvisited - furthestVisited; 213 | int orig = Math.Min(furthestPath+1, npath); 214 | int size = Math.Max(0, npath-orig); 215 | if (req+size > maxPath) 216 | size = maxPath-req; 217 | if (size != 0){ 218 | //memmove(path+req, path+orig, size*sizeof(dtPolyRef)); 219 | dtPolyRef[] buf = new dtPolyRef[size]; 220 | for (int i=0;i 1 && cut == 0; i--) { 282 | for (int j = 0; j < nneis; j++) 283 | { 284 | if (path[i] == neis[j]) { 285 | cut = i; 286 | break; 287 | } 288 | } 289 | } 290 | if (cut > 1) 291 | { 292 | int offset = cut-1; 293 | npath -= offset; 294 | for (int i = 1; i < npath; i++) 295 | path[i] = path[i+offset]; 296 | } 297 | 298 | return npath; 299 | } 300 | 301 | 302 | public static float[] Vector3ToArray(Vector3 vec){ 303 | float[] arr = new float[3]; 304 | arr[0] = vec.x; 305 | arr[1] = vec.y; 306 | arr[2] = vec.z; 307 | return arr; 308 | } 309 | 310 | public static Vector3 ArrayToVector3(float[] pos, int start = 0) { 311 | return new Vector3(pos[start], pos[start + 1], pos[start + 2]); 312 | } 313 | 314 | public static StraightPath ComputeStraightPath(Detour.dtNavMeshQuery navQuery, Vector3 startPos, Vector3 endPos){ 315 | return ComputeStraightPath(navQuery, Vector3ToArray(startPos), Vector3ToArray(endPos)); 316 | } 317 | 318 | public static StraightPath ComputeStraightPath(Detour.dtNavMeshQuery navQuery, float[] startPos, float[] endPos){ 319 | //m_ComputedPathType = PathType.Straight; 320 | 321 | StraightPath path = new StraightPath(); 322 | 323 | float[] extents = new float[3]; 324 | for (int i=0;i<3;++i){ 325 | extents[i] = 10.0f; 326 | } 327 | 328 | dtPolyRef startRef = 0; 329 | dtPolyRef endRef = 0; 330 | 331 | float[] startPt = new float[3]; 332 | float[] endPt = new float[3]; 333 | 334 | Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); 335 | 336 | navQuery.findNearestPoly( startPos, extents, filter, ref startRef, ref startPt ); 337 | navQuery.findNearestPoly( endPos, extents, filter, ref endRef, ref endPt ); 338 | 339 | int pathCount = -1; 340 | 341 | navQuery.findPath(startRef, endRef, startPt, endPt, filter, path.m_RawPathPolys, ref pathCount, StraightPath.MAX_POLYS); 342 | 343 | path.m_RawPathLength = pathCount; 344 | 345 | if (pathCount > 0) 346 | { 347 | // In case of partial path, make sure the end point is clamped to the last polygon. 348 | float[] epos = new float[3]; 349 | Detour.dtVcopy(epos, endPt); 350 | if (path.m_RawPathPolys[pathCount - 1] != endRef) { 351 | bool posOverPoly = false; 352 | navQuery.closestPointOnPoly(path.m_RawPathPolys[pathCount - 1], endPt, epos, ref posOverPoly); 353 | } 354 | 355 | navQuery.findStraightPath(startPt, endPt, path.m_RawPathPolys, pathCount, 356 | path.m_straightPath, path.m_straightPathFlags, 357 | path.m_straightPathPolys, ref path.m_straightPathCount, 358 | StraightPath.MAX_POLYS, path.m_straightPathOptions); 359 | } 360 | 361 | return path; 362 | } 363 | 364 | public static float[] GetClosestPointOnNavMesh(Detour.dtNavMeshQuery navQuery, float[] pos) { 365 | 366 | float[] extents = new float[3]; 367 | for (int i = 0; i < 3; ++i) { 368 | extents[i] = 10.0f; 369 | } 370 | 371 | Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); 372 | dtPolyRef startRef = 0; 373 | float[] res = new float[3]; 374 | 375 | navQuery.findNearestPoly(pos, extents, filter, ref startRef, ref res); 376 | 377 | return res; 378 | } 379 | 380 | public static SmoothPath ComputeSmoothPath(Detour.dtNavMeshQuery navQuery, Vector3 startPos, Vector3 endPos){ 381 | return ComputeSmoothPath(navQuery, Vector3ToArray(startPos), Vector3ToArray(endPos)); 382 | } 383 | 384 | public static SmoothPath ComputeSmoothPath(Detour.dtNavMeshQuery navQuery, float[] startWorldPos, float[] endWorldPos){ 385 | 386 | SmoothPath smoothPath = new SmoothPath(); 387 | 388 | if (navQuery == null){ 389 | return smoothPath; 390 | } 391 | 392 | float[] extents = new float[3]; 393 | for (int i=0;i<3;++i){ 394 | extents[i] = 10.0f; 395 | } 396 | 397 | dtPolyRef startRef = 0; 398 | dtPolyRef endRef = 0; 399 | 400 | float[] startPt = new float[3]; 401 | float[] endPt = new float[3]; 402 | 403 | Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); 404 | 405 | navQuery.findNearestPoly(startWorldPos, extents, filter, ref startRef, ref startPt); 406 | navQuery.findNearestPoly(endWorldPos, extents, filter, ref endRef, ref endPt); 407 | 408 | const int maxPath = SmoothPath.MAX_POLYS; 409 | dtPolyRef[] path = new dtPolyRef[maxPath]; 410 | 411 | int pathCount = -1; 412 | 413 | navQuery.findPath(startRef, endRef, startPt, endPt, filter, path, ref pathCount, maxPath ); 414 | 415 | smoothPath.m_nsmoothPath = 0; 416 | 417 | if (pathCount > 0) 418 | { 419 | // Iterate over the path to find smooth path on the detail mesh surface. 420 | dtPolyRef[] polys = new dtPolyRef[SmoothPath.MAX_POLYS]; 421 | for (int i=0;i> so some changes have happened there 75 | - any field whose bit size is specified in the C++ code was upped to the closest highest parent. Ex: uint : 2 becomes byte, uint : 30 becomes uint, etc 76 | - Some variables declared in blocks tend to bleed to the upper scope in C# so some vars had to be renamed (ex: { float v; } float v; <-- illegal. 77 | - Some ported code doesn't make much sense in C# such as out of memory management. C# is more likely to throw an exception than return null after an unsuccessful new, but appropriate behaviour was not implemented. 78 | 79 | 80 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63e076b236414ad4092ce987ac4b8c56 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /RcdtcsScreenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unitycoder/rcdtcs/3f555be1e6d93ea71e93bcdc3296cb2f3ed3dabc/RcdtcsScreenshot.jpg -------------------------------------------------------------------------------- /RecastNavU5Fork.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 11.00 2 | # Visual Studio 2008 3 | 4 | Project("{B8B57DFA-6DEC-3891-1773-9FF270727477}") = "RecastNavU5Fork", "Assembly-CSharp.csproj", "{4B33986A-7100-689E-1FD5-D0F6FE50646C}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {4B33986A-7100-689E-1FD5-D0F6FE50646C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {4B33986A-7100-689E-1FD5-D0F6FE50646C}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {4B33986A-7100-689E-1FD5-D0F6FE50646C}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {4B33986A-7100-689E-1FD5-D0F6FE50646C}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | GlobalSection(MonoDevelopProperties) = preSolution 21 | StartupItem = Assembly-CSharp.csproj 22 | Policies = $0 23 | $0.TextStylePolicy = $1 24 | $1.inheritsSet = null 25 | $1.scope = text/x-csharp 26 | $0.CSharpFormattingPolicy = $2 27 | $2.inheritsSet = Mono 28 | $2.inheritsScope = text/x-csharp 29 | $2.scope = text/x-csharp 30 | $0.TextStylePolicy = $3 31 | $3.FileWidth = 120 32 | $3.TabWidth = 4 33 | $3.IndentWidth = 4 34 | $3.EolMarker = Unix 35 | $3.inheritsSet = Mono 36 | $3.inheritsScope = text/plain 37 | $3.scope = text/plain 38 | EndGlobalSection 39 | 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /RecastNavU5Fork.userprefs: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------