1 package nl.tudelft.goal.ut2004.visualizer.timeline.map;
2
3 import java.awt.Color;
4 import java.nio.ByteBuffer;
5 import java.util.Collection;
6 import java.util.LinkedList;
7 import java.util.List;
8 import java.util.prefs.PreferenceChangeEvent;
9 import java.util.prefs.PreferenceChangeListener;
10
11 import javax.media.opengl.GL;
12 import javax.media.opengl.GL2;
13 import javax.media.opengl.glu.GLU;
14
15 import nl.tudelft.goal.ut2004.visualizer.map.BlendTriangle;
16 import nl.tudelft.goal.ut2004.visualizer.map.BlendVertex;
17 import nl.tudelft.goal.ut2004.visualizer.options.MapFlag;
18 import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
19 import cz.cuni.amis.pogamut.unreal.communication.worldview.map.Box;
20 import cz.cuni.amis.pogamut.unreal.communication.worldview.map.IUnrealMap;
21 import cz.cuni.amis.pogamut.unreal.communication.worldview.map.IUnrealMapInfo;
22 import cz.cuni.amis.pogamut.unreal.communication.worldview.map.IUnrealWaylink;
23 import cz.cuni.amis.pogamut.unreal.communication.worldview.map.IUnrealWaypoint;
24 import static javax.media.opengl.GL.*;
25 import static javax.media.opengl.GL2.*;
26
27
28
29
30
31
32
33
34
35 public class MapRenderer implements ISubGLRenderer<IUnrealMap>, PreferenceChangeListener {
36
37 private GLU glu = new GLU();
38 public final int GRID_SCALE = 1000;
39 public final double NAVPOINT_RADIUS = 30;
40
41
42
43
44 private int gridList = -1;
45 private int pathDisplayList = -1;
46 private int waypointDisplayList = -1;
47 private int backgroundList = -1;
48
49
50
51
52
53 private boolean updateLists = true;
54
55 private IUnrealMap map;
56
57
58
59 private List<PathTriangle> pathTris;
60
61
62
63 private List<BlendTriangle> waypointsTris;
64 private int glName;
65
66
67
68
69
70
71
72
73 public MapRenderer(IUnrealMap map,
74 this.map = map;
75 this.glName = glName;
76
77
78 }
79
80 @Override
81 public IUnrealMap getObject() {
82 return map;
83 }
84
85
86
87
88
89
90
91 @Override
92 public void prepare(GL gl) {
93 gridList = createGridList(gl);
94 backgroundList = createMapBackground(gl);
95
96
97
98
99 waypointsTris = new LinkedList<BlendTriangle>();
100
101 pathTris = createPathsList();
102
103 }
104
105
106
107
108
109
110
111
112
113 public int createMapBackground(GL gla) {
114 GL2 gl = gla.getGL2();
115 IUnrealMapInfo info = map.getInfo();
116
117 if (info == null) {
118 return -1;
119 }
120
121 Location[] pos2D = info.getImagePoints();
122 Location[] posMap = info.getWorldPoints();
123
124 for (int i = 0; i < posMap.length; i++) {
125 posMap[i] = posMap[i].setZ(0);
126 }
127
128
129 Location vec1stTo2nd = Location.sub(pos2D[1], pos2D[0]);
130 Location vec1stTo3rd = Location.sub(pos2D[2], pos2D[0]);
131
132
133 Coeficients[] coef = new Coeficients[4];
134
135 coef[0] = solveEquation(new Location(0, 0, 0), pos2D[0], vec1stTo2nd, vec1stTo3rd);
136
137 coef[1] = solveEquation(new Location(info.getWidth(), 0, 0), pos2D[0], vec1stTo2nd, vec1stTo3rd);
138
139 coef[2] = solveEquation(new Location(info.getWidth(), info.getHeight(), 0), pos2D[0], vec1stTo2nd, vec1stTo3rd);
140
141 coef[3] = solveEquation(new Location(0, info.getHeight(), 0), pos2D[0], vec1stTo2nd, vec1stTo3rd);
142
143 Location map1stTo2nd = Location.sub(posMap[1], posMap[0]);
144 Location map1stTo3rd = Location.sub(posMap[2], posMap[0]);
145
146 Location[] mapLoc = new Location[4];
147
148 for (int i = 0; i < 4; i++) {
149 mapLoc[i] = Location.add(posMap[0],
150 Location.add(map1stTo2nd.scale(coef[i].a), map1stTo3rd.scale(coef[i].b)));
151 }
152
153 int texture = genTexture(gl);
154
155 gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
156
157 glu.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, GL.GL_RGB8, info.getWidth(), info.getHeight(), GL.GL_RGB,
158 GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(info.getImgRGBData()));
159
160 gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
161 gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
162
163 int list = gl.glGenLists(1);
164
165
166 gl.glNewList(list, GL_COMPILE);
167
168 gl.glEnable(GL_TEXTURE_2D);
169
170 gl.glBindTexture(GL_TEXTURE_2D, texture);
171
172 gl.glBegin(GL_QUADS);
173
174 gl.glColor3d(1, 1, 1);
175
176 int i = 0;
177 double z = getFloorZ() + 5;
178 gl.glTexCoord2d(0, 1);
179 gl.glVertex3d(mapLoc[i].x, mapLoc[i].y, z);
180
181 i = 1;
182 gl.glTexCoord2d(1, 1);
183 gl.glVertex3d(mapLoc[i].x, mapLoc[i].y, z);
184
185 i = 2;
186 gl.glTexCoord2d(1, 0);
187 gl.glVertex3d(mapLoc[i].x, mapLoc[i].y, z);
188
189 i = 3;
190 gl.glTexCoord2d(0, 0);
191 gl.glVertex3d(mapLoc[i].x, mapLoc[i].y, z);
192
193 gl.glEnd();
194
195 gl.glDisable(GL.GL_TEXTURE_2D);
196
197 gl.glEndList();
198
199 return list;
200 }
201
202
203
204
205
206
207
208 private int genTexture(GL gl) {
209 int[] textures = new int[1];
210 gl.glGenTextures(1, textures, 0);
211 return textures[0];
212 }
213
214 private double getFloorZ() {
215 Box box = map.getBox();
216 return box.minZ - box.getMinDelta() * 0.20;
217 }
218
219 private double getGridZ() {
220 Box box = map.getBox();
221 return box.minZ - box.getMinDelta() * 0.35;
222 }
223
224 private static class Coeficients {
225
226 public Coeficients(double a, double b) {
227 this.a = a;
228 this.b = b;
229 }
230
231 double a, b;
232 }
233
234
235
236
237
238
239 private Coeficients solveEquation(Location r, Location p, Location u, Location v) {
240
241
242
243 if (u.x != 0) {
244 double multi = u.y / u.x;
245
246
247
248
249
250
251 double b = (-((p.y - r.y) - (p.x - r.x) * multi)) / (v.y - v.x * multi);
252
253
254 double a = -((p.x - r.x) + b * v.x) / u.x;
255
256 return new Coeficients(a, b);
257 } else {
258
259
260
261 double b = (r.x - p.x) / v.x;
262 double a = (r.y - p.y - b * v.y) / u.y;
263 return new Coeficients(a, b);
264 }
265
266 }
267
268
269
270
271
272
273
274
275 private LinkedList<BlendTriangle> createWaypointsList() {
276 LinkedList<BlendTriangle> triangles = new LinkedList<BlendTriangle>();
277 Collection<IUnrealWaypoint> navs = map.vertexSet();
278
279 GlColor color = getWayPointColor();
280 for (IUnrealWaypoint nav : navs) {
281 Location loc = nav.getLocation();
282
283 Location[] points = createCirclePoints(loc, NAVPOINT_RADIUS);
284
285 for (int pointIndex = 0; pointIndex < points.length; pointIndex++) {
286 BlendTriangle triangle = new BlendTriangle();
287
288 triangle.setVertex(0, loc, color);
289 triangle.setVertex(1, points[pointIndex], color);
290 triangle.setVertex(2, points[(pointIndex + 1) % points.length], color);
291
292 triangles.add(triangle);
293 }
294 }
295
296 return triangles;
297 }
298
299 private GlColor getWayPointColor() {
300
301 return new GlColor(Color.BLUE);
302 }
303
304
305
306
307
308
309
310
311
312
313
314 private Location[] createCirclePoints(Location loc, double radius) {
315 Location[] points = new Location[12];
316
317 double stepAngle = 2 * Math.PI / points.length;
318
319 for (int pointIndex = 0; pointIndex < points.length; pointIndex++) {
320 double angle = stepAngle * pointIndex;
321 double xPos = loc.x + radius * Math.cos(angle);
322 double yPos = loc.y + radius * Math.sin(angle);
323 points[pointIndex] = new Location(xPos, yPos, loc.z);
324 }
325
326 return points;
327 }
328
329
330
331
332
333
334 private int createGridList(GL gla) {
335 GL2 gl = gla.getGL2();
336 Box mapBox = map.getBox();
337 int numMainLines = getNumGridLines(mapBox);
338
339 double lineLength = 2 * GRID_SCALE * numMainLines;
340
341 double floorZ = getGridZ();
342 int list = gl.glGenLists(1);
343
344 gl.glNewList(list, GL_COMPILE);
345 {
346 gl.glBegin(GL_LINES);
347 {
348
349 double minY = mapBox.getCenterY() - lineLength / 2;
350 double maxY = mapBox.getCenterY() + lineLength / 2;
351
352 gl.glColor3d(0.45, 0.29, 0.32);
353 gl.glVertex3d(mapBox.getCenterX(), minY, floorZ);
354 gl.glVertex3d(mapBox.getCenterX(), maxY, floorZ);
355
356
357 double minX = mapBox.getCenterX() - lineLength / 2;
358 double maxX = mapBox.getCenterX() + lineLength / 2;
359
360 gl.glColor3d(0.3, 0.57, 0.31);
361 gl.glVertex3d(minX, mapBox.getCenterY(), floorZ);
362 gl.glVertex3d(maxX, mapBox.getCenterY(), floorZ);
363
364
365
366 for (int line = 1; line <= numMainLines; line++) {
367 for (int coef = -1; coef <= 1; coef += 2) {
368
369 gl.glColor3d(0.34, 0.34, 0.34);
370
371 gl.glVertex3d(mapBox.getCenterX() + coef * line * GRID_SCALE, minY, floorZ);
372 gl.glVertex3d(mapBox.getCenterX() + coef * line * GRID_SCALE, maxY, floorZ);
373
374 gl.glVertex3d(minX, mapBox.getCenterY() + coef * line * GRID_SCALE, floorZ);
375 gl.glVertex3d(maxX, mapBox.getCenterY() + coef * line * GRID_SCALE, floorZ);
376
377
378 gl.glColor3d(0.41, 0.41, 0.41);
379 for (int minority = 1; minority < 10; minority++) {
380
381 gl.glVertex3d(mapBox.getCenterX() + coef
382 * ((line - 1) * GRID_SCALE + minority * GRID_SCALE / 10), minY, floorZ);
383 gl.glVertex3d(mapBox.getCenterX() + coef
384 * ((line - 1) * GRID_SCALE + minority * GRID_SCALE / 10), maxY, floorZ);
385
386 gl.glVertex3d(minX, mapBox.getCenterY() + coef
387 * ((line - 1) * GRID_SCALE + minority * GRID_SCALE / 10), floorZ);
388 gl.glVertex3d(maxX, mapBox.getCenterY() + coef
389 * ((line - 1) * GRID_SCALE + minority * GRID_SCALE / 10), floorZ);
390 }
391 }
392 }
393 }
394 gl.glEnd();
395 }
396 gl.glEndList();
397
398 return list;
399 }
400
401
402
403
404
405
406
407 private int getNumGridLines(Box mapBox) {
408 double max = mapBox.getDeltaX() > mapBox.getDeltaY() ? mapBox.getDeltaX() : mapBox.getDeltaY();
409
410 max /= 2;
411 return (int) Math.ceil(max / GRID_SCALE);
412 }
413
414
415
416
417
418
419
420
421 private Location[] createQuadPath(IUnrealWaylink path) {
422 Location[] quads = new Location[3 * 4];
423
424 IUnrealWaypoint from = path.getStart();
425 IUnrealWaypoint to = path.getEnd();
426
427 Location fromLoc = from.getLocation();
428 Location toLoc = to.getLocation();
429
430
431 Location dir = Location.sub(toLoc, fromLoc);
432 dir = dir.setZ(0);
433 dir = dir.getNormalized();
434
435 Location trans = new Location(dir.y, -dir.x, 0);
436
437
438 quads[0] = Location.add(fromLoc, trans.scale(NAVPOINT_RADIUS));
439 quads[1] = Location.add(quads[0], dir.scale(NAVPOINT_RADIUS));
440 quads[2] = Location.add(quads[1], trans.scale(-2 * NAVPOINT_RADIUS));
441 quads[3] = Location.add(quads[2], dir.scale(-NAVPOINT_RADIUS));
442
443
444 quads[4] = Location.add(toLoc, trans.scale(-NAVPOINT_RADIUS));
445 quads[5] = Location.add(quads[4], dir.scale(-NAVPOINT_RADIUS));
446 quads[6] = Location.add(quads[5], trans.scale(2 * NAVPOINT_RADIUS));
447 quads[7] = Location.add(quads[6], dir.scale(NAVPOINT_RADIUS));
448
449 quads[8] = quads[2];
450 quads[9] = quads[1];
451 quads[10] = quads[6];
452 quads[11] = quads[5];
453
454 return quads;
455 }
456
457 private static class PathTriangle extends BlendTriangle {
458
459 private int flags;
460
461 public PathTriangle(int flags) {
462 this.flags = flags;
463 }
464
465 public int getFlags() {
466 return flags;
467 }
468 }
469
470 private LinkedList<PathTriangle> createPathsList() {
471 LinkedList<PathTriangle> triangles = new LinkedList<PathTriangle>();
472 Collection<IUnrealWaylink> paths = map.edgeSet();
473
474 double deltaZ = this.map.getBox().getDeltaZ();
475 double displaceZ = this.map.getBox().minZ;
476
477 for (IUnrealWaylink path : paths) {
478 Location[] quads = createQuadPath(path);
479
480 GlColor lowColor = new GlColor(0.8, 0.8, 0.8);
481 GlColor highColor = new GlColor(0.8, 0, 0);
482
483
484 int quadNum = quads.length / 4;
485 for (int quad = 0; quad < quadNum; quad++) {
486 PathTriangle triOne = new PathTriangle(path.getFlags());
487
488 for (int i = 0; i < 3; i++) {
489 Location vertexLoc = quads[quad * 4 + i];
490 GlColor color = lowColor.getMixedWith(highColor, (vertexLoc.z - displaceZ) / deltaZ);
491 triOne.setVertex(i, vertexLoc, color);
492 }
493
494 PathTriangle triTwo = new PathTriangle(path.getFlags());
495 for (int i = 0; i < 3; i++) {
496 int quadIndex = quad * 4 + ((i + 2) % 4);
497 Location vertexLoc = quads[quadIndex];
498 GlColor color = lowColor.getMixedWith(highColor, (vertexLoc.z - displaceZ) / deltaZ);
499 triTwo.setVertex(i, vertexLoc, color);
500 }
501
502 triangles.add(triOne);
503 triangles.add(triTwo);
504 }
505 }
506 return triangles;
507 }
508
509 @Override
510 public synchronized void render(GL gla) {
511 GL2 gl = gla.getGL2();
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534 if (updateLists || !gl.glIsList(waypointDisplayList) || !gl.glIsList(pathDisplayList)) {
535 updateMapDisplayLists(gl);
536 updateLists = false;
537 }
538
539 gl.glLoadName(glName);
540
541
542
543
544
545
546 gl.glShadeModel(GL_FLAT);
547 gl.glCallList(gridList);
548 gl.glCallList(backgroundList);
549 gl.glCallList(pathDisplayList);
550 gl.glCallList(waypointDisplayList);
551
552 gl.glLoadName(-1);
553 }
554
555
556
557
558
559
560 private synchronized void renderPaths(GL gla, List<PathTriangle> triangles) {
561 GL2 gl = gla.getGL2();
562
563 GlColor lowColor = getLowColor();
564 GlColor highColor = getHighColor();
565
566 boolean includeFlagBehavior = getIncludePathsFlag();
567
568 double deltaZ = this.map.getBox().getDeltaZ();
569 double displaceZ = this.map.getBox().minZ;
570
571 gl.glEnable(GL_COLOR_MATERIAL);
572 gl.glShadeModel(GL_SMOOTH);
573
574 gl.glBegin(GL_TRIANGLES);
575 for (PathTriangle triangle : triangles) {
576 boolean render = false;
577 if (includeFlagBehavior) {
578 render = includeCanRenderPathFlag(triangle.getFlags());
579 } else {
580 render = excludeCanRenderFlag(triangle.getFlags());
581 }
582
583 if (render) {
584 for (BlendVertex v : triangle.getVerts()) {
585 Location vertexLoc = v.getLocation();
586 GlColor color = lowColor.getMixedWith(highColor, (vertexLoc.z - displaceZ) / deltaZ);
587
588 gl.glColor4d(color.r, color.g, color.b, color.a);
589 gl.glVertex3d(vertexLoc.x, vertexLoc.y, vertexLoc.z);
590 }
591 }
592 }
593 gl.glEnd();
594 }
595
596 private boolean getIncludePathsFlag() {
597
598 return true;
599 }
600
601 private GlColor getHighColor() {
602
603 return new GlColor(Color.WHITE);
604 }
605
606 private GlColor getLowColor() {
607
608 return new GlColor(Color.LIGHT_GRAY);
609 }
610
611
612
613
614
615
616
617
618
619 private synchronized boolean includeCanRenderPathFlag(int testedFlag) {
620 for (MapFlag flag : MapFlag.values()) {
621
622 if ((flag.getFlag() & testedFlag) != 0) {
623
624 boolean shouldRender = getShouldRender();
625 if (shouldRender) {
626 return true;
627 }
628 }
629 }
630 return false;
631 }
632
633 private boolean getShouldRender() {
634
635 return true;
636 }
637
638
639
640
641
642
643
644
645 private synchronized boolean excludeCanRenderFlag(int testedFlag) {
646 for (MapFlag flag : MapFlag.values()) {
647
648 if ((flag.getFlag() & testedFlag) != 0) {
649
650 boolean shouldRender = getShouldRender();
651 if (!shouldRender) {
652 return false;
653 }
654 }
655 }
656 return true;
657 }
658
659
660
661
662
663
664
665
666 private synchronized void renderWaypoints(GL gla, List<BlendTriangle> triangles) {
667 GL2 gl = gla.getGL2();
668 GlColor color = getWayPointColor();
669
670 gl.glEnable(GL_COLOR_MATERIAL);
671 gl.glShadeModel(GL_SMOOTH);
672
673 gl.glBegin(GL_TRIANGLES);
674 for (BlendTriangle triangle : triangles) {
675 for (BlendVertex v : triangle.getVerts()) {
676
677 gl.glColor4d(color.r, color.g, color.b, color.a);
678 gl.glVertex3d(v.getLocation().x, v.getLocation().y, v.getLocation().z + 0.1);
679 }
680 }
681 gl.glEnd();
682 }
683
684 @Override
685 public synchronized void preferenceChange(PreferenceChangeEvent evt) {
686 updateLists = true;
687 }
688
689
690
691
692
693 private synchronized void updateMapDisplayLists(GL gla) {
694 GL2 gl = gla.getGL2();
695
696
697 gl.glDeleteLists(pathDisplayList, 1);
698 gl.glDeleteLists(waypointDisplayList, 1);
699
700
701 pathDisplayList = gl.glGenLists(1);
702 gl.glNewList(pathDisplayList, GL_COMPILE);
703 renderPaths(gl, pathTris);
704 gl.glEndList();
705
706 waypointDisplayList = gl.glGenLists(1);
707 gl.glNewList(waypointDisplayList, GL_COMPILE);
708 renderWaypoints(gl, waypointsTris);
709 gl.glEndList();
710 }
711
712 @Override
713 public List<BlendTriangle> getBlendedTris() {
714 List<BlendTriangle> list = new LinkedList<BlendTriangle>();
715 return list;
716 }
717
718
719
720
721 public void destroy() {
722
723 }
724 }