Advertisement
Guest User

RVOCoreSimulator.cs

a guest
Sep 30th, 2014
293
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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.        
  235.         private bool doUpdateObstacles = false;
  236.         private bool doCleanObstacles = false;
  237.  
  238.         private bool oversampling = false;
  239.  
  240.         private int frameTimeBufferIndex = 0;
  241.         private float[] frameTimeBuffer = new float[1];
  242.  
  243.         public float DeltaTime { get { return deltaTime; } }
  244.         public float PrevDeltaTime { get { return prevDeltaTime; } }
  245.        
  246.         /** Is using multithreading */
  247.         public bool Multithreading { get { return workers != null && workers.Length > 0; }}
  248.        
  249.         /** Time in seconds between each simulation step.
  250.          * This is the desired delta time, the simulation will never run at a higher fps than
  251.          * the rate at which the Update function is called.
  252.          */
  253.         public float DesiredDeltaTime { get { return desiredDeltaTime; } set { desiredDeltaTime = System.Math.Max (value,0.0f); }}
  254.        
  255.         /** Use Interpolation.
  256.          * If interpolation is enabled, agent positions will be interpolated on frames when no rvo calculation is done.
  257.          * This has a very small overhead, but usually yields much smoother looking movement.
  258.          */
  259.         public bool Interpolation { get { return interpolation; } set { interpolation = value; }}
  260.  
  261.         public bool Oversampling { get { return oversampling; } set { oversampling = value; }}
  262.  
  263.         //internal int textureWidth;
  264.         //internal Texture2D tex;
  265.         //internal float textureSize;
  266.         //internal float colorScale = 0.05f;
  267.         //Color[] colors;
  268.        
  269.         //bool dirtyColors = false;
  270.  
  271.         /** Get a list of all agents.
  272.          *
  273.          * This is an internal list.
  274.          * I'm not going to be restrictive so you may access it since it is better for performance
  275.          * but please do not modify it since that can cause errors in the simulation.
  276.          *
  277.          * \warning Do not modify this list! */
  278.         public List<Agent> GetAgents () {
  279.             return agents;
  280.         }
  281.        
  282.         /** Get a list of all obstacles.
  283.          * This is a list of obstacle vertices.
  284.          * Each vertex is part of a doubly linked list loop
  285.          * forming an obstacle polygon.
  286.          *
  287.          * \warning Do not modify this list!
  288.          *
  289.          * \see AddObstacle
  290.          * \see RemoveObstacle
  291.          */
  292.         public List<ObstacleVertex> GetObstacles () {
  293.             return obstacles;
  294.         }
  295.        
  296.         /** Create a new simulator.
  297.          *
  298.          * \param workers Use the specified number of worker threads.\n
  299.          * When the number zero is specified, no multithreading will be used.
  300.          * A good number is the number of cores on the machine.
  301.          *
  302.          * \param doubleBuffering Use Double Buffering for calculations.
  303.          * Testing done with 5000 agents and 0.1 desired delta time showed that with double buffering enabled
  304.          * the game ran at 50 fps for most frames, dropping to 10 fps during calculation frames. But without double buffering
  305.          * it ran at around 10 fps all the time.\n
  306.          * This will let threads calculate while the game progresses instead of waiting for the calculations
  307.          * to finish.
  308.          * \note Will only have effect if using multithreading
  309.          *
  310.          * \see #Multithreading
  311.          */
  312.         public Simulator (int workers, bool doubleBuffering) {
  313.             this.workers = new Simulator.Worker[workers];
  314.             this.doubleBuffering = doubleBuffering;
  315.            
  316.             for (int i=0;i<workers;i++) this.workers[i] = new Simulator.Worker(this);
  317.            
  318.             //kdTree = new KDTree(this);
  319.             agents = new List<Agent>();
  320.             obstacles = new List<ObstacleVertex>();
  321.            
  322.         }
  323.  
  324.         /*internal void DebugPlot ( Vector2 p, Color col ) {
  325.             if ( colors == null ) {
  326.                 tex = new Texture2D(textureWidth,textureWidth);
  327.                 //mat.mainTexture = tex;
  328.                 colors = new Color[tex.width*tex.height];
  329.             }
  330.  
  331.             int x = Mathf.RoundToInt (p.x*tex.width/textureSize);
  332.             int y = Mathf.RoundToInt (p.y*tex.height/textureSize);
  333.            
  334.             if ( x >= 0 && y >= 0 && x < tex.width && y < tex.height ) {
  335.                 dirtyColors = true;
  336.                 colors[x+y*tex.width] = col;
  337.             }
  338.         }*/
  339.  
  340.         /** Removes all agents from the simulation */
  341.         public void ClearAgents () {
  342.            
  343.             //Bad to update agents while processing of current agents might be done
  344.             //Don't interfere with ongoing calculations
  345.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  346.            
  347.             for (int i=0;i<agents.Count;i++) {
  348.                 agents[i].simulator = null;
  349.             }
  350.             agents.Clear ();
  351.  
  352. #if !AstarRelease
  353.             if (kdTree != null ) kdTree.RebuildAgents ();
  354. #endif
  355.         }
  356.        
  357.         public void OnDestroy () {
  358.             if (workers != null) {
  359.                 for (int i=0;i<workers.Length;i++) workers[i].Terminate ();
  360.             }
  361.         }
  362.        
  363.         /** Terminates any worker threads */
  364.         ~Simulator () {
  365.             OnDestroy ();
  366.         }
  367.        
  368.         /** Add a previously removed agent to the simulation.
  369.           * An agent can only be in one simulation at a time, any attempt to add an agent to two simulations
  370.           * or multiple times to the same simulation will result in an exception being thrown.
  371.           *
  372.           * \see RemoveAgent
  373.           */
  374.         public IAgent AddAgent (IAgent agent) {
  375.             if (agent == null) throw new System.ArgumentNullException ("Agent must not be null");
  376.            
  377.             Agent agentReal = agent as Agent;
  378.             if (agentReal == null) throw new System.ArgumentException ("The agent must be of type Agent. Agent was of type "+agent.GetType ());
  379.            
  380.            
  381.             if (agentReal.simulator != null && agentReal.simulator == this) throw new System.ArgumentException ("The agent is already in the simulation");
  382.             else if (agentReal.simulator != null) throw new System.ArgumentException ("The agent is already added to another simulation");
  383.             agentReal.simulator = this;
  384.            
  385.             //Don't interfere with ongoing calculations
  386.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  387.            
  388.             agents.Add (agentReal);
  389.  
  390. #if !AstarRelease
  391.             if ( kdTree != null ) kdTree.RebuildAgents ();
  392. #endif         
  393.            
  394.             return agent;
  395.         }
  396.        
  397.         /** Add an agent at the specified position.
  398.          * You can use the returned interface to read several parameters such as position and velocity
  399.          * and set for example radius and desired velocity.
  400.          *
  401.          * \see RemoveAgent
  402.          */
  403.         public IAgent AddAgent (Vector3 position) {
  404.            
  405.             Agent agent = new Agent (position);
  406.            
  407.             //Don't interfere with ongoing calculations
  408.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  409.            
  410.             agents.Add (agent);
  411. #if !AstarRelease
  412.             if ( kdTree != null ) kdTree.RebuildAgents ();
  413. #endif         
  414.             agent.simulator = this;
  415.            
  416.             return agent;
  417.         }
  418.        
  419.         /** Removes a specified agent from this simulation.
  420.          * The agent can be added again later by using AddAgent.
  421.          *
  422.          * \see AddAgent(IAgent)
  423.          * \see ClearAgents
  424.          */
  425.         public void RemoveAgent (IAgent agent) {
  426.             if (agent == null) throw new System.ArgumentNullException ("Agent must not be null");
  427.            
  428.             Agent agentReal = agent as Agent;
  429.             if (agentReal == null) throw new System.ArgumentException ("The agent must be of type Agent. Agent was of type "+agent.GetType ());
  430.            
  431.             if (agentReal.simulator != this) throw new System.ArgumentException ("The agent is not added to this simulation");
  432.            
  433.             //Don't interfere with ongoing calculations
  434.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  435.            
  436.             agentReal.simulator = null;
  437.            
  438.             if (!agents.Remove (agentReal)) {
  439.                 throw new System.ArgumentException ("Critical Bug! This should not happen. Please report this.");
  440.             }
  441.         }
  442.        
  443.         /** Adds a previously removed obstacle.
  444.          * This does not check if the obstacle is already added to the simulation, so please do not add an obstacle multiple times.
  445.          *
  446.          * It is assumed that this is a valid obstacle.
  447.          */
  448.         public ObstacleVertex AddObstacle (ObstacleVertex v) {
  449.             if (v == null) throw new System.ArgumentNullException ("Obstacle must not be null");
  450.            
  451.             //Don't interfere with ongoing calculations
  452.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  453.            
  454.             obstacles.Add (v);
  455.             UpdateObstacles ();
  456.             return v;
  457.         }
  458.        
  459.         /** Adds an obstacle described by the vertices.
  460.          *
  461.          * \see RemoveObstacle
  462.          */
  463.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height) {
  464.            
  465.             return AddObstacle (vertices, height, Matrix4x4.identity);
  466.            
  467.             /*if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  468.            
  469.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  470.            
  471.             ObstacleVertex first = null;
  472.             ObstacleVertex prev = null;
  473.            
  474.             for (int i=0;i<vertices.Length;i++) {
  475.                 ObstacleVertex v = new ObstacleVertex();
  476.                 if (first == null) first = v;
  477.                 else prev.next = v;
  478.                
  479.                 v.prev = prev;
  480.                 v.position = vertices[i];
  481.                 //v.thin = thin;
  482.                 v.height = height;
  483.                 prev = v;
  484.             }
  485.            
  486.             prev.next = first;
  487.             first.prev = prev;
  488.            
  489.             ObstacleVertex c = first;
  490.             do {
  491.                 Vector3 dir = c.next.position - c.position;
  492.                 v.dir =  new Vector2 (dir.x,dir.z).normalized;
  493.                
  494.                 if (vertices.Length == 2) {
  495.                     v.convex = true;
  496.                 } else {
  497.                     v.convex = Polygon.IsClockwiseMargin (c.prev.position,c.position, c.next.position);
  498.                 }
  499.                
  500.                 c = c.next;
  501.             } while (c != first);
  502.            
  503.             obstacles.Add (first);
  504.            
  505.             UpdateObstacles ();
  506.             return first;*/
  507.         }
  508.        
  509.         /** Adds an obstacle described by the vertices.
  510.          *
  511.          * \see RemoveObstacle
  512.          */
  513.         public ObstacleVertex AddObstacle (Vector3[] vertices, float height, Matrix4x4 matrix) {
  514.            
  515.             if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  516.            
  517.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  518.            
  519.             ObstacleVertex first = null;
  520.             ObstacleVertex prev = null;
  521.            
  522.             bool identity = matrix == Matrix4x4.identity;
  523.            
  524.             //Don't interfere with ongoing calculations
  525.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  526.            
  527.             for (int i=0;i<vertices.Length;i++) {
  528.                 ObstacleVertex v = new ObstacleVertex();
  529.                 if (first == null) first = v;
  530.                 else prev.next = v;
  531.                
  532.                 v.prev = prev;
  533.                
  534.                 //Premature optimization ftw!
  535.                 v.position = identity ? vertices[i] : matrix.MultiplyPoint3x4(vertices[i]);
  536.                
  537.                 //v.thin = thin;
  538.                 v.height = height;
  539.                
  540.                 prev = v;
  541.             }
  542.            
  543.             prev.next = first;
  544.             first.prev = prev;
  545.            
  546.             ObstacleVertex c = first;
  547.             do {
  548.                 Vector3 dir = c.next.position - c.position;
  549.                 c.dir = new Vector2 (dir.x,dir.z).normalized;
  550.                
  551.                 if (vertices.Length == 2) {
  552.                     c.convex = true;
  553.                 } else {
  554.                     c.convex = Polygon.IsClockwiseMargin (c.next.position,c.position, c.prev.position);
  555.                 }
  556.                
  557.                 c = c.next;
  558.             } while (c != first);
  559.            
  560.             obstacles.Add (first);
  561.            
  562.             UpdateObstacles ();
  563.             return first;
  564.         }
  565.        
  566.         /**
  567.          * Adds a line obstacle with a specified height.
  568.          *
  569.          * \see RemoveObstacle
  570.          */
  571.         public ObstacleVertex AddObstacle (Vector3 a, Vector3 b, float height) {
  572.             ObstacleVertex first = new ObstacleVertex ();
  573.             ObstacleVertex second = new ObstacleVertex ();
  574.            
  575.             first.prev = second;
  576.             second.prev = first;
  577.             first.next = second;
  578.             second.next = first;
  579.            
  580.             first.position = a;
  581.             second.position = b;
  582.             first.height = height;
  583.             second.height = height;
  584.            
  585.             first.convex = true;
  586.             second.convex = true;
  587.            
  588.             first.dir = new Vector2 (b.x-a.x,b.z-a.z).normalized;
  589.             second.dir = -first.dir;
  590.            
  591.             //Don't interfere with ongoing calculations
  592.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  593.            
  594.             obstacles.Add (first);
  595.            
  596.             UpdateObstacles ();
  597.             return first;
  598.         }
  599.        
  600.         /** Updates the vertices of an obstacle.
  601.          * \param obstacle %Obstacle to update
  602.          * \param vertices New vertices for the obstacle, must have at least the number of vertices in the original obstacle
  603.          * \param matrix %Matrix to multiply vertices with before updating obstacle
  604.          *
  605.          * The number of vertices in an obstacle cannot be changed, existing vertices can only be moved.
  606.          */
  607.         public void UpdateObstacle (ObstacleVertex obstacle, Vector3[] vertices, Matrix4x4 matrix) {
  608.            
  609.             if (vertices == null) throw new System.ArgumentNullException ("Vertices must not be null");
  610.             if (obstacle == null) throw new System.ArgumentNullException ("Obstacle must not be null");
  611.            
  612.             if (vertices.Length < 2) throw new System.ArgumentException ("Less than 2 vertices in an obstacle");
  613.            
  614.             if (obstacle.split) throw new System.ArgumentException ("Obstacle is not a start vertex. You should only pass those ObstacleVertices got from AddObstacle method calls");
  615.            
  616.             //Don't interfere with ongoing calculations
  617.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  618.            
  619.             //Compact obstacle and count
  620.             int count = 0;
  621.            
  622.             ObstacleVertex c = obstacle;
  623.             do {
  624.                 while (c.next.split) {
  625.                     c.next = c.next.next;
  626.                     c.next.prev = c;
  627.                 }
  628.                
  629.                 if (count >= vertices.Length) {
  630.                     Debug.DrawLine (c.prev.position, c.position,Color.red);
  631.                     throw new System.ArgumentException ("Obstacle has more vertices than supplied for updating (" + vertices.Length+ " supplied)");
  632.                 }
  633.                 c.position = matrix.MultiplyPoint3x4 (vertices[count]);
  634.                 count++;
  635.                 c = c.next;
  636.             } while (c != obstacle);
  637.            
  638.             c = obstacle;
  639.             do {
  640.                 Vector3 dir = c.next.position - c.position;
  641.                 c.dir =  new Vector2 (dir.x,dir.z).normalized;
  642.                
  643.                 if (vertices.Length == 2) {
  644.                     c.convex = true;
  645.                 } else {
  646.                     c.convex = Polygon.IsClockwiseMargin (c.next.position,c.position, c.prev.position);
  647.                 }
  648.                
  649.                 c = c.next;
  650.             } while (c != obstacle);
  651.            
  652.             ScheduleCleanObstacles ();
  653.             UpdateObstacles();
  654.         }
  655.        
  656.         private void ScheduleCleanObstacles () {
  657.             doCleanObstacles = true;
  658.         }
  659.        
  660.         private void CleanObstacles () {
  661.            
  662.             for (int i=0;i<obstacles.Count;i++) {
  663.                 ObstacleVertex first = obstacles[i];
  664.                 ObstacleVertex c = first;
  665.                 do {
  666.                     while (c.next.split) {
  667.                         c.next = c.next.next;
  668.                         c.next.prev = c;
  669.                     }
  670.                     c = c.next;
  671.                 } while (c != first);
  672.             }
  673.         }
  674.        
  675.         /** Removes the obstacle identified by the vertex.
  676.           * This must be the same vertex as the one returned by the AddObstacle call.
  677.           *
  678.           * \see AddObstacle
  679.           */
  680.         public void RemoveObstacle (ObstacleVertex v) {
  681.             if (v == null) throw new System.ArgumentNullException ("Vertex must not be null");
  682.            
  683.             //Don't interfere with ongoing calculations
  684.             if (Multithreading && doubleBuffering) for (int j=0;j<workers.Length;j++) workers[j].WaitOne();
  685.            
  686.             obstacles.Remove (v);
  687.             UpdateObstacles ();
  688.         }
  689.        
  690.         /** Rebuilds the obstacle tree at next simulation frame.
  691.          * Add and remove obstacle functions call this automatically.
  692.          */
  693.         public void UpdateObstacles () {
  694.             //Update obstacles at next frame
  695.             doUpdateObstacles = true;
  696.         }
  697.  
  698.         void BuildQuadtree () {
  699.             quadtree.Clear ();
  700.             if ( agents.Count > 0 ) {
  701.                 Rect bounds = Rect.MinMaxRect (agents[0].position.x, agents[0].position.y, agents[0].position.x, agents[0].position.y);
  702.                 for ( int i = 1; i < agents.Count; i++ ) {
  703.                     Vector3 p = agents[i].position;
  704.                     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) );
  705.                 }
  706.                 quadtree.SetBounds (bounds);
  707.  
  708.                 for (int i=0;i<agents.Count;i++) {
  709.                     quadtree.Insert (agents[i]);
  710.                 }
  711.                
  712.                 //quadtree.DebugDraw ();
  713.             }
  714.         }
  715.  
  716.         private WorkerContext coroutineWorkerContext = new WorkerContext();
  717.  
  718.         /** Should be called once per frame */
  719.         public void Update () {
  720.            
  721.             //Initialize last step
  722.             if (lastStep < 0) {
  723.                 lastStep = Time.time;
  724.                 deltaTime = DesiredDeltaTime;
  725.                 prevDeltaTime = deltaTime;
  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.                 // Implements averaging of delta times
  736.                 // Disabled for now because it seems to have caused more issues than it solved
  737.                 // Might re-enable later
  738.                 /*frameTimeBufferIndex++;
  739.                 frameTimeBufferIndex %= frameTimeBuffer.Length;
  740.                 frameTimeBuffer[frameTimeBufferIndex] = deltaTime;
  741.                
  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.             if (Interpolation) {
  854.                 for (int i=0;i<agents.Count;i++) {
  855.                     agents[i].Interpolate ((Time.time - lastStepInterpolationReference)/DeltaTime);
  856.                 }
  857.             }
  858.         }
  859.  
  860.         internal class WorkerContext {
  861.             public Agent.VO[] vos = new Agent.VO[20];
  862.  
  863.             public const int KeepCount = 3;
  864.             public Vector2[] bestPos = new Vector2[KeepCount];
  865.             public float[] bestSizes = new float[KeepCount];
  866.             public float[] bestScores = new float[KeepCount+1];
  867.  
  868.             public Vector2[] samplePos = new Vector2[50];
  869.             public float[] sampleSize = new float[50];
  870.  
  871.         }
  872.  
  873.         private class Worker {
  874.             public Thread thread;
  875.             public int start, end;
  876.             public int task = 0;
  877.            
  878.             public AutoResetEvent runFlag = new AutoResetEvent(false);
  879.            
  880.             public ManualResetEvent waitFlag = new ManualResetEvent(true);
  881.            
  882.             public Simulator simulator;
  883.            
  884.             private bool terminate = false;
  885.  
  886.             private WorkerContext context = new WorkerContext();
  887.  
  888.             public Worker (Simulator sim) {
  889.                 this.simulator = sim;
  890.                 thread = new Thread (new ThreadStart (Run));
  891.                 thread.IsBackground = true;
  892.                 thread.Name = "RVO Simulator Thread";
  893.                 thread.Start ();
  894.             }
  895.            
  896.             public void Execute (int task) {
  897.                 this.task = task;
  898.                 waitFlag.Reset ();
  899.                 runFlag.Set ();
  900.             }
  901.            
  902.             public void WaitOne () {
  903.                 waitFlag.WaitOne ();
  904.             }
  905.            
  906.             public void Terminate () {
  907.                 terminate = true;
  908.             }
  909.            
  910.             public void Run () {
  911.                
  912.                 runFlag.WaitOne ();
  913.                
  914.                 while (!terminate) {
  915.                     try {
  916.                         List<Agent> agents = simulator.GetAgents ();
  917.                         if (task == 0) {
  918.                             for (int i=start;i<end;i++) {
  919.                                 agents[i].CalculateNeighbours ();
  920.                                 agents[i].CalculateVelocity ( context );
  921.                             }
  922.                            
  923.                         } else if (task == 1) {
  924.                             for (int i=start;i<end;i++) {
  925.                                 agents[i].Update ();
  926.                                 agents[i].BufferSwitch ();
  927.                             }
  928.                         } else if ( task  == 2 ) {
  929.                             simulator.BuildQuadtree ();
  930.                         /*} else if (task == 2) {
  931.                             for (int i=start;i<end;i++) {
  932.                                 agents[i].BufferSwitch ();
  933.                             }*/
  934.                         } else {
  935.                             Debug.LogError ("Invalid Task Number: " + task);
  936.                             throw new System.Exception ("Invalid Task Number: " + task);
  937.                         }
  938.                     } catch (System.Exception e) {
  939.                         Debug.LogError (e);
  940.                     }
  941.                     waitFlag.Set ();
  942.                     runFlag.WaitOne ();
  943.                 }
  944.             }
  945.         }
  946.     }
  947. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement