/// <summary> /// Works like GridNoise(), but allows for interpolation instead of hard jumps between values. /// </summary> public static float InterpolateNoise(Vector3 seed, Func <Vector3, Vector3> tModifier) { //Get the integer values behind and in front of the seed values. float minX = Mathf.Floor(seed.x), maxX = Mathf.Ceil(seed.x), minY = Mathf.Floor(seed.y), maxY = Mathf.Ceil(seed.y), minZ = Mathf.Floor(seed.z), maxZ = Mathf.Ceil(seed.z); //Get the interpolant (will be linear if nothing is done to modify it). Vector3 lerp = tModifier(seed - new Vector3(minX, minY, minZ)); return(Mathf.Lerp(Mathf.Lerp(Mathf.Lerp(WhiteNoise(new Vector3(minX, minY, minZ)), WhiteNoise(new Vector3(maxX, minY, minZ)), lerp.x), Mathf.Lerp(WhiteNoise(new Vector3(minX, maxY, minZ)), WhiteNoise(new Vector3(maxX, maxY, minZ)), lerp.x), lerp.y), Mathf.Lerp(Mathf.Lerp(WhiteNoise(new Vector3(minX, minY, maxZ)), WhiteNoise(new Vector3(maxX, minY, maxZ)), lerp.x), Mathf.Lerp(WhiteNoise(new Vector3(minX, maxY, maxZ)), WhiteNoise(new Vector3(maxX, maxY, maxZ)), lerp.x), lerp.y), lerp.z)); }
/// <summary> /// Works like GridNoise(), but allows for interpolation instead of hard jumps between values. /// </summary> public static float InterpolateNoise(float seed, Func <float, float> tModifier) { //Get the integer values behind and in front of the seed values. float min = Mathf.Floor(seed), max = Mathf.Ceil(seed); //Get the interpolant (will be linear if nothing is done to modify it). float lerp = tModifier(seed - min); return(Mathf.Lerp(min, max, lerp)); }
void GenerateObstacleVOs(VOBuffer vos) { var range = maxSpeed * obstacleTimeHorizon; // Iterate through all obstacles that we might need to avoid for (int i = 0; i < simulator.obstacles.Count; i++) { var obstacle = simulator.obstacles[i]; var vertex = obstacle; // Iterate through all edges (defined by vertex and vertex.dir) in the obstacle do { // Ignore the edge if the agent should not collide with it if (vertex.ignore || (vertex.layer & collidesWith) == 0) { vertex = vertex.next; continue; } // Start and end points of the current segment float elevation1, elevation2; var p1 = To2D(vertex.position, out elevation1); var p2 = To2D(vertex.next.position, out elevation2); Vector2 dir = (p2 - p1).normalized; // Signed distance from the line (not segment, lines are infinite) // TODO: Can be optimized float dist = VO.SignedDistanceFromLine(p1, dir, position); if (dist >= -0.01f && dist < range) { float factorAlongSegment = Vector2.Dot(position - p1, p2 - p1) / (p2 - p1).sqrMagnitude; // Calculate the elevation (y) coordinate of the point on the segment closest to the agent var segmentY = Mathf.Lerp(elevation1, elevation2, factorAlongSegment); // Calculate distance from the segment (not line) var sqrDistToSegment = (Vector2.Lerp(p1, p2, factorAlongSegment) - position).sqrMagnitude; // Ignore the segment if it is too far away // or the agent is too high up (or too far down) on the elevation axis (usually y axis) to avoid it. // If the XY plane is used then all elevation checks are disabled if (sqrDistToSegment < range * range && (simulator.movementPlane == MovementPlane.XY || (elevationCoordinate <= segmentY + vertex.height && elevationCoordinate + height >= segmentY))) { vos.Add(VO.SegmentObstacle(p2 - position, p1 - position, Vector2.zero, radius * 0.01f, 1f / ObstacleTimeHorizon, 1f / simulator.DeltaTime)); } } vertex = vertex.next; } while (vertex != obstacle && vertex != null && vertex.next != null); } }
public void CircleXZ(Vector3 center, float radius, Color color, float startAngle = 0f, float endAngle = 2 *Mathf.PI) { int steps = 40; #if UNITY_EDITOR if (gizmos) { steps = (int)Mathf.Clamp(Mathf.Sqrt(radius / UnityEditor.HandleUtility.GetHandleSize((UnityEngine.Gizmos.matrix * matrix).MultiplyPoint3x4(center))) * 25, 4, 40); } #endif while (startAngle > endAngle) { startAngle -= 2 * Mathf.PI; } Vector3 prev = new Vector3(Mathf.Cos(startAngle) * radius, 0, Mathf.Sin(startAngle) * radius); for (int i = 0; i <= steps; i++) { Vector3 c = new Vector3(Mathf.Cos(Mathf.Lerp(startAngle, endAngle, i / (float)steps)) * radius, 0, Mathf.Sin(Mathf.Lerp(startAngle, endAngle, i / (float)steps)) * radius); Line(center + prev, center + c, color); prev = c; } }
/** Hermite spline interpolation */ static float Hermite(float start, float end, float value) { return(Mathf.Lerp(start, end, value * value * (3.0f - 2.0f * value))); }
public static IEnumerable <Progress> ScanAllTiles(this RecastGraph self) { self.transform = self.CalculateTransform(); self.InitializeTileInfo(); // If this is true, just fill the graph with empty tiles if (self.scanEmptyGraph) { self.FillWithEmptyTiles(); yield break; } // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links) // The editor scripts also enforce this but we enforce it here too just to be sure self.walkableClimb = Mathf.Min(self.walkableClimb, self.walkableHeight); yield return(new Progress(0, "Finding Meshes")); var bounds = self.transform.Transform(new Bounds(self.forcedBoundsSize * 0.5f, self.forcedBoundsSize)); var meshes = self.CollectMeshes(bounds); var buckets = self.PutMeshesIntoTileBuckets(meshes); Queue <Int2> tileQueue = new Queue <Int2>(); // Put all tiles in the queue for (int z = 0; z < self.tileZCount; z++) { for (int x = 0; x < self.tileXCount; x++) { tileQueue.Enqueue(new Int2(x, z)); } } var workQueue = new ParallelWorkQueue <Int2>(tileQueue); // Create the voxelizers and set all settings (one for each thread) var voxelizers = new Voxelize[workQueue.threadCount]; for (int i = 0; i < voxelizers.Length; i++) { voxelizers[i] = new Voxelize(self.CellHeight, self.cellSize, self.walkableClimb, self.walkableHeight, self.maxSlope, self.maxEdgeLength); } workQueue.action = (tile, threadIndex) => { voxelizers[threadIndex].inputMeshes = buckets[tile.x + tile.y * self.tileXCount]; self.tiles[tile.x + tile.y * self.tileXCount] = self.BuildTileMesh(voxelizers[threadIndex], tile.x, tile.y, threadIndex); }; // Prioritize responsiveness while playing // but when not playing prioritize throughput // (the Unity progress bar is also pretty slow to update) int timeoutMillis = Application.isPlaying ? 1 : 200; // Scan all tiles in parallel foreach (var done in workQueue.Run(timeoutMillis)) { yield return(new Progress(Mathf.Lerp(0.1f, 0.9f, done / (float)self.tiles.Length), "Calculated Tiles: " + done + "/" + self.tiles.Length)); } yield return(new Progress(0.9f, "Assigning Graph Indices")); // Assign graph index to nodes uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(self); self.GetNodes(node => node.GraphIndex = graphIndex); // First connect all tiles with an EVEN coordinate sum // This would be the white squares on a chess board. // Then connect all tiles with an ODD coordinate sum (which would be all black squares on a chess board). // This will prevent the different threads that do all // this in parallel from conflicting with each other. // The directions are also done separately // first they are connected along the X direction and then along the Z direction. // Looping over 0 and then 1 for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++) { for (int direction = 0; direction <= 1; direction++) { for (int i = 0; i < self.tiles.Length; i++) { if ((self.tiles[i].x + self.tiles[i].z) % 2 == coordinateSum) { tileQueue.Enqueue(new Int2(self.tiles[i].x, self.tiles[i].z)); } } workQueue = new ParallelWorkQueue <Int2>(tileQueue); workQueue.action = (tile, threadIndex) => { // Connect with tile at (x+1,z) and (x,z+1) if (direction == 0 && tile.x < self.tileXCount - 1) { self.ConnectTiles(self.tiles[tile.x + tile.y * self.tileXCount], self.tiles[tile.x + 1 + tile.y * self.tileXCount]); } if (direction == 1 && tile.y < self.tileZCount - 1) { self.ConnectTiles(self.tiles[tile.x + tile.y * self.tileXCount], self.tiles[tile.x + (tile.y + 1) * self.tileXCount]); } }; var numTilesInQueue = tileQueue.Count; // Connect all tiles in parallel foreach (var done in workQueue.Run(timeoutMillis)) { yield return(new Progress(0.95f, "Connected Tiles " + (numTilesInQueue - done) + "/" + numTilesInQueue + " (Phase " + (direction + 1 + 2 * coordinateSum) + " of 4)")); } } } for (int i = 0; i < meshes.Count; i++) { meshes[i].Pool(); } ListPool <RasterizationMesh> .Release(ref meshes); // This may be used by the TileHandlerHelper script to update the tiles // while taking NavmeshCuts into account after the graph has been completely recalculated. if (self.OnRecalculatedTiles != null) { self.OnRecalculatedTiles(self.tiles.Clone() as NavmeshTile[]); } }
/** Will calculate a number of points around \a center which are on the graph and are separated by \a clearance from each other. * The maximum distance from \a center to any point will be \a radius. * Points will first be tried to be laid out as \a previousPoints and if that fails, random points will be selected. * This is great if you want to pick a number of target points for group movement. If you pass all current agent points from e.g the group's average position * this method will return target points so that the units move very little within the group, this is often aesthetically pleasing and reduces jitter if using * some kind of local avoidance. * * \param center The point to generate points around * \param g The graph to use for linecasting. If you are only using one graph, you can get this by AstarPath.active.graphs[0] as IRaycastableGraph. * Note that not all graphs are raycastable, recast, navmesh and grid graphs are raycastable. On recast and navmesh it works the best. * \param previousPoints The points to use for reference. Note that these should not be in world space. They are treated as relative to \a center. * The new points will overwrite the existing points in the list. The result will be in world space, not relative to \a center. * \param radius The final points will be at most this distance from \a center. * \param clearanceRadius The points will if possible be at least this distance from each other. * * \todo Write unit tests */ public static void GetPointsAroundPoint(Vector3 center, IRaycastableGraph g, List <Vector3> previousPoints, float radius, float clearanceRadius) { if (g == null) { throw new System.ArgumentNullException("g"); } var graph = g as NavGraph; if (graph == null) { throw new System.ArgumentException("g is not a NavGraph"); } NNInfoInternal nn = graph.GetNearestForce(center, NNConstraint.Default); center = nn.clampedPosition; if (nn.node == null) { // No valid point to start from return; } // Make sure the enclosing circle has a radius which can pack circles with packing density 0.5 radius = Mathf.Max(radius, 1.4142f * clearanceRadius * Mathf.Sqrt(previousPoints.Count)); //Mathf.Sqrt(previousPoints.Count*clearanceRadius*2)); clearanceRadius *= clearanceRadius; for (int i = 0; i < previousPoints.Count; i++) { Vector3 dir = previousPoints[i]; float magn = dir.magnitude; if (magn > 0) { dir /= magn; } float newMagn = radius; //magn > radius ? radius : magn; dir *= newMagn; GraphHitInfo hit; int tests = 0; while (true) { Vector3 pt = center + dir; if (g.Linecast(center, pt, nn.node, out hit)) { if (hit.point == PF.Vector3.zero) { // Oops, linecast actually failed completely // try again unless we have tried lots of times // then we just continue anyway tests++; if (tests > 8) { previousPoints[i] = pt; break; } } else { pt = hit.point; } } bool worked = false; for (float q = 0.1f; q <= 1.0f; q += 0.05f) { Vector3 qt = Vector3.Lerp(center, pt, q); worked = true; for (int j = 0; j < i; j++) { if ((previousPoints[j] - qt).sqrMagnitude < clearanceRadius) { worked = false; break; } } // Abort after 8 tests or when we have found a valid point if (worked || tests > 8) { worked = true; previousPoints[i] = qt; break; } } // Break out of nested loop if (worked) { break; } // If we could not find a valid point, reduce the clearance radius slightly to improve // the chances next time clearanceRadius *= 0.9f; // This will pick points in 2D closer to the edge of the circle with a higher probability dir = Random.onUnitSphere * Mathf.Lerp(newMagn, radius, tests / 5); dir.y = 0; tests++; } } }
private float BrightnessToMorphValue(float brightness) { return(-1 * Math.Clamp(PupilNeutralValue + Math.Lerp(-1.0f, 1.5f, brightness), -1.0f, 1.5f)); }
public Progress MapTo(float min, float max, string prefix = null) { return(new Progress(Mathf.Lerp(min, max, progress), prefix + description)); }
/// <summary> /// Rescale parameter `v` which is in range (minA, maxA) to be in range (minB, maxB) /// </summary> /// <param name="v">Value to rescale</param> /// <param name="minA">Min value of original interval</param> /// <param name="maxA">Max value of original interval</param> /// <param name="minB">Min value of new interval</param> /// <param name="maxB">Max value of new interval</param> /// <returns></returns> private static float Rescale(float v, float minA, float maxA, float minB, float maxB) { return(Mathf.Lerp(minB, maxB, Mathf.InverseLerp(minA, maxA, v))); }