View difference between Paste ID: vrk2u9qW and rMPMNj0D
SHOW: | | - or go back to the newest paste.
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;
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-
				frameTimeBufferIndex++;
735+
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;
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;
853+
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
}