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 import java.util.ArrayList;
21 import java.util.Random;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24 import math.geom2d.Point2D;
25 import math.geom2d.line.Line2D;
26 import math.geom2d.line.StraightLine2D;
27
28
29
30
31
32
33
34 public class BSPNode implements java.io.Serializable {
35
36 public transient NavMesh navmesh;
37
38 public ArrayList polys;
39 public BSPNode parent;
40
41 public StraightLine2D sepLine;
42
43 public BSPNode left;
44 public BSPNode right;
45
46 private Random random;
47
48
49
50
51
52
53
54
55
56
57
58 public BSPNode(NavMesh m, BSPNode par) {
59 navmesh = m;
60 parent = par;
61 polys = new ArrayList();
62 random = new Random();
63 sepLine = null;
64 }
65
66 public boolean shouldSplit() {
67 return (polys.size() > NavMeshConstants.stopSplittingNumberOfPolygons);
68 }
69
70 public StraightLine2D findSeparatingLine () throws Exception {
71 System.out.println("findSeparatingLine(): Trying to split " + polys.size() + " polygons...");
72
73
74
75 double bestFoundSplitFactor = NavMeshConstants.maxAllowedSplitFactor;
76 StraightLine2D bestFoundSeparatingLine = null;
77
78 ArrayList candidatePolygons = (ArrayList) polys.clone();
79
80
81 for(int i = 0; i < NavMeshConstants.maxNumberOfPolygonsToTry; i++) {
82 if(candidatePolygons.isEmpty()) break;
83
84 int randomId = 0;
85 Integer polygonId = (Integer) candidatePolygons.get(randomId);
86 int[] polygon = navmesh.getPolygon(polygonId);
87
88
89 for(int j = 0; j<polygon.length; j++) {
90 int v1 = polygon[j];
91 int v2 = polygon[(j+1) % polygon.length];
92 double[] vertex1 = navmesh.getVertex(v1);
93 double[] vertex2 = navmesh.getVertex(v2);
94
95
96 Point2D point2D1 = new Point2D(vertex1[0], vertex1[1]);
97 Point2D point2D2 = new Point2D(vertex2[0], vertex2[1]);
98
99
100 StraightLine2D separatingLine = new StraightLine2D(point2D1, point2D2);
101
102
103 ArrayList[] splittedPolys = splitPolygonsByLine(polys, separatingLine);
104 ArrayList left = splittedPolys[0];
105 ArrayList right = splittedPolys[1];
106
107 int intersectedPolygons = left.size() + right.size() - polys.size();
108
109 double splitFactor = max(left.size() / polys.size(), right.size() / polys.size());
110
111
112 if(splitFactor < bestFoundSplitFactor) {
113 bestFoundSeparatingLine = separatingLine;
114 bestFoundSplitFactor = splitFactor;
115 }
116 }
117
118 candidatePolygons.remove(polygonId);
119 }
120 if(bestFoundSeparatingLine==null) throw new Exception("No good separating line have been found. Splitting " +polys.size()+ " polygons unsuccessfull." );
121 System.out.println("findSeparatingLine(): Returning a split line with splitFactor " + bestFoundSplitFactor);
122 return bestFoundSeparatingLine;
123 }
124
125
126
127
128
129 private ArrayList[] splitPolygonsByLine(ArrayList polysToSplit, StraightLine2D separatingLine) throws Exception {
130
131 ArrayList left = new ArrayList();
132 ArrayList right = new ArrayList();
133
134
135 for(int i = 0; i < polysToSplit.size(); i++) {
136
137
138 boolean isOnLeft = false;
139 boolean isOnRight = false;
140
141 Integer pId = (Integer) polysToSplit.get(i);
142 int[] polygon = navmesh.getPolygon(pId);
143
144
145 for(int j = 0; j < polygon.length; j++) {
146 int vId = polygon[j];
147 double[] vertex = navmesh.getVertex(vId);
148 Point2D point2D = new Point2D(vertex[0], vertex[1]);
149 double dist = separatingLine.getSignedDistance(point2D);
150 if(dist<0) isOnLeft = true;
151 if(dist>0) isOnRight = true;
152 if(isOnLeft && isOnRight) break;
153 }
154 if(isOnLeft) left.add(pId);
155 if(isOnRight) right.add(pId);
156 if(!isOnLeft && !isOnRight) throw new Exception("Something is wrong. The polygon number " +pId+ " seems to be on neither side of the separating line.");
157
158 }
159 ArrayList[] ret = new ArrayList[2];
160 ret[0] = left;
161 ret[1] = right;
162 return ret;
163 }
164
165
166
167
168 public void build() {
169
170
171 if (!shouldSplit()) {
172
173 if(polys.size() > navmesh.getNumberOfPolygonsInBiggestLeaf()) {
174 System.out.println("Setting biggest leaf sofar. Number of polygons in this node is " + polys.size());
175 navmesh.setBiggestLeafInTree(this);
176 }
177 return;
178 }
179
180
181 try {
182 sepLine = findSeparatingLine();
183 }
184 catch(Exception e) {
185 System.out.println("No separating line found for this node. Ok, stop splitting. Number of polygons in this node is " + polys.size());
186
187 if(polys.size() > navmesh.getNumberOfPolygonsInBiggestLeaf()) {
188 System.out.println("Setting biggest leaf sofar. Number of polygons in this node is " + polys.size() + ". The polygons here are: " + polys.toString());
189 navmesh.setBiggestLeafInTree(this);
190 }
191 }
192
193 if(sepLine != null) {
194 try {
195 ArrayList[] splittedPolys = splitPolygonsByLine(polys, sepLine);
196 System.out.println("build(): Left has " + splittedPolys[0].size() + " polygons.");
197 System.out.println("build(): Right has " + splittedPolys[1].size() + " polygons.");
198 System.out.println("build(): Line is crossing " + (splittedPolys[0].size() + splittedPolys[1].size() - polys.size()) + " polygons.");
199 left = new BSPNode(navmesh, this);
200 left.polys = splittedPolys[0];
201 left.build();
202 right = new BSPNode(navmesh, this);
203 right.polys = splittedPolys[1];
204 right.build();
205 } catch (Exception e) {
206 System.out.println("Could not split polys by the given line. This should not happen.");
207 e.printStackTrace();
208 }
209 }
210 }
211
212 private double max(double d1, double d2) {
213 return d1 > d2 ? d1 : d2;
214 }
215
216 public boolean isLeaf() {
217 return (left == null && right == null);
218 }
219
220
221 }