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