/// <inheritdoc/> public void Execute(int index) { int startNeighborIndex = index * numNeighbors; int endNeighborIndex = startNeighborIndex + numNeighbors; NodeTransform nt = nodesTransforms[index]; for (int i = startNeighborIndex; i < endNeighborIndex; ++i) { int neighborIndex = nodesNeighbors[i].neighborIndex; bool valid = nodesNeighbors[i].isValid; int vertexIndex = index * numNeighbors * 2 + ((i - startNeighborIndex) * 2); indices[vertexIndex] = vertexIndex; indices[vertexIndex + 1] = vertexIndex + 1; if (!valid) { vertices[vertexIndex] = float3.zero; vertices[vertexIndex + 1] = float3.zero; } else { NodeTransform ntn = nodesTransforms[neighborIndex]; vertices[vertexIndex] = nt.pos + (nt.up * NodeConnectionVisualNormalOffset); vertices[vertexIndex + 1] = math.lerp(nt.pos + (nt.up * NodeConnectionVisualNormalOffset), ntn.pos + (ntn.up * NodeConnectionVisualNormalOffset), 0.5f); } } }
/// <summary> /// Get whether it is possible to reach a given neighbor from the given node index. /// </summary> /// <param name="index"></param> /// <param name="neighborIndex"></param> /// <returns></returns> private bool CanReachNeighbor(int index, int neighborIndex) { NodeTransform nt = nodesTransforms[index]; NodeTransform ntn = nodesTransforms[neighborIndex]; // the following code will "cut corners", so the path to go over ramps works as intended bool canReachNeighbor = false; int rowNode = index / scanSettings.gridWidth; int rowNeighbor = neighborIndex / scanSettings.gridWidth; bool sameRow = rowNode == rowNeighbor; bool sameCol = (index + scanSettings.gridWidth == neighborIndex) || (index - scanSettings.gridWidth == neighborIndex); const float dotThreshold = 0.99625f; // anything over is aprox 5 degrees or less in "angle distance" float dotRightsAbs = math.abs(math.dot(nt.right, ntn.right)); float dotFwdsAbs = math.abs(math.dot(nt.fwd, ntn.fwd)); if ((sameCol && dotRightsAbs >= dotThreshold) || (sameRow && dotFwdsAbs >= dotThreshold)) { // the node can be reached if the distance in height meets the requirements canReachNeighbor = math.distance(nt.pos.y, ntn.pos.y) <= maxWalkableHeightWithStep; } return(canReachNeighbor); }
/// <inheritdoc/> public void Execute(int index) { int startNeighborIndex = index * numNeighbors; int endNeighborIndex = startNeighborIndex + numNeighbors; NodeTransform nt = nodesTransforms[index]; for (int i = startNeighborIndex; i < endNeighborIndex; i++) { int neighborIndex = nodesNeighbors[i].neighborIndex; bool valid = nodesNeighbors[i].isValid; int vertexIndex = index * numNeighbors * 2 + ((i - startNeighborIndex) * 2); indices[vertexIndex] = vertexIndex; indices[vertexIndex + 1] = vertexIndex + 1; if (!valid) { vertices[vertexIndex] = Vector3.zero; vertices[vertexIndex + 1] = Vector3.zero; } else { NodeTransform ntn = nodesTransforms[neighborIndex]; vertices[vertexIndex] = nt.Pos + (nt.Up * NodeVisualNormalOffset); vertices[vertexIndex + 1] = Vector3.Lerp(nt.Pos + (nt.Up * NodeVisualNormalOffset), ntn.Pos + (ntn.Up * NodeVisualNormalOffset), 0.5f); } } }
/// <inheritdoc/> public void Execute(int index) { NodeTransform nt = nodesTransforms[index]; // start a bit before the node just in case there's an obstacle overlapping a bit Vector3 center = (Vector3)(nt.pos - nt.up * 0.1f); // nodes are squares and we don't plan to change it float halfWidth = NodeHalfSize * boxNodePercentage; Vector3 halfExtents = new Vector3(halfWidth, 0.01f, halfWidth); commands[index] = new BoxcastCommand(center, halfExtents, (Quaternion)nt.GetRotation(), (Vector3)nt.up, maxCharacterHeight, mask); }
/// <inheritdoc/> public void Execute(int index) { NodeTransform nt = nodesTransforms[index]; // start a bit before the node just in case there's an obstacle overlapping a bit Vector3 center = nt.Pos - nt.Up * 0.1f; // nodes are squares and we don't plan to change it float halfWidth = NodeHalfSize * settings.boxNodePercentage; float halfDepth = halfWidth; Vector3 halfExtents = new Vector3(halfWidth, 0.01f, halfDepth); commands[index] = new BoxcastCommand(center, halfExtents, nt.GetRotation(), nt.Up, settings.maxCharacterHeight, settings.mask); }
/// <inheritdoc/> public void Execute(int index) { RaycastHit hit = hits[index]; RaycastCommand command = commands[index]; // Note: we can't check for collider to be null since reference types are not allowed bool validNode = hit.normal != default(Vector3); Vector3 pos = validNode ? hit.point : new Vector3(command.from.x, 0.0f, command.from.z); Vector3 normal = validNode ? hit.normal : Vector3.up; nodesTransforms[index] = new NodeTransform(pos, normal); nodesTypes[index] = !validNode ? NodeType.Invalid : NodeType.Free; }
/// <inheritdoc/> public void Execute(int index) { RaycastHit hit = hits[index]; RaycastCommand command = commands[index]; // we can't check for collider to be null since reference types are not allowed bool validNode = hit.normal != default(Vector3); float3 commandPos = (float3)command.from; commandPos.y = 0.0f; float3 pos = validNode ? (float3)hit.point : commandPos; float3 normal = validNode ? (float3)hit.normal : math.up(); nodesTransforms[index] = new NodeTransform(pos, normal); nodesTypes[index] = !validNode ? NodeType.Invalid : NodeType.Free; }
/// <summary> /// Adds the nodes debugging. /// </summary> public void RecalculateDebug() { DisposeDebugNativeDatastructures(); int numNodes = gridDepth * gridWidth; // prepare the job that calculates the vertices for the neighbor connection lines int arrayLength = numNodes * NodeNeighbors * 2; connectionsMeshVertices = new NativeArray <Vector3>(arrayLength, Allocator.Persistent); connectionsMeshIndices = new NativeArray <int>(arrayLength, Allocator.Persistent); CalculateConnectionMeshJob calcConnectionsMeshJob = new CalculateConnectionMeshJob(NodeNeighbors, nodesTransforms, nodesNeighbors, connectionsMeshVertices, connectionsMeshIndices); JobHandle calcConnectionMeshHandle = calcConnectionsMeshJob.Schedule(numNodes, 8); // do other required stuff before calling complete so we have actual parallelism MeshRenderer mr = Utils.GetOrAddComponent <MeshRenderer>(transform, out bool createdRenderer); mr.shadowCastingMode = ShadowCastingMode.Off; mr.sharedMaterial = nodeConnectionsMaterial; mr.lightProbeUsage = LightProbeUsage.Off; mr.reflectionProbeUsage = ReflectionProbeUsage.Off; mr.enabled = showNodesConnections; MeshFilter filter = Utils.GetOrAddComponent <MeshFilter>(transform, out bool createdFilter); filter.sharedMesh = connectionsMesh; // the nodes themselves nodeBatcher.Clear(); if (showNodes) { for (int i = 0; i < numNodes; i++) { NodeTransform nt = nodesTransforms[i]; NodeType nodeType = nodesTypes[i]; Color32 c; if (nodeType == NodeType.Invalid) { c = invalidNodeColor; } else if (nodeType == NodeType.OccupiedByObstacle) { c = nonWalkableNodeColor; } else { c = walkableNodeColor; } Vector3 pos = nt.Pos + (nt.Up * NodeVisualNormalOffset); Matrix4x4 trs = Matrix4x4.TRS(pos, nt.GetRotation(), Vector3.one); // batch each node quad debug nodeBatcher.AddItem(c, trs); } } calcConnectionMeshHandle.Complete(); // set the mesh using the results of the job connectionsMesh.SetVertices(calcConnectionsMeshJob.vertices); connectionsMesh.SetIndices(calcConnectionsMeshJob.indices, MeshTopology.Lines, 0); }