Guest User

RVOCoreSimulator.cs

a guest
Sep 30th, 2014
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 29.19 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. using Pathfinding;
  6. using Pathfinding.RVO.Sampled;
  7.  
  8. #if NETFX_CORE
  9. using Thread = Pathfinding.WindowsStore.Thread;
  10. using ParameterizedThreadStart = Pathfinding.WindowsStore.ParameterizedThreadStart;
  11. using ThreadStart = Pathfinding.WindowsStore.ThreadStart;
  12. #else
  13. using Thread = System.Threading.Thread;
  14. using ParameterizedThreadStart = System.Threading.ParameterizedThreadStart;
  15. using ThreadStart = System.Threading.ThreadStart;
  16. #endif
  17.  
  18. /** Local avoidance related classes */
  19. namespace Pathfinding.RVO {
  20.  
  21.     /** Exposes properties of an Agent class.
  22.       * \astarpro */
  23.     public interface IAgent {
  24.         /** Interpolated position of agent.
  25.          * Will be interpolated if the interpolation setting is enabled on the simulator.
  26.          */
  27.         Vector3 InterpolatedPosition {get;}
  28.        
  29.         /** Position of the agent.
  30.          * This can be changed manually if you need to reposition the agent, but if you are reading from InterpolatedPosition, it will not update interpolated position
  31.          * until the next simulation step.
  32.          * \see Position
  33.          */
  34.         Vector3 Position {get; set;}
  35.        
  36.         /** Desired velocity of the agent.
  37.          * Usually you set this once per frame. The agent will try move as close to the desired velocity as possible.
  38.          * Will take effect at the next simulation step.
  39.          */
  40.         Vector3 DesiredVelocity {get; set;}
  41.         /** Velocity of the agent.
  42.          * Can be used to set the rotation of the rendered agent.
  43.          * But smoothing is recommended if you do so since it might be a bit unstable when the velocity is very low.
  44.          *
  45.          * You can set this variable manually,but it is not recommended to do so unless
  46.          * you have good reasons since it might degrade the quality of the simulation.
  47.          */
  48.         Vector3 Velocity {get; set;}
  49.        
  50.         /** Locked agents will not move */
  51.         bool Locked {get; set;}
  52.        
  53.         /** Radius of the agent.
  54.          * Agents are modelled as circles/cylinders */
  55.         float Radius {get; set;}
  56.        
  57.         /** Height of the agent */
  58.         float Height {get; set;}
  59.        
  60.         /** Max speed of the agent. In units per second  */
  61.         float MaxSpeed {get; set;}
  62.        
  63.         /** Max distance to other agents to take them into account.
  64.          * Decreasing this value can lead to better performance, increasing it can lead to better quality of the simulation.
  65.          */
  66.         float NeighbourDist {get; set;}
  67.        
  68.         /** Max number of estimated seconds to look into the future for collisions with agents.
  69.           * As it turns out, this variable is also very good for controling agent avoidance priorities.
  70.           * Agents with lower values will avoid other agents less and thus you can make 'high priority agents' by
  71.           * giving them a lower value.
  72.           */
  73.         float AgentTimeHorizon {get; set;}
  74.         /** Max number of estimated seconds to look into the future for collisions with obstacles */
  75.         float ObstacleTimeHorizon {get; set;}
  76.  
  77.         /** Specifies the avoidance layer for this agent.
  78.          * The #CollidesWith mask on other agents will determine if they will avoid this agent.
  79.          */
  80.         RVOLayer Layer {get; set;}
  81.  
  82.         /** Layer mask specifying which layers this agent will avoid.
  83.          * You can set it as CollidesWith = RVOLayer.DefaultAgent | RVOLayer.Layer3 | RVOLayer.Layer6 ...
  84.          *
  85.          * \see http://en.wikipedia.org/wiki/Mask_(computing)
  86.          */
  87.         RVOLayer CollidesWith {get; set;}
  88.  
  89.         /** Debug drawing */
  90.         bool DebugDraw {get; set;}
  91.        
  92.         /** Max number of agents to take into account.
  93.          * Decreasing this value can lead to better performance, increasing it can lead to better quality of the simulation.
  94.          */
  95.         int MaxNeighbours {get; set;}
  96.        
  97.         /** List of obstacle segments which were close to the agent during the last simulation step.
  98.          * Can be used to apply additional wall avoidance forces for example.
  99.          * Segments are formed by the obstacle vertex and its .next property.
  100.          */
  101.         List<ObstacleVertex> NeighbourObstacles {get; }
  102.        
  103.         /** Teleports the agent to a new position.
  104.          * Just setting the position can cause strange effects when using interpolation.
  105.          */
  106.         void Teleport (Vector3 pos);
  107.  
  108.  
  109.     }
  110.  
  111.     [System.Flags]
  112.     public enum RVOLayer {
  113.         DefaultAgent = 1 << 0,
  114.         DefaultObstacle = 1 << 1,
  115.         Layer2 = 1 << 2,
  116.         Layer3 = 1 << 3,
  117.         Layer4 = 1 << 4,
  118.         Layer5 = 1 << 5,
  119.         Layer6 = 1 << 6,
  120.         Layer7 = 1 << 7,
  121.         Layer8 = 1 << 8,
  122.         Layer9 = 1 << 9,
  123.         Layer10 = 1 << 10,
  124.         Layer11 = 1 << 11,
  125.         Layer12 = 1 << 12,
  126.         Layer13 = 1 << 13,
  127.         Layer14 = 1 << 14,
  128.         Layer15 = 1 << 15,
  129.         Layer16 = 1 << 16,
  130.         Layer17 = 1 << 17,
  131.         Layer18 = 1 << 18,
  132.         Layer19 = 1 << 19,
  133.         Layer20 = 1 << 20,
  134.         Layer21 = 1 << 21,
  135.         Layer22 = 1 << 22,
  136.         Layer23 = 1 << 23,
  137.         Layer24 = 1 << 24,
  138.         Layer25 = 1 << 25,
  139.         Layer26 = 1 << 26,
  140.         Layer27 = 1 << 27,
  141.         Layer28 = 1 << 28,
  142.         Layer29 = 1 << 29,
  143.         Layer30 = 1 << 30
  144.     }
  145.  
  146.     /** Local Avoidance %Simulator.
  147.      * This class handles local avoidance simulation for a number of agents using
  148.      * Reciprocal Velocity Obstacles (RVO) and Optimal Reciprocal Collision Avoidance (ORCA).
  149.      *
  150.      * This class will handle calculation of velocities from desired velocities supplied by a script.
  151.      * It is, however, not responsible for moving any objects in a Unity Scene. For that there are other scripts (see below).
  152.      *
  153.      * Obstacles can be added and removed from the simulation, agents can also be added and removed at any time.
  154.      * \see
  155.      * RVOSimulator
  156.      * RVOAgent
  157.      * Pathfinding.RVO.IAgent
  158.      *
  159.      * The implementation is based on the RVO2 Library (http://gamma.cs.unc.edu/RVO2/) extended with many new features.
  160.      *
  161.      * You will most likely mostly use the wrapper class RVOSimulator.
  162.      *
  163.      * \astarpro
  164.      */
  165.     public class Simulator {
  166.        
  167.         /** Use Double Buffering.
  168.          * \see DoubleBuffering */
  169.         private bool doubleBuffering = true;
  170.        
  171.         /** Inverse desired simulation fps.
  172.          * \see DesiredDeltaTime
  173.          */
  174.         private float desiredDeltaTime = 0.05f;
  175.        
  176.         /** Use Interpolation.
  177.          * \see Interpolation */
  178.         private bool interpolation = true;
  179.        
  180.         /** Worker threads */
  181.         Worker[] workers;
  182.        
  183.         /** Agents in this simulation */
  184.         List<Agent> agents;
  185.        
  186.         /** Obstacles in this simulation */
  187.         List<ObstacleVertex> obstacles;
  188.  
  189.         public enum SamplingAlgorithm {
  190.             AdaptiveSampling,
  191.             GradientDecent
  192.         }
  193.  
  194.         /** What sampling algorithm to use.
  195.          * \see "Reciprocal Velocity Obstacles for Real-Time Multi-Agent Navigation"
  196.          * \see https://en.wikipedia.org/wiki/Gradient_descent
  197.          * \see http://digestingduck.blogspot.se/2010/04/adaptive-rvo-sampling.html
  198.          * \see http://digestingduck.blogspot.se/2010/10/rvo-sample-pattern.html
  199.           */
  200.         public SamplingAlgorithm algorithm = SamplingAlgorithm.AdaptiveSampling;
  201.  
  202. #if !AstarRelease
  203.         /** KDTree for this simulation */
  204.         KDTree kdTree;
  205. #endif
  206.  
  207.         RVOQuadtree quadtree = new RVOQuadtree();
  208.  
  209.         public float qualityCutoff = 0.05f;
  210.         public float stepScale = 1.5f;
  211.  
  212. #if !AstarRelease
  213.         /** KDTree for this simulation.
  214.          * Used internally by the simulation.
  215.          * Please only read from this tree, do not rebuild it since that can interfere with the simulation.
  216.          * It is rebuilt when needed.
  217.          *
  218.          */
  219.         public KDTree KDTree { get { return kdTree; } }
  220. #endif
  221.  
  222.         /** Quadtree for this simulation.
  223.          * Used internally by the simulation to perform fast neighbour lookups for each agent.
  224.          * Please only read from this tree, do not rebuild it since that can interfere with the simulation.
  225.          * It is rebuilt when needed.
  226.          */
  227.         public RVOQuadtree Quadtree { get { return quadtree; } }
  228.  
  229.         private float deltaTime;
  230.         private float prevDeltaTime = 0;
  231.        
  232.         private float lastStep = -99999;
  233.         private float lastStepInterpolationReference = -9999;
  234.         private float lastFrame = 0;
  235.        
  236.         private bool doUpdateObstacles = false;
  237.         private bool doCleanObstacles = false;
  238.  
  239.         private bool oversampling = false;
  240.  
  241.         private int frameTimeBufferIndex = 0;
  242.         private float[] frameTimeBuffer = new float[1];
  243.  
  244.         public float DeltaTime { get { return deltaTime; } }
  245.         public float PrevDeltaTime { get { return prevDeltaTime; } }
  246.        
  247.         /** Is using multithreading */
  248.         public bool Multithreading { get { return workers != null && workers.Length > 0; }}
  249.        
  250.         /** Time in seconds between each simulation step.
  251.          * This is the desired delta time, the simulation will never run at a higher fps than
  252.          * the rate at which the Update function is called.
  253.          */
  254.         public float DesiredDeltaTime { get { return desiredDeltaTime; } set { desiredDeltaTime = System.Math.Max (value,0.0f); }}
  255.        
  256.         /** Use Interpolation.
  257.          * If interpolation is enabled, agent positions will be interpolated on frames when no rvo calculation is done.
  258.          * This has a very small overhead, but usually yields much smoother looking movement.
  259.          */
  260.         public bool Interpolation { get { return interpolation; } set { interpolation = value; }}
  261.  
  262.         public bool Oversampling { get { return oversampling; } set { oversampling = value; }}
  263.  
  264.         //internal int textureWidth;
  265.         //internal Texture2D tex;
  266.         //internal float textureSize;
  267.         //internal float colorScale = 0.05f;
  268.         //Color[] colors;
  269.        
  270.         //bool dirtyColors = false;
  271.  
  272.         /** Get a list of all agents.
  273.          *
  274.          * This is an internal list.
  275.          * I'm not going to be restrictive so you may access it since it is better for performance
  276.          * but please do not modify it since that can cause errors in the simulation.
  277.          *
  278.          * \warning Do not modify this list! */
  279.         public List<Agent> GetAgents () {
  280.             return agents;
  281.         }
  282.        
  283.         /** Get a list of all obstacles.
  284.          * This is a list of obstacle vertices.
  285.          * Each vertex is part of a doubly linked list loop
  286.          * forming an obstacle polygon.
  287.          *
  288.          * \warning Do not modify this list!
  289.          *
  290.          * \see AddObstacle
  291.          * \see RemoveObstacle
  292.          */
  293.         public List<ObstacleVertex> GetObstacles () {
  294.             return obstacles;
  295.         }
  296.        
  297.         /** Create a new simulator.
  298.          *
  299.          * \param workers Use the specified number of worker threads.\n
  300.          * When the number zero is specified, no multithreading will be used.
  301.          * A good number is the number of cores on the machine.
  302.          *
  303.          * \param doubleBuffering Use Double Buffering for calculations.
  304.          * Testing done with 5000 agents and 0.1 desired delta time showed that with double buffering enabled
  305.          * the game ran at 50 fps for most frames, dropping to 10 fps during calculation frames. But without double buffering
  306.          * it ran at around 10 fps all the time.\n
  307.          * This will let threads calculate while the game progresses instead of waiting for the calculations
  308.          * to finish.
  309.          * \note Will only have effect if using multithreading
  310.          *
  311.          * \see #Multithreading
  312.          */
  313.         public Simulator (int workers, bool doubleBuffering) {
  314.             this.workers = new Simulator.Worker[workers];
  315.             this.doubleBuffering = doubleBuffering;
  316.            
  317.             for (int i=0;i<workers;i++) this.workers[i] = new Simulator.Worker(this);
  318.            
  319.             //kdTree = new KDTree(this);
  320.             agents = new List<Agent>();
  321.             obstacles = new List<ObstacleVertex>();
  322.            
  323.         }
  324.  
  325.         /*internal void DebugPlot ( Vector2 p, Color col ) {
  326.             if ( colors == null ) {
  327.                 tex = new Texture2D(textureWidth,textureWidth);
  328.                 //mat.mainTexture = tex;
  329.                 colors = new Color[tex.width*tex.height];
  330.             }
  331.  
  332.             int x = Mathf.RoundToInt (p.x*tex.width/textureSize);
  333.             int y = Mathf.RoundToInt (p.y*tex.height/textureSize);
  334.            
  335.             if ( x >= 0 && y >= 0 && x < tex.width && y < tex.height ) {
  336.                 dirtyColors = true;
  337.                 colors[x+y*tex.width] = col;
  338.             }
  339.         }*/
  340.  
  341.         /** Removes all agents from the simulation */
  342.         public void ClearAgents () {
  343.            
  344.             //Bad to update agents while processing of current agents might be done
  345.             //Don't interfere with ongoing calculations
  346.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  347.            
  348.             for (int i=0;i<agents.Count;i++) {
  349.                 agents[i].simulator = null;
  350.             }
  351.             agents.Clear ();
  352.  
  353. #if !AstarRelease
  354.             if (kdTree != null ) kdTree.RebuildAgents ();
  355. #endif
  356.         }
  357.        
  358.         public void OnDestroy () {
  359.             if (workers != null) {
  360.                 for (int i=0;i<workers.Length;i++) workers[i].Terminate ();
  361.             }
  362.         }
  363.        
  364.         /** Terminates any worker threads */
  365.         ~Simulator () {
  366.             OnDestroy ();
  367.         }
  368.        
  369.         /** Add a previously removed agent to the simulation.
  370.           * An agent can only be in one simulation at a time, any attempt to add an agent to two simulations
  371.           * or multiple times to the same simulation will result in an exception being thrown.
  372.           *
  373.           * \see RemoveAgent
  374.           */
  375.         public IAgent AddAgent (IAgent agent) {
  376.             if (agent == null) throw new System.ArgumentNullException ("Agent must not be null");
  377.            
  378.             Agent agentReal = agent as Agent;
  379.             if (agentReal == null) throw new System.ArgumentException ("The agent must be of type Agent. Agent was of type "+agent.GetType ());
  380.            
  381.            
  382.             if (agentReal.simulator != null && agentReal.simulator == this) throw new System.ArgumentException ("The agent is already in the simulation");
  383.             else if (agentReal.simulator != null) throw new System.ArgumentException ("The agent is already added to another simulation");
  384.             agentReal.simulator = this;
  385.            
  386.             //Don't interfere with ongoing calculations
  387.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  388.            
  389.             agents.Add (agentReal);
  390.  
  391. #if !AstarRelease
  392.             if ( kdTree != null ) kdTree.RebuildAgents ();
  393. #endif         
  394.            
  395.             return agent;
  396.         }
  397.        
  398.         /** Add an agent at the specified position.
  399.          * You can use the returned interface to read several parameters such as position and velocity
  400.          * and set for example radius and desired velocity.
  401.          *
  402.          * \see RemoveAgent
  403.          */
  404.         public IAgent AddAgent (Vector3 position) {
  405.            
  406.             Agent agent = new Agent (position);
  407.            
  408.             //Don't interfere with ongoing calculations
  409.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  410.            
  411.             agents.Add (agent);
  412. #if !AstarRelease
  413.             if ( kdTree != null ) kdTree.RebuildAgents ();
  414. #endif         
  415.             agent.simulator = this;
  416.            
  417.             return agent;
  418.         }
  419.        
  420.         /** Removes a specified agent from this simulation.
  421.          * The agent can be added again later by using AddAgent.
  422.          *
  423.          * \see AddAgent(IAgent)
  424.          * \see ClearAgents
  425.          */
  426.         public void RemoveAgent (IAgent agent) {
  427.             if (agent == null) throw new System.ArgumentNullException ("Agent must not be null");
  428.            
  429.             Agent agentReal = agent as Agent;
  430.             if (agentReal == null) throw new System.ArgumentException ("The agent must be of type Agent. Agent was of type "+agent.GetType ());
  431.            
  432.             if (agentReal.simulator != this) throw new System.ArgumentException ("The agent is not added to this simulation");
  433.            
  434.             //Don't interfere with ongoing calculations
  435.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  436.            
  437.             agentReal.simulator = null;
  438.            
  439.             if (!agents.Remove (agentReal)) {
  440.                 throw new System.ArgumentException ("Critical Bug! This should not happen. Please report this.");
  441.             }
  442.         }
  443.        
  444.         /** Adds a previously removed obstacle.
  445.          * This does not check if the obstacle is already added to the simulation, so please do not add an obstacle multiple times.
  446.          *
  447.          * It is assumed that this is a valid obstacle.
  448.          */
  449.         public ObstacleVertex AddObstacle (ObstacleVertex v) {
  450.             if (v == null) throw new System.ArgumentNullException ("Obstacle must not be null");
  451.            
  452.             //Don't interfere with ongoing calculations
  453.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  454.            
  455.             obstacles.Add (v);
  456.             UpdateObstacles ();
  457.             return v;
  458.         }
  459.        
  460.         /** Adds an obstacle described by the vertices.
  461.          *
  462.          * \see RemoveObstacle
  463.          */
  464.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height) {
  465.            
  466.             return AddObstacle (vertices, height, Matrix4x4.identity);
  467.            
  468.             /*if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  469.            
  470.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  471.            
  472.             ObstacleVertex first = null;
  473.             ObstacleVertex prev = null;
  474.            
  475.             for (int i=0;i<vertices.Length;i++) {
  476.                 ObstacleVertex v = new ObstacleVertex();
  477.                 if (first == null) first = v;
  478.                 else prev.next = v;
  479.                
  480.                 v.prev = prev;
  481.                 v.position = vertices[i];
  482.                 //v.thin = thin;
  483.                 v.height = height;
  484.                 prev = v;
  485.             }
  486.            
  487.             prev.next = first;
  488.             first.prev = prev;
  489.            
  490.             ObstacleVertex c = first;
  491.             do {
  492.                 Vector3 dir = c.next.position - c.position;
  493.                 v.dir =  new Vector2 (dir.x,dir.z).normalized;
  494.                
  495.                 if (vertices.Length == 2) {
  496.                     v.convex = true;
  497.                 } else {
  498.                     v.convex = Polygon.IsClockwiseMargin (c.prev.position,c.position, c.next.position);
  499.                 }
  500.                
  501.                 c = c.next;
  502.             } while (c != first);
  503.            
  504.             obstacles.Add (first);
  505.            
  506.             UpdateObstacles ();
  507.             return first;*/
  508.         }
  509.        
  510.         /** Adds an obstacle described by the vertices.
  511.          *
  512.          * \see RemoveObstacle
  513.          */
  514.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height, Matrix4x4 matrix) {
  515.            
  516.             if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  517.            
  518.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  519.            
  520.             ObstacleVertex first = null;
  521.             ObstacleVertex prev = null;
  522.            
  523.             bool identity = matrix == Matrix4x4.identity;
  524.            
  525.             //Don't interfere with ongoing calculations
  526.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  527.            
  528.             for (int i=0;i<vertices.Length;i++) {
  529.                 ObstacleVertex v = new ObstacleVertex();
  530.                 if (first == null) first = v;
  531.                 else prev.next = v;
  532.                
  533.                 v.prev = prev;
  534.                
  535.                 //Premature optimization ftw!
  536.                 v.position = identity ? vertices[i] : matrix.MultiplyPoint3x4(vertices[i]);
  537.                
  538.                 //v.thin = thin;
  539.                 v.height = height;
  540.                
  541.                 prev = v;
  542.             }
  543.            
  544.             prev.next = first;
  545.             first.prev = prev;
  546.            
  547.             ObstacleVertex c = first;
  548.             do {
  549.                 Vector3 dir = c.next.position - c.position;
  550.                 c.dir = new Vector2 (dir.x,dir.z).normalized;
  551.                
  552.                 if (vertices.Length == 2) {
  553.                     c.convex = true;
  554.                 } else {
  555.                     c.convex = Polygon.IsClockwiseMargin (c.next.position,c.position, c.prev.position);
  556.                 }
  557.                
  558.                 c = c.next;
  559.             } while (c != first);
  560.            
  561.             obstacles.Add (first);
  562.            
  563.             UpdateObstacles ();
  564.             return first;
  565.         }
  566.        
  567.         /**
  568.          * Adds a line obstacle with a specified height.
  569.          *
  570.          * \see RemoveObstacle
  571.          */
  572.         public ObstacleVertex AddObstacle (Vector3 a, Vector3 b, float height) {
  573.             ObstacleVertex first = new ObstacleVertex ();
  574.             ObstacleVertex second = new ObstacleVertex ();
  575.            
  576.             first.prev = second;
  577.             second.prev = first;
  578.             first.next = second;
  579.             second.next = first;
  580.            
  581.             first.position = a;
  582.             second.position = b;
  583.             first.height = height;
  584.             second.height = height;
  585.            
  586.             first.convex = true;
  587.             second.convex = true;
  588.            
  589.             first.dir = new Vector2 (b.x-a.x,b.z-a.z).normalized;
  590.             second.dir = -first.dir;
  591.            
  592.             //Don't interfere with ongoing calculations
  593.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  594.            
  595.             obstacles.Add (first);
  596.            
  597.             UpdateObstacles ();
  598.             return first;
  599.         }
  600.        
  601.         /** Updates the vertices of an obstacle.
  602.          * \param obstacle %Obstacle to update
  603.          * \param vertices New vertices for the obstacle, must have at least the number of vertices in the original obstacle
  604.          * \param matrix %Matrix to multiply vertices with before updating obstacle
  605.          *
  606.          * The number of vertices in an obstacle cannot be changed, existing vertices can only be moved.
  607.          */
  608.         public void UpdateObstacle (ObstacleVertex obstacle, Vector3[] vertices, Matrix4x4 matrix) {
  609.            
  610.             if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  611.             if (obstacle == null) throw new System.ArgumentNullException ("Obstacle must not be null");
  612.            
  613.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  614.            
  615.             if (obstacle.split) throw new System.ArgumentException ("Obstacle is not a start vertex. You should only pass those ObstacleVertices got from AddObstacle method calls");
  616.            
  617.             //Don't interfere with ongoing calculations
  618.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  619.            
  620.             //Compact obstacle and count
  621.             int count = 0;
  622.            
  623.             ObstacleVertex c = obstacle;
  624.             do {
  625.                 while (c.next.split) {
  626.                     c.next = c.next.next;
  627.                     c.next.prev = c;
  628.                 }
  629.                
  630.                 if (count >= vertices.Length) {
  631.                     Debug.DrawLine (c.prev.position, c.position,Color.red);
  632.                     throw new System.ArgumentException ("Obstacle has more vertices than supplied for updating (" + vertices.Length+ " supplied)");
  633.                 }
  634.                 c.position = matrix.MultiplyPoint3x4 (vertices[count]);
  635.                 count++;
  636.                 c = c.next;
  637.             } while (c != obstacle);
  638.            
  639.             c = obstacle;
  640.             do {
  641.                 Vector3 dir = c.next.position - c.position;
  642.                 c.dir =  new Vector2 (dir.x,dir.z).normalized;
  643.                
  644.                 if (vertices.Length == 2) {
  645.                     c.convex = true;
  646.                 } else {
  647.                     c.convex = Polygon.IsClockwiseMargin (c.next.position,c.position, c.prev.position);
  648.                 }
  649.                
  650.                 c = c.next;
  651.             } while (c != obstacle);
  652.            
  653.             ScheduleCleanObstacles ();
  654.             UpdateObstacles();
  655.         }
  656.        
  657.         private void ScheduleCleanObstacles () {
  658.             doCleanObstacles = true;
  659.         }
  660.        
  661.         private void CleanObstacles () {
  662.            
  663.             for (int i=0;i<obstacles.Count;i++) {
  664.                 ObstacleVertex first = obstacles[i];
  665.                 ObstacleVertex c = first;
  666.                 do {
  667.                     while (c.next.split) {
  668.                         c.next = c.next.next;
  669.                         c.next.prev = c;
  670.                     }
  671.                     c = c.next;
  672.                 } while (c != first);
  673.             }
  674.         }
  675.        
  676.         /** Removes the obstacle identified by the vertex.
  677.           * This must be the same vertex as the one returned by the AddObstacle call.
  678.           *
  679.           * \see AddObstacle
  680.           */
  681.         public void RemoveObstacle (ObstacleVertex v) {
  682.             if (v == null) throw new System.ArgumentNullException ("Vertex must not be null");
  683.            
  684.             //Don't interfere with ongoing calculations
  685.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  686.            
  687.             obstacles.Remove (v);
  688.             UpdateObstacles ();
  689.         }
  690.        
  691.         /** Rebuilds the obstacle tree at next simulation frame.
  692.          * Add and remove obstacle functions call this automatically.
  693.          */
  694.         public void UpdateObstacles () {
  695.             //Update obstacles at next frame
  696.             doUpdateObstacles = true;
  697.         }
  698.  
  699.         void BuildQuadtree () {
  700.             quadtree.Clear ();
  701.             if ( agents.Count > 0 ) {
  702.                 Rect bounds = Rect.MinMaxRect (agents[0].position.x, agents[0].position.y, agents[0].position.x, agents[0].position.y);
  703.                 for ( int i = 1; i < agents.Count; i++ ) {
  704.                     Vector3 p = agents[i].position;
  705.                     bounds = Rect.MinMaxRect ( Mathf.Min(bounds.xMin, p.x), Mathf.Min(bounds.yMin, p.z), Mathf.Max(bounds.xMax, p.x), Mathf.Max(bounds.yMax, p.z) );
  706.                 }
  707.                 quadtree.SetBounds (bounds);
  708.  
  709.                 for (int i=0;i<agents.Count;i++) {
  710.                     quadtree.Insert (agents[i]);
  711.                 }
  712.                
  713.                 //quadtree.DebugDraw ();
  714.             }
  715.         }
  716.  
  717.         private WorkerContext coroutineWorkerContext = new WorkerContext();
  718.  
  719.         /** Should be called once per frame */
  720.         public void Update () {
  721.            
  722.             //Initialize last step
  723.             if (lastStep < 0) {
  724.                 lastStep = Time.time;
  725.                 deltaTime = DesiredDeltaTime;
  726.                 lastStepInterpolationReference = lastStep;
  727.             }
  728.            
  729.             if (Time.time - lastStep >= DesiredDeltaTime) {
  730.                
  731.                 prevDeltaTime = DeltaTime;
  732.                 deltaTime = Time.time - lastStep;
  733.                 lastStep = Time.time;
  734.                
  735.                 frameTimeBufferIndex++;
  736.                 frameTimeBufferIndex %= frameTimeBuffer.Length;
  737.                 frameTimeBuffer[frameTimeBufferIndex] = deltaTime;
  738.  
  739.                 // Implements averaging of delta times
  740.                 // Disabled for now because it seems to have caused more issues than it solved
  741.                 // Might re-enable later
  742.                 /*float sum = 0;
  743.                 float mn = float.PositiveInfinity;
  744.                 float mx = float.NegativeInfinity;
  745.                 for (int i=0;i<frameTimeBuffer.Length;i++) {
  746.                     sum += frameTimeBuffer[i];
  747.                     mn = Mathf.Min (mn, frameTimeBuffer[i]);
  748.                     mx = Mathf.Max (mx, frameTimeBuffer[i]);
  749.                 }
  750.                 sum -= mn;
  751.                 sum -= mx;
  752.                 sum /= (frameTimeBuffer.Length-2);
  753.                 sum = frame
  754.                 deltaTime = sum;*/
  755.                
  756.                 //Calculate smooth delta time
  757.                 //Disabled because it seemed to cause more problems than it solved
  758.                 //deltaTime = (Time.time - frameTimeBuffer[(frameTimeBufferIndex-1+frameTimeBuffer.Length)%frameTimeBuffer.Length]) / frameTimeBuffer.Length;
  759.                
  760.                 //Prevent a zero delta time
  761.                 deltaTime = System.Math.Max (deltaTime, 1.0f/2000f);
  762.  
  763.                 // Time reference for the interpolation
  764.                 // If delta time would not be subtracted, the character would have a zero velocity
  765.                 // during all frames when the velocity was recalculated
  766.                 lastStepInterpolationReference = lastStep - Time.deltaTime;
  767.                
  768.                 if (Multithreading) {
  769.                    
  770.                     if (doubleBuffering) {
  771.                         for (int i=0;i<workers.Length;i++) workers[i].WaitOne();
  772.                         if (!Interpolation) for (int i=0;i<agents.Count;i++) agents[i].Interpolate (1.0f);
  773.                     }
  774.                    
  775.                    
  776.                     if (doCleanObstacles) {
  777.                         CleanObstacles();
  778.                         doCleanObstacles = false;
  779.                         doUpdateObstacles = true;
  780.                     }
  781.                    
  782.                     if (doUpdateObstacles) {
  783.                         doUpdateObstacles = false;
  784. #if !AstarRelease
  785.                         if ( kdTree != null ) kdTree.BuildObstacleTree ();
  786. #endif
  787.                     }
  788.                    
  789.  
  790.                     BuildQuadtree ();
  791.  
  792.                     for (int i=0;i<workers.Length;i++) {
  793.                         workers[i].start = i*agents.Count / workers.Length;
  794.                         workers[i].end = (i+1)*agents.Count / workers.Length;
  795.                     }
  796.  
  797.                     //Update
  798.                     //BufferSwitch
  799.                     for (int i=0;i<workers.Length;i++) workers[i].Execute (1);
  800.                     for (int i=0;i<workers.Length;i++) workers[i].WaitOne();
  801.  
  802.                     //Calculate New Velocity
  803.                     for (int i=0;i<workers.Length;i++) workers[i].Execute (0);
  804.  
  805.                     if (!doubleBuffering) {
  806.                         for (int i=0;i<workers.Length;i++) workers[i].WaitOne();
  807.                         if (!Interpolation) for (int i=0;i<agents.Count;i++) agents[i].Interpolate (1.0f);
  808.                     }
  809.                 } else {
  810.                    
  811.                     if (doCleanObstacles) {
  812.                         CleanObstacles();
  813.                         doCleanObstacles = false;
  814.                         doUpdateObstacles = true;
  815.                     }
  816.                    
  817.                     if (doUpdateObstacles) {
  818.                         doUpdateObstacles = false;
  819. #if !AstarRelease
  820.                         if ( kdTree != null ) kdTree.BuildObstacleTree ();
  821. #endif
  822.                     }
  823.  
  824.                     BuildQuadtree ();
  825.  
  826.                     for (int i=0;i<agents.Count;i++) {
  827.                         agents[i].Update ();
  828.                         agents[i].BufferSwitch ();
  829.                     }
  830.  
  831.  
  832.                     for (int i=0;i<agents.Count;i++) {
  833.                         agents[i].CalculateNeighbours ();
  834.                         agents[i].CalculateVelocity ( coroutineWorkerContext );
  835.                     }
  836.  
  837.                     if ( oversampling ) {
  838.                         for (int i=0;i<agents.Count;i++) {
  839.                             agents[i].Velocity = agents[i].newVelocity;
  840.                         }
  841.  
  842.                         for (int i=0;i<agents.Count;i++) {
  843.                             Vector3 vel = agents[i].newVelocity;
  844.                             agents[i].CalculateVelocity ( coroutineWorkerContext );
  845.                             agents[i].newVelocity = (vel + agents[i].newVelocity)*0.5f;
  846.                         }
  847.                     }
  848.  
  849.                     if (!Interpolation) for (int i=0;i<agents.Count;i++) agents[i].Interpolate (1.0f);
  850.                 }
  851.             }
  852.  
  853.             lastFrame = Time.time;
  854.            
  855.             if (Interpolation) {
  856.                 for (int i=0;i<agents.Count;i++) {
  857.                     agents[i].Interpolate ((Time.time - lastStepInterpolationReference)/DeltaTime);
  858.                 }
  859.             }
  860.         }
  861.  
  862.         internal class WorkerContext {
  863.             public Agent.VO[] vos = new Agent.VO[20];
  864.  
  865.             public const int KeepCount = 3;
  866.             public Vector2[] bestPos = new Vector2[KeepCount];
  867.             public float[] bestSizes = new float[KeepCount];
  868.             public float[] bestScores = new float[KeepCount+1];
  869.  
  870.             public Vector2[] samplePos = new Vector2[50];
  871.             public float[] sampleSize = new float[50];
  872.  
  873.         }
  874.  
  875.         private class Worker {
  876.             public Thread thread;
  877.             public int start, end;
  878.             public int task = 0;
  879.            
  880.             public AutoResetEvent runFlag = new AutoResetEvent(false);
  881.            
  882.             public ManualResetEvent waitFlag = new ManualResetEvent(true);
  883.            
  884.             public Simulator simulator;
  885.            
  886.             private bool terminate = false;
  887.  
  888.             private WorkerContext context = new WorkerContext();
  889.  
  890.             public Worker (Simulator sim) {
  891.                 this.simulator = sim;
  892.                 thread = new Thread (new ThreadStart (Run));
  893.                 thread.IsBackground = true;
  894.                 thread.Name = "RVO Simulator Thread";
  895.                 thread.Start ();
  896.             }
  897.            
  898.             public void Execute (int task) {
  899.                 this.task = task;
  900.                 waitFlag.Reset ();
  901.                 runFlag.Set ();
  902.             }
  903.            
  904.             public void WaitOne () {
  905.                 waitFlag.WaitOne ();
  906.             }
  907.            
  908.             public void Terminate () {
  909.                 terminate = true;
  910.             }
  911.            
  912.             public void Run () {
  913.                
  914.                 runFlag.WaitOne ();
  915.                
  916.                 while (!terminate) {
  917.                     try {
  918.                         List<Agent> agents = simulator.GetAgents ();
  919.                         if (task == 0) {
  920.                             for (int i=start;i<end;i++) {
  921.                                 agents[i].CalculateNeighbours ();
  922.                                 agents[i].CalculateVelocity ( context );
  923.                             }
  924.                            
  925.                         } else if (task == 1) {
  926.                             for (int i=start;i<end;i++) {
  927.                                 agents[i].Update ();
  928.                                 agents[i].BufferSwitch ();
  929.                             }
  930.                         } else if ( task  == 2 ) {
  931.                             simulator.BuildQuadtree ();
  932.                         /*} else if (task == 2) {
  933.                             for (int i=start;i<end;i++) {
  934.                                 agents[i].BufferSwitch ();
  935.                             }*/
  936.                         } else {
  937.                             Debug.LogError ("Invalid Task Number: " + task);
  938.                             throw new System.Exception ("Invalid Task Number: " + task);
  939.                         }
  940.                     } catch (System.Exception e) {
  941.                         Debug.LogError (e);
  942.                     }
  943.                     waitFlag.Set ();
  944.                     runFlag.WaitOne ();
  945.                 }
  946.             }
  947.         }
  948.     }
  949. }
Advertisement
Add Comment
Please, Sign In to add comment