1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package cz.cuni.amis.pogamut.ut2004.agent.navigation.navmesh;
18
19
20
21
22
23 import cz.cuni.amis.pogamut.base.agent.navigation.IPathFuture;
24 import cz.cuni.amis.pogamut.base.agent.navigation.IPathPlanner;
25 import cz.cuni.amis.pogamut.base.agent.navigation.impl.PrecomputedPathFuture;
26 import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
27 import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
28 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
29 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DrawStayingDebugLines;
30 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
31 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
32 import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerFactory;
33 import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerModule;
34 import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
35 import cz.cuni.amis.pogamut.ut2004.utils.LinkFlag;
36 import cz.cuni.amis.pogamut.ut2004.utils.UT2004ServerRunner;
37 import cz.cuni.amis.utils.exception.PogamutException;
38 import java.io.*;
39 import java.util.*;
40 import java.util.logging.Level;
41 import javax.vecmath.Vector2d;
42 import math.geom2d.Point2D;
43 import math.geom2d.Shape2D;
44 import math.geom2d.line.Line2D;
45 import math.geom2d.line.StraightLine2D;
46 import math.geom3d.Point3D;
47 import math.geom3d.Vector3D;
48 import math.geom3d.line.StraightLine3D;
49 import math.geom3d.plane.Plane3D;
50
51
52
53
54
55 public class NavMesh implements IPathPlanner<ILocated> {
56 private ArrayList<double[]> verts = new ArrayList<double[]>();
57 private ArrayList<int[]> polys = new ArrayList<int[]>();
58 private ArrayList<ArrayList<Integer>> vertsToPolys;
59 private ArrayList<Boolean> safeVertex;
60 private BSPNode bspTree;
61 private BSPNode biggestLeafInTree;
62 private ArrayList<OffMeshPoint> offMeshPoints;
63 private ArrayList<ArrayList<OffMeshPoint>> polysToOffMeshPoints;
64 private LevelGeometry levelGeometry;
65
66 private UT2004Server server;
67
68 public Random random;
69
70
71
72
73 public NavMesh(boolean loadLevelGeometry) throws FileNotFoundException, IOException {
74
75 random = new Random();
76
77
78 UT2004ServerModule serverModule = new UT2004ServerModule();
79 UT2004ServerFactory serverFactory = new UT2004ServerFactory(serverModule);
80 UT2004ServerRunner serverRunner = new UT2004ServerRunner(serverFactory);
81 this.server = (UT2004Server) serverRunner.startAgent();
82 String mapName = server.getMapName();
83
84
85 boolean dataRestored = false;
86 System.out.println("Looking for previously saved data core...");
87 try {
88 ObjectInputStream in = new ObjectInputStream(new FileInputStream(NavMeshConstants.processedMeshDir + "\\" + mapName + ".processed"));
89 NavMeshCore core = (NavMeshCore) in.readObject();
90 this.biggestLeafInTree = core.biggestLeafInTree;
91 this.bspTree = core.bspTree;
92 this.polys = core.polys;
93 this.verts = core.verts;
94 this.vertsToPolys = core.vertsToPolys;
95 this.offMeshPoints = core.offMeshPoints;
96 this.polysToOffMeshPoints = core.polysToOffMeshPoints;
97 this.safeVertex = core.safeVertex;
98 System.out.println("Data core has been restored. Great!");
99 dataRestored = true;
100 } catch (Exception e) {
101 System.out.println("Previously saved data core could not be restored. New NavMesh will be processed. Exception message: " + e.getMessage());
102 }
103
104
105 if(!dataRestored) {
106
107 String fileName = NavMeshConstants.pureMeshReadDir + "\\" + mapName + ".navmesh";
108 BufferedReader br = new BufferedReader(new FileReader(fileName));
109 String line;
110
111 while((line = br.readLine()) != null) {
112 String[] toks = line.split("[ \\t]");
113
114 if(toks[0].equals("v")) {
115 double[] v = new double[3];
116 v[0] = Double.parseDouble(toks[1]);
117 v[1] = Double.parseDouble(toks[2]);
118 v[2] = Double.parseDouble(toks[3]);
119 verts.add(v);
120 }
121 if(toks[0].equals("p")) {
122 int[] p = new int[toks.length-1];
123 for(int i = 0; i<toks.length-1; i++) {
124 p[i] = Integer.parseInt(toks[i+1]);
125 }
126 polys.add(p);
127 }
128 }
129
130
131 resetVertsToPolys();
132
133
134 resetSafeVerts();
135
136
137 resetBSPTree();
138
139
140 eliminateUnreachablePolygons();
141
142
143 resetOffMeshConnections();
144
145
146 System.out.println("Writing navmesh core to a file...");
147 try {
148 NavMeshCore core = new NavMeshCore();
149 core.biggestLeafInTree = this.biggestLeafInTree;
150 core.bspTree = this.bspTree;
151 core.polys = this.polys;
152 core.verts = this.verts;
153 core.vertsToPolys = this.vertsToPolys;
154 core.offMeshPoints = this.offMeshPoints;
155 core.polysToOffMeshPoints = this.polysToOffMeshPoints;
156 core.safeVertex = this.safeVertex;
157 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(NavMeshConstants.processedMeshDir + "\\" + mapName + ".processed"));
158 out.writeObject(core);
159 System.out.println("Writing navmesh to a file complete.");
160 } catch (Exception e) {
161 System.out.println("Exception during writing navmesh to a file: " + e.getMessage());
162 }
163 }
164
165
166 if(loadLevelGeometry) {
167
168 dataRestored = false;
169 System.out.println("Looking for previously saved data level geom...");
170 try {
171 ObjectInputStream in = new ObjectInputStream(new FileInputStream(NavMeshConstants.processedLevelGeometryDir + "\\" + mapName + ".processed"));
172 levelGeometry = (LevelGeometry) in.readObject();
173 System.out.println("Level geom has been restored. Great!");
174 dataRestored = true;
175 } catch (Exception e) {
176 System.out.println("Previously saved level geom could not be restored. Exception message: " + e.getMessage());
177 e.printStackTrace();
178 }
179
180 if(!dataRestored) {
181 System.out.println("Reading and building level geometry...");
182 try {
183 levelGeometry = new LevelGeometry(mapName);
184 System.out.println("Level geometry read and built ok.");
185
186 try {
187 System.out.println("Writing level geometry to a file...");
188 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(NavMeshConstants.processedLevelGeometryDir + "\\" + mapName + ".processed"));
189 out.writeObject(levelGeometry);
190 System.out.println("Level geometry written ok.");
191 } catch (Exception e) {
192 System.out.println("Exception during writing level geom to a file: " + e.getMessage());
193 }
194 } catch(Exception e) {
195 System.out.println("Unable to get level geometry from file. Exception message: "+e.getMessage());
196 levelGeometry = null;
197 }
198 }
199 }
200
201 }
202
203
204
205
206 public NavMesh() throws FileNotFoundException, IOException {
207 this(true);
208 }
209
210
211
212
213 public String toDebugString() {
214 StringBuilder result = new StringBuilder("");
215
216 for(int i = 0; i < polys.size(); i++) {
217 int[] p = (int[]) polys.get(i);
218 for(int j = 0; j<p.length; j++) {
219 if(result.length()>0) result.append(";");
220
221 double[] v1,v2;
222 v1 = (double[]) verts.get(p[j]);
223 if(j==p.length-1) v2 = (double[]) verts.get(p[0]);
224 else v2 = (double[]) verts.get(p[j+1]);
225
226 result.append(v1[0]+","+v1[1]+","+v1[2]+";"+v2[0]+","+v2[1]+","+v2[2]);
227 }
228 }
229 return result.toString();
230 }
231
232
233
234
235 public String polygonToDebugString(int polygonNumber) {
236 StringBuilder result = new StringBuilder("");
237
238 int[] p = (int[]) polys.get(polygonNumber);
239 for(int j = 0; j<p.length; j++) {
240 if(result.length()>0) result.append(";");
241
242 double[] v1,v2;
243 v1 = (double[]) verts.get(p[j]);
244 if(j==p.length-1) v2 = (double[]) verts.get(p[0]);
245 else v2 = (double[]) verts.get(p[j+1]);
246
247 result.append(v1[0]+","+v1[1]+","+v1[2]+";"+v2[0]+","+v2[1]+","+v2[2]);
248 }
249 return result.toString();
250 }
251
252
253
254
255
256
257 private String offMeshEdgeToDebugString(OffMeshEdge oe) {
258 StringBuilder result = new StringBuilder("");
259 Location l1 = oe.getFrom().getLocation();
260 Location l2 = oe.getTo().getLocation();
261 result.append(l1.x+","+l1.y+","+l1.z+";"+l2.x+","+l2.y+","+l2.z);
262
263
264 double[] vector = new double[3];
265 vector[0] = l1.x - l2.x;
266 vector[1] = l1.y - l2.y;
267 vector[2] = l1.z - l2.z;
268
269 double length = Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]);
270 vector[0] *= 1/length * 20;
271 vector[1] *= 1/length * 20;
272 vector[2] *= 1/length * 20;
273 Point3D cross = new Point3D(l2.x+vector[0], l2.y+vector[1], l2.z+vector[2]);
274
275 double[] vector2 = new double[2];
276 vector2[0] = vector[1]/2;
277 vector2[1] = -vector[0]/2;
278 Point3D arrowPoint1 = new Point3D(cross.getX()+vector2[0], cross.getY()+vector2[1], cross.getZ());
279 Point3D arrowPoint2 = new Point3D(cross.getX()-vector2[0], cross.getY()-vector2[1], cross.getZ());
280
281 result.append(";");
282 result.append(arrowPoint1.getX()+","+arrowPoint1.getY()+","+arrowPoint1.getZ()+";"+l2.x+","+l2.y+","+l2.z);
283 result.append(";");
284 result.append(arrowPoint2.getX()+","+arrowPoint2.getY()+","+arrowPoint2.getZ()+";"+l2.x+","+l2.y+","+l2.z);
285 result.append(";");
286 result.append(arrowPoint1.getX()+","+arrowPoint1.getY()+","+arrowPoint1.getZ()+";"+arrowPoint2.getX()+","+arrowPoint2.getY()+","+arrowPoint2.getZ());
287
288
289 return result.toString();
290 }
291
292
293
294
295
296
297 private String pathToDebugString(IPathFuture<ILocated> path) {
298 StringBuilder result = new StringBuilder("");
299
300 ILocated p0 = null;
301 List<ILocated> pathList = path.get();
302 for(ILocated p1 : pathList) {
303 if(result.length()>0) result.append(";");
304 if(p0 != null) {
305 result.append(Math.round(p0.getLocation().x)+","+Math.round(p0.getLocation().y)+","+Math.round(p0.getLocation().z)+";"+Math.round(p1.getLocation().x)+","+Math.round(p1.getLocation().y)+","+Math.round(p1.getLocation().z));
306 }
307 p0 = p1;
308 }
309 return result.toString();
310 }
311
312
313
314
315 public void draw() {
316
317 for(int i = 0; i < this.polyCount(); i++) {
318 DrawStayingDebugLines d = new DrawStayingDebugLines();
319 String lines = this.polygonToDebugString(i);
320 d.setVectors(lines);
321 d.setColor(new Location(255, 255, 255));
322 d.setClearAll(i==0);
323 server.getAct().act(d);
324 }
325
326
327 for(OffMeshPoint op : offMeshPoints) {
328
329 for(OffMeshEdge oe : op.getOutgoingEdges()) {
330 DrawStayingDebugLines d = new DrawStayingDebugLines();
331 String lines = offMeshEdgeToDebugString(oe);
332 d.setVectors(lines);
333 d.setColor(NavMeshConstants.getColorForOffMeshConnection(oe, server));
334 d.setClearAll(false);
335 server.getAct().act(d);
336 }
337 }
338 }
339
340
341
342
343
344 public void draw(String lines, Location color) {
345 DrawStayingDebugLines d = new DrawStayingDebugLines();
346 d.setVectors(lines);
347 d.setColor(color);
348 d.setClearAll(false);
349 server.getAct().act(d);
350 }
351
352
353
354
355 public void unDraw() {
356 DrawStayingDebugLines d = new DrawStayingDebugLines();
357 d.setClearAll(true);
358 server.getAct().act(d);
359 }
360
361 public void drawLevelGeometry(Location color) {
362 levelGeometry.draw(server, color);
363 }
364
365
366
367
368
369 public void drawOnePolygon(int i, Location color) {
370 DrawStayingDebugLines d = new DrawStayingDebugLines();
371 String lines = this.polygonToDebugString(i);
372 d.setVectors(lines);
373 d.setColor(color);
374 d.setClearAll(false);
375 server.getAct().act(d);
376 }
377
378
379
380
381
382 public void drawOnePolygon(int i) {
383 drawOnePolygon(i, new Location(255,255,0));
384 }
385
386
387
388
389
390
391 private void drawAtom(INavMeshAtom atom, Location location) {
392 if(atom.getClass()==NavMeshPolygon.class) {
393 NavMeshPolygon p = (NavMeshPolygon) atom;
394 drawOnePolygon(p.getPolygonId(), location);
395 }
396 }
397
398
399
400
401
402 public void drawPolygonPath(List<INavMeshAtom> polygonPath, Location location) {
403 for(INavMeshAtom atom : polygonPath) {
404 drawAtom(atom, location);
405 }
406 }
407
408
409
410
411
412 public void drawPath(IPathFuture<ILocated> path, Location color) {
413
414
415
416
417
418 List<ILocated> pathList = path.get();
419 for(int i = 0; i<pathList.size()-1; i++) {
420 StringBuilder lines = new StringBuilder();
421 lines.append(pathList.get(i).getLocation().x + ",");
422 lines.append(pathList.get(i).getLocation().y + ",");
423 lines.append(pathList.get(i).getLocation().z + ";");
424 lines.append(pathList.get(i+1).getLocation().x + ",");
425 lines.append(pathList.get(i+1).getLocation().y + ",");
426 lines.append(pathList.get(i+1).getLocation().z + "");
427 draw(lines.toString(), color);
428 }
429 }
430
431
432
433
434
435
436 public void drawOnlyBiggestLeaf() {
437 for(int i = 0; i < this.polyCount(); i++) {
438 if(!biggestLeafInTree.polys.contains(i)) continue;
439
440 DrawStayingDebugLines d = new DrawStayingDebugLines();
441 String lines = this.polygonToDebugString(i);
442
443 d.setVectors(lines);
444 d.setColor(new Location(255, 255, 0));
445 d.setClearAll(false);
446 server.getAct().act(d);
447 }
448 }
449
450
451
452
453
454 public int polyCount() {
455 return polys.size();
456 }
457
458
459
460
461 public int vertCount() {
462 return verts.size();
463 }
464
465
466
467
468 public int[] getPolygon(int i) {
469 int[] p = ((int[])polys.get(i)).clone();
470 return p;
471 }
472
473
474
475
476 public double[] getVertex(int i) {
477 double[] v = ((double[])verts.get(i)).clone();
478 return v;
479 }
480
481
482
483
484 private void resetVertsToPolys() {
485 System.out.println("Setting vertsToPolys mapping array...");
486 vertsToPolys = new ArrayList();
487 for(int i = 0; i < verts.size(); i++) vertsToPolys.add(new ArrayList());
488 for(int i = 0; i < polys.size(); i++) {
489 int[] p = (int[]) polys.get(i);
490 for(int j = 0; j < p.length; j++) {
491 ArrayList p2 = (ArrayList) vertsToPolys.get(p[j]);
492 if(!p2.contains(i)) p2.add(i);
493 }
494 }
495 }
496
497
498
499
500 private void resetSafeVerts() {
501 System.out.println("Setting safe vertices...");
502 safeVertex = new ArrayList<Boolean>();
503 int safeCount = 0;
504
505
506 for(int v1 = 0; v1<verts.size(); v1++) {
507 System.out.println("Looking at vertex "+v1+"...");
508
509
510 double sum = 0;
511
512 ArrayList<Integer> polys = vertsToPolys.get(v1);
513
514 for(Integer pId : polys) {
515
516
517 int[] polygon = getPolygon(pId);
518
519 int v0 = -1, v2 = -1;
520 for(int i = 0; i<polygon.length; i++) {
521 if(polygon[i]==v1) {
522 v0 = (i==0 ? polygon[polygon.length-1] : polygon[i-1]);
523 v2 = (i==polygon.length-1 ? polygon[0] : polygon[i+1]);
524 break;
525 }
526 }
527
528 double[] vv0 = getVertex(v0);
529 double[] vv1 = getVertex(v1);
530 double[] vv2 = getVertex(v2);
531
532 double a = Math.sqrt( (vv1[0]-vv0[0])*(vv1[0]-vv0[0]) + (vv1[1]-vv0[1])*(vv1[1]-vv0[1]) + (vv1[2]-vv0[2])*(vv1[2]-vv0[2]) );
533 double b = Math.sqrt( (vv2[0]-vv1[0])*(vv2[0]-vv1[0]) + (vv2[1]-vv1[1])*(vv2[1]-vv1[1]) + (vv2[2]-vv1[2])*(vv2[2]-vv1[2]) );
534 double c = Math.sqrt( (vv2[0]-vv0[0])*(vv2[0]-vv0[0]) + (vv2[1]-vv0[1])*(vv2[1]-vv0[1]) + (vv2[2]-vv0[2])*(vv2[2]-vv0[2]) );
535
536
537 double gama = Math.acos( (a*a + b*b - c*c) / (2*a*b) );
538 System.out.println("angle gama is "+gama);
539
540 sum += gama;
541 }
542 System.out.println("sum angle is "+sum);
543
544 if(sum >= 2 * 3.14) {
545 safeVertex.add(v1, true);
546 safeCount++;
547 } else {
548 safeVertex.add(v1, false);
549 }
550 }
551 System.out.println("There are " + safeCount + " safe and " + (verts.size() + safeCount) + " unsafe vertices.");
552 }
553
554
555
556
557 public ArrayList getPolygonsByVertex(int i) {
558 return (ArrayList) ((ArrayList) this.vertsToPolys.get(i)).clone();
559 }
560
561
562
563
564 public ArrayList getNeighbourIdsToPolygon(int i) {
565
566 ArrayList neighbours = new ArrayList();
567 int[] p = this.getPolygon(i);
568
569 for(int j = 0; j<p.length; j++) {
570 ArrayList<Integer> p2 = getPolygonsByVertex(p[j]);
571 for(int k = 0; k < p2.size(); k++) {
572 int candidateId = p2.get(k);
573
574
575 int secondVertex = p[((j == p.length-1) ? 0 : j+1)];
576 int[] candidatePolygon = this.getPolygon(candidateId);
577 for(int l = 0; l < candidatePolygon.length; l++) {
578 if(candidatePolygon[l]==secondVertex) {
579
580 if(!neighbours.contains(candidateId) && candidateId!=i) neighbours.add(candidateId);
581 break;
582 }
583 }
584
585 }
586 }
587 return neighbours;
588 }
589
590
591
592
593
594
595 public List<OffMeshPoint> getOffMeshPoinsOnPolygon(int pId) {
596 return polysToOffMeshPoints.get(pId);
597 }
598
599
600
601
602 private void resetBSPTree() {
603 System.out.println("Creating BSP tree...");
604 bspTree = new BSPNode(this, null);
605 for(int i = 0; i<polys.size(); i++) bspTree.polys.add(i);
606 biggestLeafInTree = null;
607 bspTree.build();
608 System.out.println("BSP tree is done building.");
609 System.out.println("Biggest leaf has " + biggestLeafInTree.polys.size() + " polygons.");
610 }
611
612
613
614
615
616 public int getNumberOfPolygonsInBiggestLeaf() {
617 if(biggestLeafInTree != null) {
618 return biggestLeafInTree.polys.size();
619 }
620 else return -1;
621 }
622
623
624
625
626 public void setBiggestLeafInTree(BSPNode node) {
627 biggestLeafInTree = node;
628 }
629
630
631
632
633
634
635 public int getPolygonIdByPoint3D(Point3D point3D) {
636
637
638 Point2D point2D = new Point2D(point3D.getX(), point3D.getY());
639
640 BSPNode node = bspTree;
641 while(!node.isLeaf()) {
642
643 StraightLine2D sepLine = node.sepLine;
644 double dist = sepLine.getSignedDistance(point2D);
645 if(dist<0) {
646
647 node = node.left;
648 }
649 if(dist>0) {
650
651 node = node.right;
652 }
653 if(dist==0) {
654
655 point2D = new Point2D(point3D.getX() + random.nextDouble() -0.5 , point3D.getY() + random.nextDouble() -0.5);
656 }
657 }
658
659
660
661
662 ArrayList candidatePolygons = new ArrayList();
663
664
665 for(int i = 0; i<node.polys.size(); i++) {
666 Integer pId = (Integer) node.polys.get(i);
667
668 if( polygonContainsPoint(pId, point2D) ) {
669
670 candidatePolygons.add(pId);
671 }
672 else {
673
674 }
675 }
676
677
678
679
680 if(candidatePolygons.isEmpty()) return -1;
681
682
683 double minDist = NavMeshConstants.maxDistanceBotPolygon;
684 int retPId = -2;
685 for(int i = 0; i < candidatePolygons.size(); i++) {
686 Integer pId = (Integer) candidatePolygons.get(i);
687 int[] polygon = getPolygon(pId);
688
689
690 double[] v1 = getVertex(polygon[0]);
691 double[] v2 = getVertex(polygon[1]);
692 double[] v3 = getVertex(polygon[2]);
693 Point3D p1 = new Point3D(v1[0], v1[1], v1[2]);
694 Vector3D vector1 = new Vector3D(v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2]);
695 Vector3D vector2 = new Vector3D(v3[0]-v1[0], v3[1]-v1[1], v3[2]-v1[2]);
696 Plane3D plane = new Plane3D(p1, vector1, vector2);
697 double dist = plane.getDistance(point3D);
698 if(dist < minDist) {
699 retPId = pId;
700 minDist = dist;
701 }
702
703
704
705
706 if(polygon.length > 3) {
707 v1 = getVertex(polygon[0]);
708 v2 = getVertex(polygon[1]);
709 v3 = getVertex(polygon[3]);
710 p1 = new Point3D(v1[0], v1[1], v1[2]);
711 vector1 = new Vector3D(v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2]);
712 vector2 = new Vector3D(v3[0]-v1[0], v3[1]-v1[1], v3[2]-v1[2]);
713 plane = new Plane3D(p1, vector1, vector2);
714 dist = plane.getDistance(point3D);
715 if(dist < minDist) {
716 retPId = pId;
717 minDist = dist;
718 }
719 }
720
721 if(polygon.length > 4) {
722 v1 = getVertex(polygon[0]);
723 v2 = getVertex(polygon[2]);
724 v3 = getVertex(polygon[4]);
725 p1 = new Point3D(v1[0], v1[1], v1[2]);
726 vector1 = new Vector3D(v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2]);
727 vector2 = new Vector3D(v3[0]-v1[0], v3[1]-v1[1], v3[2]-v1[2]);
728 plane = new Plane3D(p1, vector1, vector2);
729 dist = plane.getDistance(point3D);
730 if(dist < minDist) {
731 retPId = pId;
732 minDist = dist;
733 }
734 }
735 }
736 System.out.println("distance of a point from polygon " +retPId+ " is " + minDist);
737 return retPId;
738 }
739
740
741
742
743
744
745 public int getPolygonIdByLocation(Location location) {
746 return getPolygonIdByPoint3D(new Point3D(location.x, location.y, location.z));
747 }
748
749
750
751
752
753
754
755 private boolean polygonContainsPoint(Integer pId, Point2D point2D) {
756 boolean result = true;
757 double rightSide = 0.0;
758
759 int[] polygon = getPolygon(pId);
760 double[] v1, v2;
761 for(int i = 0; i < polygon.length; i++) {
762 v1 = getVertex(polygon[i]);
763 if(i < polygon.length -1) v2 = getVertex(polygon[i+1]);
764 else v2 = getVertex(polygon[0]);
765 Point2D p1 = new Point2D(v1[0], v1[1]);
766 Point2D p2 = new Point2D(v2[0], v2[1]);
767 StraightLine2D line = new StraightLine2D(p1, p2);
768 double dist = line.getSignedDistance(point2D);
769
770 if(rightSide == 0.0) {
771 rightSide = Math.signum(dist);
772 }
773 else {
774 if(rightSide * dist < 0) {
775 result = false;
776 break;
777 }
778 }
779 }
780 return result;
781 }
782
783
784
785
786
787
788
789 private void eliminateUnreachablePolygons() {
790 System.out.println("eliminateUnreachablePolygons() starts...");
791
792
793 Map<UnrealId, NavPoint> navPoints = (Map<UnrealId, NavPoint>)(Map)server.getWorldView().getAll(NavPoint.class);
794
795
796 boolean[] reachable = new boolean[polys.size()];
797
798 System.out.println("marking reachable polygons...");
799 for(NavPoint navPoint : navPoints.values()) {
800 Point3D point3D = navPoint.getLocation().asPoint3D();
801 int pId = getPolygonIdByPoint3D(point3D);
802 if(pId < 0) continue;
803 markAsReachableRecursive(pId, reachable);
804 }
805
806 int reachableCount = 0; int polyDelCount = 0; int vertDelCount = 0;
807 for(int i = 0; i < polys.size(); i++) {
808 if(reachable[i]) reachableCount++;
809 }
810 System.out.println("Marking complete. There are " +reachableCount+ " reachable polygons and " + (polys.size()-reachableCount) + " unreachable polygons.");
811 System.out.println("Deleting unreachable polygons...");
812 for(int i = polys.size()-1; i>=0; i--) {
813 if(!reachable[i]) {
814 polys.remove(i);
815 polyDelCount++;
816 }
817 }
818 System.out.println("Reseting vertsToPolys...");
819 resetVertsToPolys();
820 System.out.println("Deleting unused vertices...");
821 for(int i = vertsToPolys.size()-1; i>=0; i--) {
822 ArrayList polygons = (ArrayList) vertsToPolys.get(i);
823 if(polygons.isEmpty()) {
824 verts.remove(i);
825 vertDelCount++;
826
827 for(int j = 0; j < polys.size(); j++) {
828 int[] polygon = (int[]) polys.get(j);
829 for(int k = 0; k < polygon.length; k++) {
830 if(polygon[k] > i) polygon[k]--;
831 }
832 }
833 }
834 }
835 System.out.println("Deleting done. " + polyDelCount + " Polygons and " + vertDelCount + " vertices were deleted.");
836
837
838 resetVertsToPolys();
839 resetSafeVerts();
840 resetBSPTree();
841 }
842
843
844
845
846
847
848 private void markAsReachableRecursive(int pId, boolean[] reachable) {
849 if(reachable[pId]) return;
850 reachable[pId] = true;
851 ArrayList neighbours = getNeighbourIdsToPolygon(pId);
852 for(int i = 0; i < neighbours.size(); i++) {
853 markAsReachableRecursive((Integer)neighbours.get(i), reachable);
854 }
855 }
856
857
858
859
860
861
862
863 private void resetOffMeshConnections() {
864 System.out.println("Creating off-mesh connections...");
865
866 Map<UnrealId, OffMeshPoint> offPoints = new HashMap<UnrealId, OffMeshPoint>();
867
868
869 Map<UnrealId, NavPoint> navPoints = (Map<UnrealId, NavPoint>)(Map)server.getWorldView().getAll(NavPoint.class);
870
871
872
873
874
875
876 for(Map.Entry<UnrealId, NavPoint> entry : navPoints.entrySet()) {
877 NavPoint np = entry.getValue();
878 UnrealId uId = entry.getKey();
879 int pId = this.getPolygonIdByPoint3D(np.getLocation().asPoint3D());
880 OffMeshPoint op = new OffMeshPoint(np, pId);
881 offPoints.put(uId, op);
882 }
883
884
885
886 for(Map.Entry<UnrealId, NavPoint> entry : navPoints.entrySet()) {
887 NavPoint np = entry.getValue();
888 UnrealId uId = entry.getKey();
889
890
891 for(Map.Entry<UnrealId, NavPointNeighbourLink> entry2 : np.getOutgoingEdges().entrySet()) {
892 NavPointNeighbourLink link = entry2.getValue();
893 UnrealId uId2 = entry2.getKey();
894
895
896 NavPoint target = link.getToNavPoint();
897
898 System.out.println("Checking edge from navpoint "+uId+" to navpoint "+target.getId()+"...");
899
900
901
902
903
904 boolean forceIgnore = false;
905 boolean forceAdd = false;
906 boolean addThisEdge = false;
907
908 if(np.isLiftCenter()) { forceAdd = true; }
909 if(target.isLiftCenter()) { forceAdd = true; }
910
911 int linkFlags = link.getFlags();
912 if ((linkFlags & LinkFlag.DOOR.get()) > 0) {}
913 if ((linkFlags & LinkFlag.FLY.get()) > 0) { forceIgnore = true; }
914 if ((linkFlags & LinkFlag.FORCED.get()) > 0) {}
915 if ((linkFlags & LinkFlag.JUMP.get()) > 0) {}
916 if ((linkFlags & LinkFlag.LADDER.get()) > 0) { forceIgnore = true; }
917 if ((linkFlags & LinkFlag.PLAYERONLY.get()) > 0) {}
918 if ((linkFlags & LinkFlag.PROSCRIBED.get()) > 0) { forceIgnore = true; }
919 if ((linkFlags & LinkFlag.SPECIAL.get()) > 0) { }
920 if ((linkFlags & LinkFlag.SWIM.get()) > 0) { forceIgnore = true; }
921 if ((linkFlags & LinkFlag.WALK.get()) > 0) {}
922
923 if(!forceAdd && !forceIgnore) {
924
925
926 Line2D linkAsLine2D = new Line2D(link.getFromNavPoint().getLocation().x, link.getFromNavPoint().getLocation().y, link.getToNavPoint().getLocation().x, link.getToNavPoint().getLocation().y);
927
928
929
930
931
932 int currentPolygonId = getPolygonIdByPoint3D(link.getFromNavPoint().getLocation().asPoint3D());
933 int targetPolygonId = this.getPolygonIdByPoint3D(link.getToNavPoint().getLocation().asPoint3D());
934 int tabooPolygon = -1;
935 while(currentPolygonId >= 0 && currentPolygonId != targetPolygonId) {
936 int newPolygon = -1;
937
938
939 List<Integer> neighbours = this.getNeighbourIdsToPolygon(currentPolygonId);
940 for (Integer neighbour : neighbours) {
941 if(neighbour.intValue() == tabooPolygon) continue;
942
943
944 Line2D sharedEdge = null;
945 int[] polygon1 = getPolygon(currentPolygonId);
946 int[] polygon2 = getPolygon(neighbour);
947 for(int i = 0; i < polygon1.length; i++) {
948 int v1 = polygon1[i];
949 int v2 = polygon1[((i==polygon1.length-1) ? 0 : i+1)];
950
951 boolean containsV1 = false, containsV2 = false;
952 for(int j = 0; j < polygon2.length; j++) {
953 if(polygon2[j]==v1) containsV1 = true;
954 if(polygon2[j]==v2) containsV2 = true;
955 }
956 if(containsV1 && containsV2) {
957 double[] vertex1 = this.getVertex(v1);
958 double[] vertex2 = this.getVertex(v2);
959 sharedEdge = new Line2D(vertex1[0], vertex1[1], vertex2[0], vertex2[1]);
960 }
961 }
962
963 if(sharedEdge==null) {
964 System.out.println("Error! shared edge between polygon "+currentPolygonId+" and "+neighbour+" was not found!");
965 }
966
967
968 if(linkAsLine2D.getIntersection(sharedEdge) != null) {
969 System.out.println("Crossed a line into polygon "+neighbour);
970 tabooPolygon = currentPolygonId;
971 newPolygon = neighbour;
972 break;
973 }
974 }
975 currentPolygonId = newPolygon;
976 }
977
978
979 if(currentPolygonId > 0) {
980
981 addThisEdge = false;
982 } else {
983
984 addThisEdge = true;
985 }
986 }
987
988 else {
989
990 if(forceAdd) addThisEdge = true;
991 if(forceIgnore) addThisEdge = false;
992 }
993
994
995 if(addThisEdge) {
996 System.out.println("This edge is off-mesh!");
997 OffMeshPoint op1 = offPoints.get(uId);
998 OffMeshPoint op2 = offPoints.get(target.getId());
999 OffMeshEdge oe = new OffMeshEdge(op1, op2, link);
1000 op1.getOutgoingEdges().add(oe);
1001 op2.getIncomingEdges().add(oe);
1002 } else {
1003 System.out.println("This edge is not off-mesh.");
1004 }
1005 }
1006 }
1007
1008
1009 offMeshPoints = new ArrayList<OffMeshPoint>();
1010 int offCount = 0;
1011 for(OffMeshPoint op : offPoints.values()) {
1012 if(op.getOutgoingEdges().isEmpty() && op.getIncomingEdges().isEmpty()) {
1013
1014 }
1015 else {
1016 offMeshPoints.add(op);
1017 offCount++;
1018 }
1019 }
1020 System.out.println("We found "+offCount+" offMeshPoints in total of "+offPoints.size()+" NavPoints.");
1021
1022
1023 polysToOffMeshPoints = new ArrayList<ArrayList<OffMeshPoint>>();
1024 for(int i=0; i<polys.size(); i++) {
1025 polysToOffMeshPoints.add(new ArrayList<OffMeshPoint>());
1026 }
1027 for(OffMeshPoint op : offMeshPoints) {
1028 int pId = op.getPId();
1029 if(pId >=0) polysToOffMeshPoints.get(pId).add(op);
1030 }
1031
1032 System.out.println("Off-mesh connections done.");
1033 }
1034
1035
1036
1037
1038
1039
1040
1041 public List<INavMeshAtom> getPolygonPath(INavMeshAtom fromAtom, INavMeshAtom toAtom) {
1042
1043 List<AStarNode> pickable = new ArrayList<AStarNode>();
1044
1045 List<AStarNode> expanded = new ArrayList<AStarNode>();
1046 pickable.add(new AStarNode(null, fromAtom, this, fromAtom, toAtom));
1047
1048
1049 AStarNode targetNode = null;
1050 while(targetNode==null) {
1051
1052
1053 if(pickable.isEmpty()) {
1054 return null;
1055 }
1056
1057
1058
1059 AStarNode best = pickable.get(0);
1060 for(AStarNode node : pickable) {
1061 if(node.getEstimatedTotalDistance() < best.getEstimatedTotalDistance()) {
1062 best = node;
1063 }
1064 }
1065
1066
1067 List<INavMeshAtom> neighbours = best.getAtom().getNeighbours(this);
1068 for(INavMeshAtom atom : neighbours) {
1069 boolean add = true;
1070
1071
1072 for(AStarNode expNode : expanded) {
1073 if(expNode.getAtom().equals(atom)) add = false;
1074 }
1075
1076 if(add) {
1077 AStarNode newNode = new AStarNode(best, atom, this, fromAtom, toAtom);
1078 best.getFollowers().add(newNode);
1079 pickable.add(newNode);
1080
1081 if(atom.equals(toAtom)) {
1082 targetNode = newNode;
1083 }
1084 }
1085 }
1086
1087 pickable.remove(best);
1088 expanded.add(best);
1089 }
1090
1091
1092 List<INavMeshAtom> path = new ArrayList<INavMeshAtom>();
1093 AStarNode node = targetNode;
1094 while(node != null) {
1095 path.add(node.getAtom());
1096 node = node.getFrom();
1097 }
1098 Collections.reverse(path);
1099 return path;
1100 }
1101
1102
1103
1104
1105
1106
1107
1108 public List<INavMeshAtom> getPolygonPath(Location from, Location to) {
1109 INavMeshAtom fromAtom = getNearestAtom(from);
1110 INavMeshAtom toAtom = getNearestAtom(to);
1111 return getPolygonPath(fromAtom, toAtom);
1112 }
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132 private List<ILocated> getPolygonCentrePath(ILocated from, ILocated to, List<INavMeshAtom> polygonPath) {
1133 List path = new ArrayList<ILocated>();
1134 path.add(from);
1135 INavMeshAtom lastAtom = null;
1136 for(INavMeshAtom atom : polygonPath) {
1137
1138 if(atom.getClass()==OffMeshPoint.class) {
1139 NavPoint np = (NavPoint)server.getWorldView().get(((OffMeshPoint)atom).getNavPointId());
1140 path.add(np);
1141 }
1142 else {
1143
1144 NavMeshPolygon polygon = (NavMeshPolygon)atom;
1145
1146 if(lastAtom==null || lastAtom.getClass()==OffMeshPoint.class) {
1147 OffMeshPoint op = (OffMeshPoint)lastAtom;
1148
1149
1150 boolean inside;
1151 if(lastAtom==null) {
1152 inside = polygonContainsPoint(polygon.getPolygonId(), new Point2D(from.getLocation().x, from.getLocation().y));
1153 } else {
1154 inside = false;
1155 List<OffMeshPoint> offPs = polysToOffMeshPoints.get(polygon.getPolygonId());
1156 for(OffMeshPoint op2 : offPs) {
1157 if(op2.equals(op)) inside = true;
1158 }
1159 }
1160
1161 if(inside) {
1162
1163 } else {
1164
1165 path.add(getLocation(atom));
1166 }
1167 }
1168
1169 else {
1170
1171 NavMeshPolygon polygon2 = (NavMeshPolygon)lastAtom;
1172 int[] p1 = this.getPolygon(polygon.getPolygonId());
1173 int[] p2 = this.getPolygon(polygon2.getPolygonId());
1174 int v1 = -1;
1175 int v2 = -1;
1176 outer: for(int i = 0; i < p1.length; i++) {
1177 for(int j = 0; j < p2.length; j++) {
1178 if(p1[i]==p2[j]) {
1179 if(v1==-1) {
1180 v1 = p1[i];
1181 }
1182 else {
1183 if(p1[i]!=v1) v2 = p1[i];
1184 break outer;
1185 }
1186 }
1187 }
1188 }
1189 double[] vv1 = getVertex(v1);
1190 double[] vv2 = getVertex(v2);
1191 path.add(new Location( (vv1[0]+vv2[0])/2,(vv1[1]+vv2[1])/2 ,(vv1[2]+vv2[2])/2 + NavMeshConstants.liftPolygonLocation));
1192 }
1193 }
1194 lastAtom = atom;
1195 }
1196 path.add(to);
1197 return path;
1198 }
1199
1200
1201
1202
1203
1204
1205
1206
1207 private List<ILocated> getFunneledPath(ILocated from, ILocated to, List<INavMeshAtom> polygonPath) {
1208 List<ILocated> path = new ArrayList<ILocated>();
1209 path.add(from);
1210 INavMeshAtom lastAtom = null;
1211 INavMeshAtom atom = null;
1212 int index = -1;
1213 while(index < polygonPath.size()-1) {
1214 index++;
1215 if(index>0) lastAtom = polygonPath.get(index-1);
1216 else lastAtom = null;
1217 atom = polygonPath.get(index);
1218
1219
1220 if(atom.getClass()==OffMeshPoint.class) {
1221 NavPoint np = (NavPoint)server.getWorldView().get(((OffMeshPoint)atom).getNavPointId());
1222 path.add(np);
1223 }
1224
1225
1226 else {
1227
1228 NavMeshPolygon polygon = (NavMeshPolygon)atom;
1229
1230 if(lastAtom==null || lastAtom.getClass()==OffMeshPoint.class) {
1231 OffMeshPoint op = (OffMeshPoint)lastAtom;
1232
1233
1234 boolean inside;
1235 if(lastAtom==null) {
1236 inside = polygonContainsPoint(polygon.getPolygonId(), new Point2D(from.getLocation().x, from.getLocation().y));
1237 } else {
1238 inside = false;
1239 List<OffMeshPoint> offPs = polysToOffMeshPoints.get(polygon.getPolygonId());
1240 for(OffMeshPoint op2 : offPs) {
1241 if(op2.equals(op)) inside = true;
1242 }
1243 }
1244
1245 if(inside) {
1246
1247 } else {
1248
1249 path.add(getLocation(atom));
1250 }
1251 }
1252
1253 else {
1254
1255
1256
1257 ILocated start = path.get(path.size()-1);
1258
1259
1260 NavMeshPolygon polygon2 = (NavMeshPolygon)lastAtom;
1261 int[] p1 = this.getPolygon(polygon.getPolygonId());
1262 int[] p2 = this.getPolygon(polygon2.getPolygonId());
1263 int v1 = -1;
1264 int v2 = -1;
1265 outer: for(int i = 0; i < p1.length; i++) {
1266 for(int j = 0; j < p2.length; j++) {
1267 if(p1[i]==p2[j]) {
1268 if(v1==-1) {
1269 v1 = p1[i];
1270 }
1271 else {
1272 if(p1[i]!=v1) v2 = p1[i];
1273 break outer;
1274 }
1275 }
1276 }
1277 }
1278 double[] vv1 = getVertex(v1);
1279 double[] vv2 = getVertex(v2);
1280 Line2D gateway = new Line2D(vv1[0], vv1[1], vv2[0], vv2[1]);
1281
1282
1283 if( (start.getLocation().x==vv2[0] && start.getLocation().y==vv2[1]) ||
1284 (start.getLocation().x==vv1[0] && start.getLocation().y==vv1[1])) {
1285 System.out.println("We are already in the next polygon. No comparation, let's just continue.");
1286 continue;
1287 }
1288
1289
1290 double dist = gateway.getSignedDistance(start.getLocation().x, start.getLocation().y);
1291
1292
1293 Line2D leftMantinel = new Line2D(start.getLocation().x, start.getLocation().y, vv2[0], vv2[1]);
1294 Line2D rightMantinel = new Line2D(start.getLocation().x, start.getLocation().y, vv1[0], vv1[1]);
1295 if(dist < 0) {
1296 Line2D swap = leftMantinel;
1297 leftMantinel = rightMantinel;
1298 rightMantinel = swap;
1299 vv1 = getVertex(v2);
1300 vv2 = getVertex(v1);
1301 gateway = new Line2D(vv1[0], vv1[1], vv2[0], vv2[1]);
1302 }
1303
1304
1305 int leftMantinelIndex = index;
1306 double leftMantinelZ = vv2[2];
1307 Location leftMantinelTarget;
1308 if(safeVertex.get(v2)) {
1309 leftMantinelTarget = new Location(vv2[0],vv2[1],vv2[2]+NavMeshConstants.liftPolygonLocation);
1310 } else {
1311 if(gateway.getLength() <= 2*NavMeshConstants.agentRadius) {
1312 leftMantinelTarget = new Location((vv2[0]+vv1[0])/2,(vv2[1]+vv1[1])/2,(vv2[2]+vv1[2])/2+NavMeshConstants.liftPolygonLocation);
1313 } else {
1314 leftMantinelTarget = new Location( vv2[0] + (vv1[0]-vv2[0])/gateway.getLength()*NavMeshConstants.agentRadius,
1315 vv2[1] + (vv1[1]-vv2[1])/gateway.getLength()*NavMeshConstants.agentRadius,
1316 vv2[2] + (vv1[2]-vv2[2])/gateway.getLength()*NavMeshConstants.agentRadius+NavMeshConstants.liftPolygonLocation);
1317 }
1318 }
1319
1320 int rightMantinelIndex = index;
1321 double rightMantinelZ = vv1[2];
1322 Location rightMantinelTarget;
1323 if(safeVertex.get(v1)) {
1324 rightMantinelTarget = new Location(vv1[0],vv1[1],vv1[2]+NavMeshConstants.liftPolygonLocation);
1325 } else {
1326 if(gateway.getLength() <= 2*NavMeshConstants.agentRadius) {
1327 rightMantinelTarget = new Location((vv2[0]+vv1[0])/2,(vv2[1]+vv1[1])/2,(vv2[2]+vv1[2])/2+NavMeshConstants.liftPolygonLocation);
1328 } else {
1329 rightMantinelTarget = new Location( vv1[0] + (vv2[0]-vv1[0])/gateway.getLength()*NavMeshConstants.agentRadius,
1330 vv1[1] + (vv2[1]-vv1[1])/gateway.getLength()*NavMeshConstants.agentRadius,
1331 vv1[2] + (vv2[2]-vv1[2])/gateway.getLength()*NavMeshConstants.agentRadius+NavMeshConstants.liftPolygonLocation);
1332 }
1333 }
1334
1335
1336
1337 boolean targetAdded = false;
1338 boolean outOfMantinels = false;
1339 boolean endOfPolygonPathReached = false;
1340 while(!targetAdded && !outOfMantinels) {
1341 index++;
1342 lastAtom = polygonPath.get(index-1);
1343 if(index<polygonPath.size()) atom = polygonPath.get(index);
1344
1345 else endOfPolygonPathReached = true;
1346
1347 polygon2 = (NavMeshPolygon)lastAtom;
1348
1349
1350
1351
1352 if(endOfPolygonPathReached || atom.getClass()==OffMeshPoint.class) {
1353 NavPoint np = null;
1354 if(!endOfPolygonPathReached) np = (NavPoint)server.getWorldView().get(((OffMeshPoint)atom).getNavPointId());
1355
1356 ILocated target;
1357 if(endOfPolygonPathReached) target = to;
1358 else target = np;
1359
1360
1361
1362 Line2D virtualGateway1 = new Line2D(target.getLocation().x, target.getLocation().y, leftMantinel.p2.x, leftMantinel.p2.y);
1363 dist = virtualGateway1.getSignedDistance(start.getLocation().x, start.getLocation().y);
1364 if(dist<0) {
1365
1366
1367 path.add(leftMantinelTarget);
1368
1369 outOfMantinels = true;
1370 index = leftMantinelIndex;
1371 }
1372 else {
1373
1374
1375 Line2D virtualGateway2 = new Line2D(rightMantinel.p2.x, rightMantinel.p2.y, target.getLocation().x, target.getLocation().y);
1376 dist = virtualGateway2.getSignedDistance(start.getLocation().x, start.getLocation().y);
1377 if(dist<0) {
1378
1379
1380 path.add(rightMantinelTarget);
1381
1382 outOfMantinels = true;
1383 index = leftMantinelIndex;
1384 }
1385 else {
1386
1387 if(!endOfPolygonPathReached) path.add(np);
1388 targetAdded = true;
1389 }
1390 }
1391 }
1392
1393 else {
1394 polygon = (NavMeshPolygon)atom;
1395 Point2D middleOfOldGateway = new Point2D((gateway.p1.x+gateway.p2.x)/2, (gateway.p1.y+gateway.p2.y)/2);
1396
1397 p1 = this.getPolygon(polygon.getPolygonId());
1398 p2 = this.getPolygon(polygon2.getPolygonId());
1399 v1 = -1;
1400 v2 = -1;
1401 outer: for(int i = 0; i < p1.length; i++) {
1402 for(int j = 0; j < p2.length; j++) {
1403 if(p1[i]==p2[j]) {
1404 if(v1==-1) {
1405 v1 = p1[i];
1406 }
1407 else {
1408 if(p1[i]!=v1) v2 = p1[i];
1409 break outer;
1410 }
1411 }
1412 }
1413 }
1414 vv1 = getVertex(v1);
1415 vv2 = getVertex(v2);
1416 gateway = new Line2D(vv1[0], vv1[1], vv2[0], vv2[1]);
1417
1418
1419 dist = gateway.getSignedDistance(middleOfOldGateway);
1420 if(dist<0) {
1421 vv1 = getVertex(v2);
1422 vv2 = getVertex(v1);
1423 gateway = new Line2D(vv1[0], vv1[1], vv2[0], vv2[1]);
1424 }
1425
1426
1427
1428
1429 dist = leftMantinel.getSignedDistance(gateway.p2);
1430
1431 if(dist < 0) {
1432
1433
1434 dist = rightMantinel.getSignedDistance(gateway.p2);
1435
1436 if(dist > 0) {
1437
1438 leftMantinel = new Line2D(leftMantinel.p1, gateway.p2);
1439 leftMantinelIndex = index;
1440 leftMantinelZ = vv2[2];
1441 if(safeVertex.get(v2)) {
1442 leftMantinelTarget = new Location(vv2[0],vv2[1],vv2[2]+NavMeshConstants.liftPolygonLocation);
1443 } else {
1444 if(gateway.getLength() <= 2*NavMeshConstants.agentRadius) {
1445 leftMantinelTarget = new Location((vv2[0]+vv1[0])/2,(vv2[1]+vv1[1])/2,(vv2[2]+vv1[2])/2+NavMeshConstants.liftPolygonLocation);
1446 } else {
1447 leftMantinelTarget = new Location( vv2[0] + (vv1[0]-vv2[0])/gateway.getLength()*NavMeshConstants.agentRadius,
1448 vv2[1] + (vv1[1]-vv2[1])/gateway.getLength()*NavMeshConstants.agentRadius,
1449 vv2[2] + (vv1[2]-vv2[2])/gateway.getLength()*NavMeshConstants.agentRadius+NavMeshConstants.liftPolygonLocation);
1450 }
1451 }
1452 }
1453 else {
1454
1455
1456
1457 path.add(rightMantinelTarget);
1458 outOfMantinels = true;
1459 index = rightMantinelIndex;
1460 }
1461 }
1462 else {
1463
1464
1465
1466 }
1467
1468
1469 dist = rightMantinel.getSignedDistance(gateway.p1);
1470
1471 if(dist > 0) {
1472
1473
1474
1475
1476
1477 dist = leftMantinel.getSignedDistance(gateway.p1);
1478
1479 if(dist < 0) {
1480
1481 rightMantinel = new Line2D(rightMantinel.p1, gateway.p1);
1482 rightMantinelIndex = index;
1483 rightMantinelZ = vv1[2];
1484 if(safeVertex.get(v1)) {
1485 rightMantinelTarget = new Location(vv1[0],vv1[1],vv1[2]+NavMeshConstants.liftPolygonLocation);
1486 } else {
1487 if(gateway.getLength() <= 2*NavMeshConstants.agentRadius) {
1488 rightMantinelTarget = new Location((vv2[0]+vv1[0])/2,(vv2[1]+vv1[1])/2,(vv2[2]+vv1[2])/2+NavMeshConstants.liftPolygonLocation);
1489 } else {
1490 rightMantinelTarget = new Location( vv1[0] + (vv2[0]-vv1[0])/gateway.getLength()*NavMeshConstants.agentRadius,
1491 vv1[1] + (vv2[1]-vv1[1])/gateway.getLength()*NavMeshConstants.agentRadius,
1492 vv1[2] + (vv2[2]-vv1[2])/gateway.getLength()*NavMeshConstants.agentRadius+NavMeshConstants.liftPolygonLocation);
1493 }
1494 }
1495 }
1496 else {
1497
1498
1499
1500 path.add(leftMantinelTarget);
1501 outOfMantinels = true;
1502 index = leftMantinelIndex;
1503 }
1504 }
1505 else {
1506
1507
1508
1509 }
1510 }
1511 }
1512 }
1513 }
1514 }
1515 path.add(to);
1516 return path;
1517 }
1518
1519
1520
1521
1522
1523
1524
1525
1526 public List<ILocated> getPath(ILocated from, ILocated to) {
1527
1528
1529
1530 List<INavMeshAtom> polygonPath = getPolygonPath(from.getLocation(), to.getLocation());
1531 if(polygonPath == null) return null;
1532
1533
1534
1535 List<ILocated> path;
1536
1537
1538
1539 path = getFunneledPath(from, to, polygonPath);
1540
1541 return path;
1542 }
1543
1544
1545
1546
1547
1548
1549
1550
1551 public IPathFuture<ILocated> computePath(ILocated from, ILocated to) {
1552 return new PrecomputedPathFuture<ILocated>(from, to, getPath(from, to));
1553 }
1554
1555
1556
1557
1558
1559
1560 private INavMeshAtom getNearestAtom(Location location) {
1561
1562
1563 int pId = getPolygonIdByLocation(location);
1564 if(pId > 0) {
1565 return new NavMeshPolygon(pId);
1566 }
1567 else {
1568
1569
1570 double minDist = Double.MAX_VALUE;
1571 INavMeshAtom nearest = null;
1572 for(OffMeshPoint op : offMeshPoints) {
1573 double dist = location.getDistance(op.getLocation());
1574 if(dist < minDist) {
1575 nearest = op;
1576 minDist = dist;
1577 }
1578 }
1579
1580
1581
1582 if(nearest==null) {
1583 for(int i = 0; i < polys.size(); i++) {
1584 NavMeshPolygon p = new NavMeshPolygon(i);
1585 Location pl = getLocation(p);
1586 double dist = location.getDistance(pl);
1587 if(dist < minDist) {
1588 nearest = p;
1589 minDist = dist;
1590 }
1591 }
1592 }
1593
1594 return nearest;
1595 }
1596 }
1597
1598
1599
1600
1601
1602
1603
1604
1605 double getDistance(INavMeshAtom atom1, INavMeshAtom atom2) {
1606 if(atom1.equals(atom2)) return 0.0;
1607 Location l1, l2;
1608 l1 = getLocation(atom1);
1609 l2 = getLocation(atom2);
1610 return l1.getDistance(l2);
1611 }
1612
1613
1614
1615
1616
1617
1618
1619 private Location getLocation(INavMeshAtom atom1) {
1620 if(atom1.getClass()==OffMeshPoint.class) return getLocation((OffMeshPoint)atom1);
1621 if(atom1.getClass()==NavMeshPolygon.class) return getLocation((NavMeshPolygon)atom1);
1622 throw new UnsupportedOperationException("Not implemented. Not now. Not ever.");
1623 }
1624
1625
1626
1627
1628
1629 private Location getLocation(OffMeshPoint op) {
1630 NavPoint np = (NavPoint) server.getWorldView().get(op.getNavPointId());
1631 return np.getLocation();
1632 }
1633
1634
1635
1636
1637
1638 private Location getLocation(NavMeshPolygon p) {
1639 int[] polygon = this.getPolygon(p.getPolygonId());
1640 double sumX = 0.0;
1641 double sumY = 0.0;
1642 double sumZ = 0.0;
1643 for(int i = 0; i < polygon.length; i++) {
1644 double[] v = getVertex(polygon[i]);
1645 sumX += v[0];
1646 sumY += v[1];
1647 sumZ += v[2];
1648 }
1649 return new Location(sumX / polygon.length, sumY / polygon.length, (sumZ / polygon.length) + NavMeshConstants.liftPolygonLocation);
1650 }
1651
1652
1653
1654
1655
1656 public LevelGeometry getLevelGeometry() {
1657 return this.levelGeometry;
1658 }
1659
1660
1661
1662
1663
1664
1665
1666
1667 public double getDistanceFromEdge(Location location, Vector2d vector, double rayLength) {
1668 if(rayLength <= 0) return 0;
1669
1670
1671 vector.normalize();
1672 vector.x *= rayLength;
1673 vector.y *= rayLength;
1674 Location end = new Location(location.x + vector.x, location.y + vector.y);
1675
1676
1677
1678 Line2D ray = new Line2D(location.x, location.y, end.x, end.y);
1679
1680 int pId = this.getPolygonIdByLocation(location);
1681 if(pId < 0) return 0;
1682
1683
1684
1685
1686
1687
1688 int currentPolygonId = pId;
1689 int lastPolygonId = -1;
1690 int nextPolygonId = -1;
1691
1692
1693 Point2D cross = null;
1694 int v1=-1,v2=-1;
1695 int[] polygon = getPolygon(currentPolygonId);
1696 for(int i = 0; i<polygon.length; i++) {
1697 v1 = polygon[i];
1698 v2 = polygon[((i==polygon.length-1) ? 0 : i+1)];
1699 double[] vertex1 = getVertex(v1);
1700 double[] vertex2 = getVertex(v2);
1701 Line2D edge = new Line2D(vertex1[0],vertex1[1],vertex2[0],vertex2[1]);
1702 cross = ray.getIntersection(edge);
1703 if(cross!=null) {
1704 if(cross.x <= Math.max(edge.p1.x, edge.p2.x) && cross.x >= Math.min(edge.p1.x, edge.p2.x)
1705 && cross.x <= Math.max(ray.p1.x, ray.p2.x) && cross.x >= Math.min(ray.p1.x, ray.p2.x)
1706 ) {
1707
1708 break;
1709 } else {
1710
1711 cross = null;
1712 }
1713 }
1714 }
1715
1716
1717 if(cross==null || ray.p1.getDistance(cross) >= rayLength ) return 0;
1718
1719
1720 nextPolygonId = getNeighbourPolygon(currentPolygonId,v1,v2);
1721 if(nextPolygonId == -1) {
1722
1723 return ray.p1.getDistance(cross);
1724 } else {
1725
1726
1727 vector = ((Vector2d) vector.clone());
1728 vector.normalize();
1729 Location crossLocation = new Location(cross.x+vector.x, cross.y+vector.y, location.z);
1730 return getDistanceFromEdge(crossLocation , vector, rayLength - ray.p1.getDistance(cross));
1731
1732 }
1733 }
1734
1735
1736
1737
1738
1739
1740
1741
1742 public double getDistanceFromEdge(Location location, Vector2d vector) {
1743 return getDistanceFromEdge(location, vector, Double.MAX_VALUE);
1744 }
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754 private int getNeighbourPolygon(int currentPolygonId, int v1, int v2) {
1755
1756 List<Integer> neighbours = this.getNeighbourIdsToPolygon(currentPolygonId);
1757 for (Integer neighbour : neighbours) {
1758
1759 int[] polygon2 = getPolygon(neighbour);
1760
1761 boolean containsV1 = false, containsV2 = false;
1762 for(int j = 0; j < polygon2.length; j++) {
1763 if(polygon2[j]==v1) containsV1 = true;
1764 if(polygon2[j]==v2) containsV2 = true;
1765 }
1766
1767 if(containsV1 && containsV2) {
1768 return neighbour;
1769 }
1770 }
1771
1772 return -1;
1773 }
1774
1775 }