public void Execute(int index) { if (!m_recompute) { return; } //Compute whether a vertex is convex or concave //as well as its direction ObstacleVertexData v = m_inputObstacles[index]; float2 pos = v.pos, nextPos = m_inputObstacles[v.next].pos, prevPos = m_inputObstacles[v.prev].pos; ObstacleInfos infos = m_inputObstacleInfos[v.infos]; if (infos.length == 2)//infos.edge || { v.convex = true; } else { v.convex = LeftOf(prevPos, pos, nextPos) >= 0.0f; } v.dir = normalize(nextPos - pos); m_referenceObstacles[index] = v; }
protected override void Prepare(ref Unemployed job, float delta) { int obsCount = m_obstacles == null ? 0 : m_obstacles.Count, refCount = m_referenceObstacles.Length, vCount = 0; if (m_outputObstacleInfos.Length != obsCount) { m_outputObstacleInfos.Dispose(); m_outputObstacleInfos = new NativeArray <ObstacleInfos>(obsCount, Allocator.Persistent); m_recompute = true; } Obstacle o; ObstacleInfos infos; for (int i = 0; i < obsCount; i++) { o = m_obstacles[i]; //Keep collision infos & ORCALayer up-to-date //there is no need to recompute anything else. infos = o.infos; infos.index = i; infos.start = vCount; m_outputObstacleInfos[i] = infos; vCount += infos.length; } if (!m_recompute) { if (refCount != vCount) { m_recompute = true; } else { return; } } if (refCount != vCount) { m_referenceObstacles.Dispose(); m_referenceObstacles = new NativeArray <ObstacleVertexData>(vCount, Allocator.Persistent); m_outputObstacles.Dispose(); m_outputObstacles = new NativeArray <ObstacleVertexData>(vCount, Allocator.Persistent); } ObstacleVertexData oData; int gIndex = 0, index = 0, vCountMinusOne, firstIndex, lastIndex; if (plane == AxisPair.XY) { for (int i = 0; i < obsCount; i++) { o = m_obstacles[i]; vCount = o.Count; vCountMinusOne = vCount - 1; firstIndex = gIndex; lastIndex = gIndex + vCountMinusOne; if (!o.edge) { //Obstacle is a closed polygon for (int v = 0; v < vCount; v++) { oData = new ObstacleVertexData() { infos = i, index = index, pos = o[v].XY, prev = v == 0 ? lastIndex : index - 1, next = v == vCountMinusOne ? firstIndex : index + 1 }; m_referenceObstacles[index++] = oData; } } else { //Obstacle is an open path for (int v = 0; v < vCount; v++) { oData = new ObstacleVertexData() { infos = i, index = index, pos = o[v].XY, prev = v == 0 ? index : index - 1, next = v == vCountMinusOne ? index : index + 1 }; m_referenceObstacles[index++] = oData; } } gIndex += vCount; } } else { for (int i = 0; i < obsCount; i++) { o = m_obstacles[i]; vCount = o.Count; vCountMinusOne = vCount - 1; firstIndex = gIndex; lastIndex = gIndex + vCountMinusOne; if (!o.edge) { //Obstacle is a closed polygon for (int v = 0; v < vCount; v++) { oData = new ObstacleVertexData() { infos = i, index = index, pos = o[v].XZ, prev = v == 0 ? lastIndex : index - 1, next = v == vCountMinusOne ? firstIndex : index + 1 }; m_referenceObstacles[index++] = oData; } } else { //Obstacle is an open path for (int v = 0; v < vCount; v++) { oData = new ObstacleVertexData() { infos = i, index = index, pos = o[v].XZ, prev = v == 0 ? index : index - 1, next = v == vCountMinusOne ? index : index + 1 }; m_referenceObstacles[index++] = oData; } } gIndex += vCount; } } m_referenceObstacles.CopyTo(m_outputObstacles); }
/// <summary> /// Recursive method for building an agent k-D tree. /// </summary> /// <param name="begin">The beginning agent k-D tree node node index.</param> /// <param name="end">The ending agent k-D tree node index.</param> /// <param name="node">The current agent k-D tree node index.</param> private void BuildAgentTreeRecursive(int begin, int end, int node) { ObstacleTreeNode treeNode = m_outputTree[node]; ObstacleVertexData obstacle = m_inputObstacles[begin]; float2 pos; float minX, minY, maxX, maxY; treeNode.begin = begin; treeNode.end = end; minX = maxX = obstacle.pos.x; minY = maxY = obstacle.pos.y; for (int i = begin + 1; i < end; ++i) { pos = m_inputObstacles[i].pos; maxX = max(maxX, pos.x); minX = min(minX, pos.x); maxY = max(maxY, pos.y); minY = min(minY, pos.y); } treeNode.minX = minX; treeNode.maxX = maxX; treeNode.minY = minY; treeNode.maxY = maxY; m_outputTree[node] = treeNode; if (end - begin > ObstacleTreeNode.MAX_LEAF_SIZE) { // No leaf node. bool isVertical = treeNode.maxX - treeNode.minX > treeNode.maxY - treeNode.minY; float splitValue = 0.5f * (isVertical ? treeNode.maxX + treeNode.minX : treeNode.maxY + treeNode.minY); int left = begin; int right = end; while (left < right) { while (left < right && (isVertical ? m_inputObstacles[left].pos.x : m_inputObstacles[left].pos.y) < splitValue) { ++left; } while (right > left && (isVertical ? m_inputObstacles[right - 1].pos.x : m_inputObstacles[right - 1].pos.y) >= splitValue) { --right; } if (left < right) { ObstacleVertexData tempAgent = m_inputObstacles[left]; m_inputObstacles[left] = m_inputObstacles[right - 1]; m_inputObstacles[right - 1] = tempAgent; ++left; --right; } } int leftSize = left - begin; if (leftSize == 0) { ++leftSize; ++left; ++right; } treeNode.left = node + 1; treeNode.right = node + 2 * leftSize; m_outputTree[node] = treeNode; BuildAgentTreeRecursive(begin, left, treeNode.left); BuildAgentTreeRecursive(left, end, treeNode.right); } }
public void Execute(int index) { RaycastData raycast = m_inputRaycasts[index]; RaycastResult result = new RaycastResult() { hitAgent = -1, hitObstacle = -1, dynamicObstacle = false }; RaycastFilter filter = raycast.filter; if (filter == 0) { m_results[index] = result; return; } float2 r_position = raycast.position, r_dir = raycast.direction, hitLocation; float a_bottom = raycast.baseline, a_top = a_bottom, a_sqRange = lengthsq(raycast.distance); Segment2D raySegment = new Segment2D(raycast.position, raycast.position + raycast.direction * raycast.distance), segment; UIntPair pair = new UIntPair(0, 0); ObstacleVertexData otherVertex; bool twoSidedCast = raycast.twoSided, alreadyCovered = false, hit; #region static obstacles if ((filter & RaycastFilter.OBSTACLE_STATIC) != 0) { NativeList <int> staticObstacleNeighbors = new NativeList <int>(10, Allocator.Temp); if (m_staticObstacleTree.Length > 0) { QueryObstacleTreeRecursive(ref raycast, ref a_sqRange, 0, ref staticObstacleNeighbors, ref m_staticObstacles, ref m_staticRefObstacles, ref m_staticObstacleInfos, ref m_staticObstacleTree); } NativeHashMap <UIntPair, bool> coveredStaticEdges = new NativeHashMap <UIntPair, bool>(m_staticObstacleTree.Length * 2, Allocator.Temp); for (int i = 0; i < staticObstacleNeighbors.Length; ++i) { ObstacleVertexData vertex = m_staticObstacles[staticObstacleNeighbors[i]]; ObstacleInfos infos = m_staticObstacleInfos[vertex.infos]; pair = new UIntPair(vertex.index, vertex.next); alreadyCovered = coveredStaticEdges.TryGetValue(pair, out hit); if (!alreadyCovered) { otherVertex = m_staticRefObstacles[vertex.next]; segment = new Segment2D(vertex.pos, otherVertex.pos); alreadyCovered = (!twoSidedCast && dot(r_dir, segment.normal) < 0f); if (!alreadyCovered) { if (raySegment.IsIntersecting(segment, out hitLocation)) { //Rays intersecting ! if (result.hitObstacle == -1 || distancesq(r_position, hitLocation) < distancesq(r_position, result.hitObstacleLocation2D)) { result.hitObstacleLocation2D = hitLocation; result.hitObstacle = infos.index; } } } coveredStaticEdges.TryAdd(pair, true); } pair = new UIntPair(vertex.index, vertex.prev); alreadyCovered = coveredStaticEdges.ContainsKey(pair); if (!alreadyCovered) { otherVertex = m_staticRefObstacles[vertex.prev]; segment = new Segment2D(vertex.pos, otherVertex.pos); alreadyCovered = (!twoSidedCast && dot(r_dir, segment.normal) < 0f); if (!alreadyCovered) { if (raySegment.IsIntersecting(segment, out hitLocation)) { //Rays intersecting ! if (result.hitObstacle == -1 || distancesq(r_position, hitLocation) < distancesq(r_position, result.hitObstacleLocation2D)) { result.hitObstacleLocation2D = hitLocation; result.hitObstacle = infos.index; } } } coveredStaticEdges.TryAdd(pair, true); } } staticObstacleNeighbors.Dispose(); coveredStaticEdges.Dispose(); } #endregion #region dynamic obstacles if ((filter & RaycastFilter.OBSTACLE_DYNAMIC) != 0) { NativeList <int> dynObstacleNeighbors = new NativeList <int>(10, Allocator.Temp); if (m_dynObstacleTree.Length > 0) { QueryObstacleTreeRecursive(ref raycast, ref a_sqRange, 0, ref dynObstacleNeighbors, ref m_dynObstacles, ref m_dynRefObstacles, ref m_dynObstacleInfos, ref m_dynObstacleTree); } NativeHashMap <UIntPair, bool> coveredDynEdges = new NativeHashMap <UIntPair, bool>(m_dynObstacleTree.Length * 2, Allocator.Temp); for (int i = 0; i < dynObstacleNeighbors.Length; ++i) { ObstacleVertexData vertex = m_dynObstacles[dynObstacleNeighbors[i]]; ObstacleInfos infos = m_dynObstacleInfos[vertex.infos]; pair = new UIntPair(vertex.index, vertex.next); alreadyCovered = coveredDynEdges.TryGetValue(pair, out hit); if (!alreadyCovered) { otherVertex = m_dynRefObstacles[vertex.next]; segment = new Segment2D(vertex.pos, otherVertex.pos); alreadyCovered = (!twoSidedCast && dot(r_dir, segment.normal) < 0f); if (!alreadyCovered) { if (raySegment.IsIntersecting(segment, out hitLocation)) { //Rays intersecting ! if (result.hitObstacle == -1 || distancesq(r_position, hitLocation) < distancesq(r_position, result.hitObstacleLocation2D)) { result.hitObstacleLocation2D = hitLocation; result.hitObstacle = infos.index; result.dynamicObstacle = true; } } } coveredDynEdges.TryAdd(pair, true); } pair = new UIntPair(vertex.index, vertex.prev); alreadyCovered = coveredDynEdges.ContainsKey(pair); if (!alreadyCovered) { otherVertex = m_dynRefObstacles[vertex.prev]; segment = new Segment2D(vertex.pos, otherVertex.pos); alreadyCovered = (!twoSidedCast && dot(r_dir, segment.normal) < 0f); if (!alreadyCovered) { if (raySegment.IsIntersecting(segment, out hitLocation)) { //Rays intersecting ! if (result.hitObstacle == -1 || distancesq(r_position, hitLocation) < distancesq(r_position, result.hitObstacleLocation2D)) { result.hitObstacleLocation2D = hitLocation; result.hitObstacle = infos.index; result.dynamicObstacle = true; } } } coveredDynEdges.TryAdd(pair, true); } } dynObstacleNeighbors.Dispose(); coveredDynEdges.Dispose(); } #endregion #region Agents if ((filter & RaycastFilter.AGENTS) != 0) { NativeList <int> agentNeighbors = new NativeList <int>(10, Allocator.Temp); float radSq = a_sqRange + lengthsq(m_maxAgentRadius); if (m_inputAgents.Length > 0) { QueryAgentTreeRecursive(ref raycast, ref radSq, 0, ref agentNeighbors); } AgentData agent; int iResult, agentIndex; float2 center, i1, i2, rayEnd = r_position + normalize(r_dir) * raycast.distance; float a_radius, a_sqRadius, cx, cy, Ax = r_position.x, Ay = r_position.y, Bx = rayEnd.x, By = rayEnd.y, dx = Bx - Ax, dy = By - Ay, magA = dx * dx + dy * dy, distSq; int TryGetIntersection(out float2 intersection1, out float2 intersection2) { float magB = 2f * (dx * (Ax - cx) + dy * (Ay - cy)), magC = (Ax - cx) * (Ax - cx) + (Ay - cy) * (Ay - cy) - a_sqRadius, det = magB * magB - 4f * magA * magC, sqDet, t; if ((magA <= float.Epsilon) || (det < 0f)) { // No real solutions. intersection1 = float2(float.NaN, float.NaN); intersection2 = float2(float.NaN, float.NaN); return(0); } if (det == 0) { // One solution. t = -magB / (2f * magA); intersection1 = float2(Ax + t * dx, Ay + t * dy); intersection2 = float2(float.NaN, float.NaN); return(1); } else { // Two solutions. sqDet = sqrt(det); t = ((-magB + sqDet) / (2f * magA)); intersection1 = float2(Ax + t * dx, Ay + t * dy); t = ((-magB - sqDet) / (2f * magA)); intersection2 = float2(Ax + t * dx, Ay + t * dy); return(2); } } for (int i = 0; i < agentNeighbors.Length; ++i) { agentIndex = agentNeighbors[i]; agent = m_inputAgents[agentIndex]; center = agent.position; cx = center.x; cy = center.y; a_radius = agent.radius; a_sqRadius = a_radius * a_radius; if (dot(center - r_position, r_dir) < 0f) { continue; } iResult = TryGetIntersection(out i1, out i2); if (iResult == 0) { continue; } distSq = distancesq(r_position, i1); if (distSq < a_sqRange && (result.hitAgent == -1 || distSq < distancesq(r_position, result.hitAgentLocation2D)) && IsBetween(r_position, center, i1)) { result.hitAgentLocation2D = i1; result.hitAgent = agentIndex; } if (iResult == 2) { distSq = distancesq(r_position, i2); if (distSq < a_sqRange && (result.hitAgent == -1 || distSq < distancesq(r_position, result.hitAgentLocation2D)) && IsBetween(r_position, center, i2)) { result.hitAgentLocation2D = i2; result.hitAgent = agentIndex; } } } agentNeighbors.Dispose(); } #endregion if (m_plane == AxisPair.XY) { float baseline = raycast.worldPosition.z; if (result.hitAgent != -1) { result.hitAgentLocation = float3(result.hitAgentLocation2D, baseline); } if (result.hitObstacle != -1) { result.hitObstacleLocation = float3(result.hitObstacleLocation2D, baseline); } } else { float baseline = raycast.worldPosition.y; if (result.hitAgent != -1) { result.hitAgentLocation = float3(result.hitAgentLocation2D.x, baseline, result.hitAgentLocation2D.y); } if (result.hitObstacle != -1) { result.hitObstacleLocation = float3(result.hitObstacleLocation2D.x, baseline, result.hitObstacleLocation2D.y); } } m_results[index] = result; }