Advertisement
Guest User

Untitled

a guest
Nov 16th, 2018
407
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 32.63 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using Pathfinding.RVO.Sampled;
  5. using UnityEngine.Profiling;
  6.  
  7. #if NETFX_CORE
  8. using Thread = Pathfinding.WindowsStore.Thread;
  9. using ThreadStart = Pathfinding.WindowsStore.ThreadStart;
  10. #else
  11. using Thread = System.Threading.Thread;
  12. using ThreadStart = System.Threading.ThreadStart;
  13. #endif
  14.  
  15. /// <summary>Local avoidance related classes</summary>
  16. namespace Pathfinding.RVO {
  17.     /// <summary>
  18.     /// Exposes properties of an Agent class.
  19.     ///
  20.     /// See: RVOController
  21.     /// See: RVOSimulator
  22.     /// </summary>
  23.     public interface IAgent {
  24.         /// <summary>
  25.         /// Position of the agent.
  26.         /// The agent does not move by itself, a movement script has to be responsible for
  27.         /// reading the CalculatedTargetPoint and CalculatedSpeed properties and move towards that point with that speed.
  28.         /// This property should ideally be set every frame.
  29.         ///
  30.         /// Note that this is a Vector2, not a Vector3 since the RVO simulates everything internally in 2D. So if your agents move in the
  31.         /// XZ plane you may have to convert it to a Vector3 like this.
  32.         ///
  33.         /// <code>
  34.         /// Vector3 position3D = new Vector3(agent.Position.x, agent.ElevationCoordinate, agent.Position.y);
  35.         /// </code>
  36.         /// </summary>
  37.         Vector2 Position { get; set; }
  38.  
  39.         /// <summary>
  40.         /// Coordinate which separates characters in the height direction.
  41.         /// Since RVO can be used either in 2D or 3D, it is not as simple as just using the y coordinate of the 3D position.
  42.         /// In 3D this will most likely be set to the y coordinate, but in 2D (top down) it should in most cases be set to 0 since
  43.         /// all characters are always in the same plane, however it may be set to some other value, for example if the game
  44.         /// is 2D isometric.
  45.         ///
  46.         /// The position is assumed to be at the base of the character (near the feet).
  47.         /// </summary>
  48.         float ElevationCoordinate { get; set; }
  49.  
  50.         /// <summary>
  51.         /// Optimal point to move towards to avoid collisions.
  52.         /// The movement script should move towards this point with a speed of <see cref="CalculatedSpeed"/>.
  53.         ///
  54.         /// Note: This is a Vector2, not a Vector3 as that is what the <see cref="SetTarget"/> method accepts.
  55.         ///
  56.         /// See: RVOController.CalculateMovementDelta.
  57.         /// </summary>
  58.         Vector2 CalculatedTargetPoint { get; }
  59.  
  60.         /// <summary>
  61.         /// Optimal speed of the agent to avoid collisions.
  62.         /// The movement script should move towards <see cref="CalculatedTargetPoint"/> with this speed.
  63.         /// </summary>
  64.         float CalculatedSpeed { get; }
  65.  
  66.         /// <summary>
  67.         /// Point towards which the agent should move.
  68.         /// Usually you set this once per frame. The agent will try move as close to the target point as possible.
  69.         /// Will take effect at the next simulation step.
  70.         ///
  71.         /// Note: The system assumes that the agent will stop when it reaches the target point
  72.         /// so if you just want to move the agent in a particular direction, make sure that you set the target point
  73.         /// a good distance in front of the character as otherwise the system may not avoid colisions that well.
  74.         /// What would happen is that the system (in simplified terms) would think that the agents would stop
  75.         /// before the collision and thus it wouldn't slow down or change course. See the image below.
  76.         /// In the image the desiredSpeed is the length of the blue arrow and the target point
  77.         /// is the point where the black arrows point to.
  78.         /// In the upper case the agent does not avoid the red agent (you can assume that the red
  79.         /// agent has a very small velocity for simplicity) while in the lower case it does.\n
  80.         /// If you are following a path a good way to pick the target point is to set it to
  81.         /// <code>
  82.         /// targetPoint = directionToNextWaypoint.normalized * remainingPathDistance
  83.         /// </code>
  84.         /// Where remainingPathDistance is the distance until the character would reach the end of the path.
  85.         /// This works well because at the end of the path the direction to the next waypoint will just be the
  86.         /// direction to the last point on the path and remainingPathDistance will be the distance to the last point
  87.         /// in the path, so targetPoint will be set to simply the last point in the path. However when remainingPathDistance
  88.         /// is large the target point will be so far away that the agent will essentially be told to move in a particular
  89.         /// direction, which is precisely what we want.
  90.         /// [Open online documentation to see images]
  91.         /// </summary>
  92.         /// <param name="targetPoint">Target point in world space (XZ plane or XY plane depending on if the simulation is configured for 2D or 3D).
  93.         ///      Note that this is a Vector2, not a Vector3 since the system simulates everything internally in 2D. So if your agents move in the
  94.         ///      XZ plane you will have to supply it as a Vector2 with (x,z) coordinates.</param>
  95.         /// <param name="desiredSpeed">Desired speed of the agent. In world units per second. The agent will try to move with this
  96.         ///      speed if possible.</param>
  97.         /// <param name="maxSpeed">Max speed of the agent. In world units per second. If necessary (for example if another agent
  98.         ///      is on a collision trajectory towards this agent) the agent can move at this speed.
  99.         ///      Should be at least as high as desiredSpeed, but it is recommended to use a slightly
  100.         ///      higher value than desiredSpeed (for example desiredSpeed*1.2).</param>
  101.         void SetTarget (Vector2 targetPoint, float desiredSpeed, float maxSpeed);
  102.  
  103.         /// <summary>Locked agents will be assumed not to move</summary>
  104.         bool Locked { get; set; }
  105.  
  106.         /// <summary>
  107.         /// Radius of the agent in world units.
  108.         /// Agents are modelled as circles/cylinders.
  109.         /// </summary>
  110.         float Radius { get; set; }
  111.  
  112.         /// <summary>
  113.         /// Height of the agent in world units.
  114.         /// Agents are modelled as circles/cylinders.
  115.         /// </summary>
  116.         float Height { get; set; }
  117.  
  118.         /// <summary>
  119.         /// Max number of estimated seconds to look into the future for collisions with agents.
  120.         /// As it turns out, this variable is also very good for controling agent avoidance priorities.
  121.         /// Agents with lower values will avoid other agents less and thus you can make 'high priority agents' by
  122.         /// giving them a lower value.
  123.         /// </summary>
  124.         float AgentTimeHorizon { get; set; }
  125.  
  126.         /// <summary>Max number of estimated seconds to look into the future for collisions with obstacles</summary>
  127.         float ObstacleTimeHorizon { get; set; }
  128.  
  129.         /// <summary>
  130.         /// Max number of agents to take into account.
  131.         /// Decreasing this value can lead to better performance, increasing it can lead to better quality of the simulation.
  132.         /// </summary>
  133.         int MaxNeighbours { get; set; }
  134.  
  135.         /// <summary>Number of neighbours that the agent took into account during the last simulation step</summary>
  136.         int NeighbourCount { get; }
  137.  
  138.         /// <summary>
  139.         /// Specifies the avoidance layer for this agent.
  140.         /// The <see cref="CollidesWith"/> mask on other agents will determine if they will avoid this agent.
  141.         /// </summary>
  142.         RVOLayer Layer { get; set; }
  143.  
  144.         /// <summary>
  145.         /// Layer mask specifying which layers this agent will avoid.
  146.         /// You can set it as CollidesWith = RVOLayer.DefaultAgent | RVOLayer.Layer3 | RVOLayer.Layer6 ...
  147.         ///
  148.         /// See: http://en.wikipedia.org/wiki/Mask_(computing)
  149.         /// </summary>
  150.         RVOLayer CollidesWith { get; set; }
  151.  
  152.         /// <summary>
  153.         /// Draw debug information.
  154.         ///
  155.         /// Note: Will always draw debug info in the XZ plane even if <see cref="Pathfinding.RVO.Simulator.movementPlane"/> is set to XY.
  156.         /// Note: Ignored if multithreading on the simulator component has been enabled
  157.         /// since Unity's Debug API can only be called from the main thread.
  158.         /// </summary>
  159.         bool DebugDraw { get; set; }
  160.  
  161.         /// <summary>
  162.         /// List of obstacle segments which were close to the agent during the last simulation step.
  163.         /// Can be used to apply additional wall avoidance forces for example.
  164.         /// Segments are formed by the obstacle vertex and its .next property.
  165.         ///
  166.         /// \bug Always returns null
  167.         /// </summary>
  168.         [System.Obsolete()]
  169.         List<ObstacleVertex> NeighbourObstacles { get; }
  170.  
  171.         /// <summary>
  172.         /// How strongly other agents will avoid this agent.
  173.         /// Usually a value between 0 and 1.
  174.         /// Agents with similar priorities will avoid each other with an equal strength.
  175.         /// If an agent sees another agent with a higher priority than itself it will avoid that agent more strongly.
  176.         /// In the extreme case (e.g this agent has a priority of 0 and the other agent has a priority of 1) it will treat the other agent as being a moving obstacle.
  177.         /// Similarly if an agent sees another agent with a lower priority than itself it will avoid that agent less.
  178.         ///
  179.         /// In general the avoidance strength for this agent is:
  180.         /// <code>
  181.         /// if this.priority > 0 or other.priority > 0:
  182.         ///     avoidanceStrength = other.priority / (this.priority + other.priority);
  183.         /// else:
  184.         ///     avoidanceStrength = 0.5
  185.         /// </code>
  186.         /// </summary>
  187.         float Priority { get; set; }
  188.  
  189.         /// <summary>
  190.         /// Callback which will be called right before avoidance calculations are started.
  191.         /// Used to update the other properties with the most up to date values
  192.         /// </summary>
  193.         System.Action PreCalculationCallback { set; }
  194.  
  195.         /// <summary>
  196.         /// Set the normal of a wall (or something else) the agent is currently colliding with.
  197.         /// This is used to make the RVO system aware of things like physics or an agent being clamped to the navmesh.
  198.         /// The velocity of this agent that other agents observe will be modified so that there is no component
  199.         /// into the wall. The agent will however not start to avoid the wall, for that you will need to add RVO obstacles.
  200.         ///
  201.         /// This value will be cleared after the next simulation step, normally it should be set every frame
  202.         /// when the collision is still happening.
  203.         /// </summary>
  204.         void SetCollisionNormal (Vector2 normal);
  205.  
  206.         /// <summary>
  207.         /// Set the current velocity of the agent.
  208.         /// This will override the local avoidance input completely.
  209.         /// It is useful if you have a player controlled character and want other agents to avoid it.
  210.         ///
  211.         /// Calling this method will mark the agent as being externally controlled for 1 simulation step.
  212.         /// Local avoidance calculations will be skipped for the next simulation step but will be resumed
  213.         /// after that unless this method is called again.
  214.         /// </summary>
  215.         void ForceSetVelocity (Vector2 velocity);
  216.     }
  217.  
  218.     /// <summary>Plane which movement is primarily happening in</summary>
  219.     public enum MovementPlane {
  220.         /// <summary>Movement happens primarily in the XZ plane (3D)</summary>
  221.         XZ,
  222.         /// <summary>Movement happens primarily in the XY plane (2D)</summary>
  223.         XY
  224.     }
  225.  
  226.     [System.Flags]
  227.     public enum RVOLayer {
  228.         DefaultAgent = 1 << 0,
  229.         DefaultObstacle = 1 << 1,
  230.         Layer2 = 1 << 2,
  231.         Layer3 = 1 << 3,
  232.         Layer4 = 1 << 4,
  233.         Layer5 = 1 << 5,
  234.         Layer6 = 1 << 6,
  235.         Layer7 = 1 << 7,
  236.         Layer8 = 1 << 8,
  237.         Layer9 = 1 << 9,
  238.         Layer10 = 1 << 10,
  239.         Layer11 = 1 << 11,
  240.         Layer12 = 1 << 12,
  241.         Layer13 = 1 << 13,
  242.         Layer14 = 1 << 14,
  243.         Layer15 = 1 << 15,
  244.         Layer16 = 1 << 16,
  245.         Layer17 = 1 << 17,
  246.         Layer18 = 1 << 18,
  247.         Layer19 = 1 << 19,
  248.         Layer20 = 1 << 20,
  249.         Layer21 = 1 << 21,
  250.         Layer22 = 1 << 22,
  251.         Layer23 = 1 << 23,
  252.         Layer24 = 1 << 24,
  253.         Layer25 = 1 << 25,
  254.         Layer26 = 1 << 26,
  255.         Layer27 = 1 << 27,
  256.         Layer28 = 1 << 28,
  257.         Layer29 = 1 << 29,
  258.         Layer30 = 1 << 30
  259.     }
  260.  
  261.     /// <summary>
  262.     /// Local Avoidance %Simulator.
  263.     /// This class handles local avoidance simulation for a number of agents using
  264.     /// Reciprocal Velocity Obstacles (RVO) and Optimal Reciprocal Collision Avoidance (ORCA).
  265.     ///
  266.     /// This class will handle calculation of velocities from desired velocities supplied by a script.
  267.     /// It is, however, not responsible for moving any objects in a Unity Scene. For that there are other scripts (see below).
  268.     ///
  269.     /// Obstacles can be added and removed from the simulation, agents can also be added and removed at any time.
  270.     /// See: RVOSimulator
  271.     /// See: RVOAgent
  272.     /// See: Pathfinding.RVO.IAgent
  273.     ///
  274.     /// The implementation uses a sampling based algorithm with gradient descent to find the avoidance velocities.
  275.     ///
  276.     /// You will most likely mostly use the wrapper class RVOSimulator.
  277.     /// </summary>
  278.     public class Simulator {
  279.         /// <summary>
  280.         /// Use Double Buffering.
  281.         /// See: DoubleBuffering
  282.         /// </summary>
  283.         private readonly bool doubleBuffering = true;
  284.  
  285.         /// <summary>
  286.         /// Inverse desired simulation fps.
  287.         /// See: DesiredDeltaTime
  288.         /// </summary>
  289.         private float desiredDeltaTime = 0.05f;
  290.  
  291.         /// <summary>Worker threads</summary>
  292.         readonly WorkerSpecialName[] workers;
  293.  
  294.         /// <summary>Agents in this simulation</summary>
  295.         List<Agent> agents;
  296.  
  297.         /// <summary>Obstacles in this simulation</summary>
  298.         public List<ObstacleVertex> obstacles;
  299.  
  300.         /// <summary>
  301.         /// Quadtree for this simulation.
  302.         /// Used internally by the simulation to perform fast neighbour lookups for each agent.
  303.         /// Please only read from this tree, do not rebuild it since that can interfere with the simulation.
  304.         /// It is rebuilt when necessary.
  305.         /// </summary>
  306.         public RVOQuadtree Quadtree { get; private set; }
  307.  
  308.         private float deltaTime;
  309.         private float lastStep = -99999;
  310.  
  311.         private bool doUpdateObstacles = false;
  312.         private bool doCleanObstacles = false;
  313.  
  314.         public float DeltaTime { get { return deltaTime; } }
  315.  
  316.         /// <summary>Is using multithreading</summary>
  317.         public bool Multithreading { get { return workers != null && workers.Length > 0; } }
  318.  
  319.         /// <summary>
  320.         /// Time in seconds between each simulation step.
  321.         /// This is the desired delta time, the simulation will never run at a higher fps than
  322.         /// the rate at which the Update function is called.
  323.         /// </summary>
  324.         public float DesiredDeltaTime { get { return desiredDeltaTime; } set { desiredDeltaTime = System.Math.Max(value, 0.0f); } }
  325.  
  326.         /// <summary>
  327.         /// Bias agents to pass each other on the right side.
  328.         /// If the desired velocity of an agent puts it on a collision course with another agent or an obstacle
  329.         /// its desired velocity will be rotated this number of radians (1 radian is approximately 57°) to the right.
  330.         /// This helps to break up symmetries and makes it possible to resolve some situations much faster.
  331.         ///
  332.         /// When many agents have the same goal this can however have the side effect that the group
  333.         /// clustered around the target point may as a whole start to spin around the target point.
  334.         ///
  335.         /// Recommended values are in the range of 0 to 0.2.
  336.         ///
  337.         /// If this value is negative, the agents will be biased towards passing each other on the left side instead.
  338.         /// </summary>
  339.         public float symmetryBreakingBias = 0.1f;
  340.  
  341.         /// <summary>Determines if the XY (2D) or XZ (3D) plane is used for movement</summary>
  342.         public readonly MovementPlane movementPlane = MovementPlane.XZ;
  343.  
  344.         /// <summary>
  345.         /// Get a list of all agents.
  346.         ///
  347.         /// This is an internal list.
  348.         /// I'm not going to be restrictive so you may access it since it is better for performance
  349.         /// but please do not modify it since that can cause errors in the simulation.
  350.         ///
  351.         /// Warning: Do not modify this list!
  352.         /// </summary>
  353.         public List<Agent> GetAgents () {
  354.             return agents;
  355.         }
  356.  
  357.         /// <summary>
  358.         /// Get a list of all obstacles.
  359.         /// This is a list of obstacle vertices.
  360.         /// Each vertex is part of a doubly linked list loop
  361.         /// forming an obstacle polygon.
  362.         ///
  363.         /// Warning: Do not modify this list!
  364.         ///
  365.         /// See: AddObstacle
  366.         /// See: RemoveObstacle
  367.         /// </summary>
  368.         public List<ObstacleVertex> GetObstacles () {
  369.             return obstacles;
  370.         }
  371.  
  372.         /// <summary>
  373.         /// Create a new simulator.
  374.         ///
  375.         /// Note: Will only have effect if using multithreading
  376.         ///
  377.         /// See: <see cref="Multithreading"/>
  378.         /// </summary>
  379.         /// <param name="workers">Use the specified number of worker threads.\n
  380.         /// When the number zero is specified, no multithreading will be used.
  381.         /// A good number is the number of cores on the machine.</param>
  382.         /// <param name="doubleBuffering">Use Double Buffering for calculations.
  383.         /// Testing done with 5000 agents and 0.1 desired delta time showed that with double buffering enabled
  384.         /// the game ran at 50 fps for most frames, dropping to 10 fps during calculation frames. But without double buffering
  385.         /// it ran at around 10 fps all the time.\n
  386.         /// This will let threads calculate while the game progresses instead of waiting for the calculations
  387.         /// to finish.</param>
  388.         /// <param name="movementPlane">The plane that the movement happens in. XZ for 3D games, XY for 2D games.</param>
  389.         public Simulator (int workers, bool doubleBuffering, MovementPlane movementPlane) {
  390.             this.workers = new Simulator.WorkerSpecialName[workers];
  391.             this.doubleBuffering = doubleBuffering;
  392.             this.DesiredDeltaTime = 1;
  393.             this.movementPlane = movementPlane;
  394.             Quadtree = new RVOQuadtree();
  395.  
  396.             for (int i = 0; i < workers; i++) this.workers[i] = new Simulator.WorkerSpecialName(this);
  397.  
  398.             agents = new List<Agent>();
  399.             obstacles = new List<ObstacleVertex>();
  400.         }
  401.  
  402.         /// <summary>Removes all agents from the simulation</summary>
  403.         public void ClearAgents () {
  404.             //Bad to update agents while processing of current agents might be done
  405.             //Don't interfere with ongoing calculations
  406.             BlockUntilSimulationStepIsDone();
  407.  
  408.             for (int i = 0; i < agents.Count; i++) {
  409.                 agents[i].simulator = null;
  410.             }
  411.             agents.Clear();
  412.         }
  413.  
  414.         /// <summary>
  415.         /// Terminates all worker threads.
  416.         /// Warning: You must call this when you are done with the simulator, otherwise some threads can linger and lead to memory leaks.
  417.         /// </summary>
  418.         public void OnDestroy () {
  419.             if (workers != null) {
  420.                 for (int i = 0; i < workers.Length; i++) workers[i].Terminate();
  421.             }
  422.         }
  423.  
  424.         /// <summary>
  425.         /// Add a previously removed agent to the simulation.
  426.         /// An agent can only be in one simulation at a time, any attempt to add an agent to two simulations
  427.         /// or multiple times to the same simulation will result in an exception being thrown.
  428.         ///
  429.         /// See: RemoveAgent
  430.         /// </summary>
  431.         public IAgent AddAgent (IAgent agent) {
  432.             if (agent == null) throw new System.ArgumentNullException("Agent must not be null");
  433.  
  434.             Agent agentReal = agent as Agent;
  435.             if (agentReal == null) throw new System.ArgumentException("The agent must be of type Agent. Agent was of type "+agent.GetType());
  436.  
  437.             if (agentReal.simulator != null && agentReal.simulator == this) throw new System.ArgumentException("The agent is already in the simulation");
  438.             else if (agentReal.simulator != null) throw new System.ArgumentException("The agent is already added to another simulation");
  439.             agentReal.simulator = this;
  440.  
  441.             //Don't interfere with ongoing calculations
  442.             BlockUntilSimulationStepIsDone();
  443.  
  444.             agents.Add(agentReal);
  445.             return agent;
  446.         }
  447.  
  448.         /// <summary>
  449.         /// Add an agent at the specified position.
  450.         /// You can use the returned interface to read several parameters such as position and velocity
  451.         /// and set for example radius and desired velocity.
  452.         ///
  453.         /// Deprecated: Use AddAgent(Vector2,float) instead
  454.         /// </summary>
  455.         [System.Obsolete("Use AddAgent(Vector2,float) instead")]
  456.         public IAgent AddAgent (Vector3 position) {
  457.             return AddAgent(new Vector2(position.x, position.z), position.y);
  458.         }
  459.  
  460.         /// <summary>
  461.         /// Add an agent at the specified position.
  462.         /// You can use the returned interface to read and write parameters
  463.         /// and set for example radius and desired point to move to.
  464.         ///
  465.         /// See: RemoveAgent
  466.         /// </summary>
  467.         /// <param name="position">See IAgent.Position</param>
  468.         /// <param name="elevationCoordinate">See IAgent.ElevationCoordinate</param>
  469.         public IAgent AddAgent (Vector2 position, float elevationCoordinate) {
  470.             return AddAgent(new Agent(position, elevationCoordinate));
  471.         }
  472.  
  473.         /// <summary>
  474.         /// Removes a specified agent from this simulation.
  475.         /// The agent can be added again later by using AddAgent.
  476.         ///
  477.         /// See: AddAgent(IAgent)
  478.         /// See: ClearAgents
  479.         /// </summary>
  480.         public void RemoveAgent (IAgent agent) {
  481.             if (agent == null) throw new System.ArgumentNullException("Agent must not be null");
  482.  
  483.             Agent agentReal = agent as Agent;
  484.             if (agentReal == null) throw new System.ArgumentException("The agent must be of type Agent. Agent was of type "+agent.GetType());
  485.  
  486.             if (agentReal.simulator != this) throw new System.ArgumentException("The agent is not added to this simulation");
  487.  
  488.             //Don't interfere with ongoing calculations
  489.             BlockUntilSimulationStepIsDone();
  490.  
  491.             agentReal.simulator = null;
  492.  
  493.             if (!agents.Remove(agentReal)) {
  494.                 throw new System.ArgumentException("Critical Bug! This should not happen. Please report this.");
  495.             }
  496.         }
  497.  
  498.         /// <summary>
  499.         /// Adds a previously removed obstacle.
  500.         /// This does not check if the obstacle is already added to the simulation, so please do not add an obstacle multiple times.
  501.         ///
  502.         /// It is assumed that this is a valid obstacle.
  503.         /// </summary>
  504.         public ObstacleVertex AddObstacle (ObstacleVertex v) {
  505.             if (v == null) throw new System.ArgumentNullException("Obstacle must not be null");
  506.  
  507.             //Don't interfere with ongoing calculations
  508.             BlockUntilSimulationStepIsDone();
  509.  
  510.             obstacles.Add(v);
  511.             UpdateObstacles();
  512.             return v;
  513.         }
  514.  
  515.         /// <summary>
  516.         /// Adds an obstacle described by the vertices.
  517.         ///
  518.         /// See: RemoveObstacle
  519.         /// </summary>
  520.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height, bool cycle = true) {
  521.             return AddObstacle(vertices, height, Matrix4x4.identity, RVOLayer.DefaultObstacle, cycle);
  522.         }
  523.  
  524.         /// <summary>
  525.         /// Adds an obstacle described by the vertices.
  526.         ///
  527.         /// See: RemoveObstacle
  528.         /// </summary>
  529.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height, Matrix4x4 matrix, RVOLayer layer = RVOLayer.DefaultObstacle, bool cycle = true) {
  530.             if (vertices == null) throw new System.ArgumentNullException("Vertices must not be null");
  531.             if (vertices.Length < 2) throw new System.ArgumentException("Less than 2 vertices in an obstacle");
  532.  
  533.             ObstacleVertex first = null;
  534.             ObstacleVertex prev = null;
  535.  
  536.             // Don't interfere with ongoing calculations
  537.             BlockUntilSimulationStepIsDone();
  538.  
  539.             for (int i = 0; i < vertices.Length; i++) {
  540.                 var v = new ObstacleVertex {
  541.                     prev = prev,
  542.                     layer = layer,
  543.                     height = height
  544.                 };
  545.  
  546.                 if (first == null) first = v;
  547.                 else prev.next = v;
  548.  
  549.                 prev = v;
  550.             }
  551.  
  552.             if (cycle) {
  553.                 prev.next = first;
  554.                 first.prev = prev;
  555.             }
  556.  
  557.             UpdateObstacle(first, vertices, matrix);
  558.             obstacles.Add(first);
  559.             return first;
  560.         }
  561.  
  562.         /// <summary>
  563.         /// Adds a line obstacle with a specified height.
  564.         ///
  565.         /// See: RemoveObstacle
  566.         /// </summary>
  567.         public ObstacleVertex AddObstacle (Vector3 a, Vector3 b, float height) {
  568.             ObstacleVertex first = new ObstacleVertex();
  569.             ObstacleVertex second = new ObstacleVertex();
  570.  
  571.             first.layer = RVOLayer.DefaultObstacle;
  572.             second.layer = RVOLayer.DefaultObstacle;
  573.  
  574.             first.prev = second;
  575.             second.prev = first;
  576.             first.next = second;
  577.             second.next = first;
  578.  
  579.             first.position = a;
  580.             second.position = b;
  581.             first.height = height;
  582.             second.height = height;
  583.             second.ignore = true;
  584.  
  585.             first.dir = new Vector2(b.x-a.x, b.z-a.z).normalized;
  586.             second.dir = -first.dir;
  587.  
  588.             //Don't interfere with ongoing calculations
  589.             BlockUntilSimulationStepIsDone();
  590.  
  591.             obstacles.Add(first);
  592.  
  593.             UpdateObstacles();
  594.             return first;
  595.         }
  596.  
  597.         /// <summary>
  598.         /// Updates the vertices of an obstacle.
  599.         ///
  600.         /// The number of vertices in an obstacle cannot be changed, existing vertices can only be moved.
  601.         /// </summary>
  602.         /// <param name="obstacle">%Obstacle to update</param>
  603.         /// <param name="vertices">New vertices for the obstacle, must have at least the number of vertices in the original obstacle</param>
  604.         /// <param name="matrix">%Matrix to multiply vertices with before updating obstacle</param>
  605.         public void UpdateObstacle (ObstacleVertex obstacle, Vector3[] vertices, Matrix4x4 matrix) {
  606.             if (vertices == null) throw new System.ArgumentNullException("Vertices must not be null");
  607.             if (obstacle == null) throw new System.ArgumentNullException("Obstacle must not be null");
  608.  
  609.             if (vertices.Length < 2) throw new System.ArgumentException("Less than 2 vertices in an obstacle");
  610.  
  611.             bool identity = matrix == Matrix4x4.identity;
  612.  
  613.             // Don't interfere with ongoing calculations
  614.             BlockUntilSimulationStepIsDone();
  615.  
  616.             int count = 0;
  617.  
  618.             // Obstacles are represented using linked lists
  619.             var vertex = obstacle;
  620.             do {
  621.                 if (count >= vertices.Length) {
  622.                     Debug.DrawLine(vertex.prev.position, vertex.position, Color.red);
  623.                     throw new System.ArgumentException("Obstacle has more vertices than supplied for updating (" + vertices.Length+ " supplied)");
  624.                 }
  625.  
  626.                 // Premature optimization ftw!
  627.                 vertex.position = identity ? vertices[count] : matrix.MultiplyPoint3x4(vertices[count]);
  628.                 vertex = vertex.next;
  629.                 count++;
  630.             } while (vertex != obstacle && vertex != null);
  631.  
  632.             vertex = obstacle;
  633.             do {
  634.                 if (vertex.next == null) {
  635.                     vertex.dir = Vector2.zero;
  636.                 } else {
  637.                     Vector3 dir = vertex.next.position - vertex.position;
  638.                     vertex.dir = new Vector2(dir.x, dir.z).normalized;
  639.                 }
  640.  
  641.                 vertex = vertex.next;
  642.             } while (vertex != obstacle && vertex != null);
  643.  
  644.             ScheduleCleanObstacles();
  645.             UpdateObstacles();
  646.         }
  647.  
  648.         private void ScheduleCleanObstacles () {
  649.             doCleanObstacles = true;
  650.         }
  651.  
  652.         private void CleanObstacles () {
  653.         }
  654.  
  655.         /// <summary>
  656.         /// Removes the obstacle identified by the vertex.
  657.         /// This must be the same vertex as the one returned by the AddObstacle call.
  658.         ///
  659.         /// See: AddObstacle
  660.         /// </summary>
  661.         public void RemoveObstacle (ObstacleVertex v) {
  662.             if (v == null) throw new System.ArgumentNullException("Vertex must not be null");
  663.  
  664.             // Don't interfere with ongoing calculations
  665.             BlockUntilSimulationStepIsDone();
  666.  
  667.             obstacles.Remove(v);
  668.             UpdateObstacles();
  669.         }
  670.  
  671.         /// <summary>
  672.         /// Rebuilds the obstacle tree at next simulation frame.
  673.         /// Add and remove obstacle functions call this automatically.
  674.         /// </summary>
  675.         public void UpdateObstacles () {
  676.             // Update obstacles at next frame
  677.             doUpdateObstacles = true;
  678.         }
  679.  
  680.         void BuildQuadtree () {
  681.             Quadtree.Clear();
  682.             if (agents.Count > 0) {
  683.                 Rect bounds = Rect.MinMaxRect(agents[0].position.x, agents[0].position.y, agents[0].position.x, agents[0].position.y);
  684.                 for (int i = 1; i < agents.Count; i++) {
  685.                     Vector2 p = agents[i].position;
  686.                     bounds = Rect.MinMaxRect(Mathf.Min(bounds.xMin, p.x), Mathf.Min(bounds.yMin, p.y), Mathf.Max(bounds.xMax, p.x), Mathf.Max(bounds.yMax, p.y));
  687.                 }
  688.                 Quadtree.SetBounds(bounds);
  689.  
  690.                 for (int i = 0; i < agents.Count; i++) {
  691.                     Quadtree.Insert(agents[i]);
  692.                 }
  693.  
  694.                 //quadtree.DebugDraw ();
  695.             }
  696.  
  697.             Quadtree.CalculateSpeeds();
  698.         }
  699.  
  700.         /// <summary>
  701.         /// Blocks until separate threads have finished with the current simulation step.
  702.         /// When double buffering is done, the simulation is performed in between frames.
  703.         /// </summary>
  704.         void BlockUntilSimulationStepIsDone () {
  705.             if (Multithreading && doubleBuffering) for (int j = 0; j < workers.Length; j++) workers[j].WaitOne();
  706.         }
  707.  
  708.         private WorkerContext coroutineWorkerContext = new WorkerContext();
  709.  
  710.         void PreCalculation () {
  711.             for (int i = 0; i < agents.Count; i++) agents[i].PreCalculation();
  712.         }
  713.  
  714.         void CleanAndUpdateObstaclesIfNecessary () {
  715.             if (doCleanObstacles) {
  716.                 CleanObstacles();
  717.                 doCleanObstacles = false;
  718.                 doUpdateObstacles = true;
  719.             }
  720.  
  721.             if (doUpdateObstacles) {
  722.                 doUpdateObstacles = false;
  723.             }
  724.         }
  725.  
  726.         /// <summary>Should be called once per frame</summary>
  727.         public void Update () {
  728.             // Initialize last step
  729.             if (lastStep < 0) {
  730.                 lastStep = Time.time;
  731.                 deltaTime = DesiredDeltaTime;
  732.             }
  733.  
  734.             if (Time.time - lastStep >= DesiredDeltaTime) {
  735.                 deltaTime = Time.time - lastStep;
  736.                 lastStep = Time.time;
  737.  
  738.                 // Prevent a zero delta time
  739.                 deltaTime = System.Math.Max(deltaTime, 1.0f/2000f);
  740.  
  741.                 if (Multithreading) {
  742.                     Profiler.BeginSample("Pre-wait");
  743.                     // Make sure the threads have completed their tasks
  744.                     // Otherwise block until they have
  745.                     if (doubleBuffering) {
  746.                         for (int i = 0; i < workers.Length; i++) workers[i].WaitOne();
  747.                         for (int i = 0; i < agents.Count; i++) agents[i].PostCalculation();
  748.                     }
  749.                     Profiler.EndSample();
  750.                     Profiler.BeginSample("Pre-calculation");
  751.                     PreCalculation();
  752.                     Profiler.EndSample();
  753.                     Profiler.BeginSample("Update obstacles");
  754.                     CleanAndUpdateObstaclesIfNecessary();
  755.                     Profiler.EndSample();
  756.                     Profiler.BeginSample("Build Quadtree");
  757.                     BuildQuadtree();
  758.                     Profiler.EndSample();
  759.  
  760.                     Profiler.BeginSample("Setup workers");
  761.                     for (int i = 0; i < workers.Length; i++) {
  762.                         workers[i].start = i*agents.Count / workers.Length;
  763.                         workers[i].end = (i+1)*agents.Count / workers.Length;
  764.                     }
  765.                     Profiler.EndSample();
  766.                     Profiler.BeginSample("Buffer switch");
  767.                     // BufferSwitch
  768.                     for (int i = 0; i < workers.Length; i++) workers[i].Execute(1);
  769.                     Profiler.EndSample();
  770.                     Profiler.BeginSample("Buffer switch wait");
  771.                     for (int i = 0; i < workers.Length; i++) workers[i].WaitOne();
  772.                     Profiler.EndSample();
  773.  
  774.                     Profiler.BeginSample("Calculate velocity");
  775.                     // Calculate New Velocity
  776.                     for (int i = 0; i < workers.Length; i++) workers[i].Execute(0);
  777.  
  778.                     // Make sure the threads have completed their tasks
  779.                     // Otherwise block until they have
  780.                     if (!doubleBuffering) {
  781.                         for (int i = 0; i < workers.Length; i++) workers[i].WaitOne();
  782.                         Profiler.EndSample();
  783.                         Profiler.BeginSample("Post calculation");
  784.                         for (int i = 0; i < agents.Count; i++) agents[i].PostCalculation();
  785.                         Profiler.EndSample();
  786.                     }
  787.                 } else {
  788.                     PreCalculation();
  789.                     CleanAndUpdateObstaclesIfNecessary();
  790.                     BuildQuadtree();
  791.  
  792.                     for (int i = 0; i < agents.Count; i++) {
  793.                         agents[i].BufferSwitch();
  794.                     }
  795.  
  796.                     for (int i = 0; i < agents.Count; i++) {
  797.                         agents[i].CalculateNeighbours();
  798.                         agents[i].CalculateVelocity(coroutineWorkerContext);
  799.                     }
  800.  
  801.                     for (int i = 0; i < agents.Count; i++) agents[i].PostCalculation();
  802.                 }
  803.             }
  804.         }
  805.  
  806.         internal class WorkerContext {
  807.             public Agent.VOBuffer vos = new Agent.VOBuffer(16);
  808.  
  809.             public const int KeepCount = 3;
  810.             public Vector2[] bestPos = new Vector2[KeepCount];
  811.             public float[] bestSizes = new float[KeepCount];
  812.             public float[] bestScores = new float[KeepCount+1];
  813.  
  814.             public Vector2[] samplePos = new Vector2[50];
  815.             public float[] sampleSize = new float[50];
  816.         }
  817.  
  818.         /// <summary>Worker thread for RVO simulation</summary>
  819.         class WorkerSpecialName {
  820.             public int start, end;
  821.             readonly ManualResetEventSlim runFlag = new ManualResetEventSlim(false);
  822.             readonly ManualResetEventSlim waitFlag = new ManualResetEventSlim(true);
  823.             readonly Simulator simulator;
  824.             int task = 0;
  825.             bool terminate = false;
  826.             WorkerContext context = new WorkerContext();
  827.  
  828.             public WorkerSpecialName (Simulator sim) {
  829.                 this.simulator = sim;
  830.                 var thread = new Thread(new ThreadStart(Run));
  831.                 thread.IsBackground = true;
  832.                 thread.Name = "RVO Simulator Thread";
  833.                 thread.Start();
  834.             }
  835.  
  836.             public void Execute (int task) {
  837.                 this.task = task;
  838.                 waitFlag.Reset();
  839.                 runFlag.Set();
  840.             }
  841.  
  842.             public void WaitOne () {
  843.                 if (!terminate) {
  844.                     waitFlag.Wait();
  845.                 }
  846.             }
  847.  
  848.             public void Terminate () {
  849.                 WaitOne();
  850.                 terminate = true;
  851.                 Execute(-1);
  852.             }
  853.  
  854.             public void Run () {
  855.                 runFlag.Wait();
  856.                 runFlag.Reset();
  857.  
  858.                 while (!terminate) {
  859.                     try {
  860.                         List<Agent> agents = simulator.GetAgents();
  861.                         if (task == 0) {
  862.                             for (int i = start; i < end; i++) {
  863.                                 agents[i].CalculateNeighbours();
  864.                                 agents[i].CalculateVelocity(context);
  865.                             }
  866.                         } else if (task == 1) {
  867.                             for (int i = start; i < end; i++) {
  868.                                 agents[i].BufferSwitch();
  869.                             }
  870.                         } else if (task == 2) {
  871.                             simulator.BuildQuadtree();
  872.                         } else {
  873.                             Debug.LogError("Invalid Task Number: " + task);
  874.                             throw new System.Exception("Invalid Task Number: " + task);
  875.                         }
  876.                     } catch (System.Exception e) {
  877.                         Debug.LogError(e);
  878.                     }
  879.                     waitFlag.Set();
  880.                     runFlag.Wait();
  881.                     runFlag.Reset();
  882.                 }
  883.             }
  884.         }
  885.     }
  886. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement