View Javadoc

1   package nl.tudelft.goal.ut2004.visualizer.timeline.map;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.LinkedList;
6   import java.util.List;
7   import java.util.logging.Level;
8   import java.util.logging.Logger;
9   
10  import javax.media.opengl.GL;
11  import javax.media.opengl.GL2;
12  import javax.media.opengl.glu.GLU;
13  import javax.media.opengl.glu.GLUquadric;
14  
15  import nl.tudelft.goal.ut2004.visualizer.map.BlendTriangle;
16  import nl.tudelft.goal.ut2004.visualizer.services.ISelectionHandler;
17  
18  import com.jogamp.opengl.util.gl2.GLUT;
19  import static javax.media.opengl.GL.*; // GL constants
20  import static javax.media.opengl.GL2.*; // GL2 constants
21  
22  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
23  import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation;
24  import cz.cuni.amis.pogamut.unreal.communication.worldview.map.IUnrealMap;
25  
26  /**
27   * Sub-renderer for object {@link IRenderableUTAgent}
28   * 
29   * <b>Implementation note:</b> be careful when asking twice for same field of
30   * agent (like agent.getRotation()), because it is dependant on time when you
31   * ask and returned value is not guaranteed to be same (like first can be valid
32   * and second null).
33   * 
34   * @author Honza
35   */
36  public class UTAgentSubGLRenderer implements ISubGLRenderer<IRenderableUTAgent> {
37  
38  	private static Logger logger;
39  	private static GLU glu;
40  	private static GLUT glut;
41  	private static GLUquadric quadratic;
42  
43  	/**
44  	 * The agent this class renders
45  	 */
46  	private final IRenderableUTAgent agent;
47  	private final IUnrealMap map;
48  	private final double SPHERE_RADIUS = 60;
49  	private final int SPHERE_SLICES = 32;
50  	private final int SPHERE_STACKS = 32;
51  
52  	/**
53  	 * Create a new subrenderer with passed agent as source of data.
54  	 * 
55  	 * @param renderableUTAgent
56  	 *            agent used as source of data.
57  	 */
58  	public UTAgentSubGLRenderer(IRenderableUTAgent utAgent, IUnrealMap map) {
59  		this.agent = utAgent;
60  		this.map = map;
61  	}
62  
63  	@Override
64  	public void prepare(GL gl) {
65  		glu = new GLU();
66  		glut = new GLUT();
67  		quadratic = glu.gluNewQuadric();
68  
69  		logger = Logger.getLogger("TLMapRenderer");
70  		logger.setLevel(Level.INFO);
71  	}
72  
73  	@Override
74  	public IRenderableUTAgent getObject() {
75  		return agent;
76  	}
77  
78  	@Override
79  	public void render(GL gla) {
80  		GL2 gl = gla.getGL2();
81  
82  		try {
83  			Location entityLocation = agent.getLocation();
84  			if (entityLocation == null) {
85  				return;
86  			}
87  
88  			Location center = new Location(entityLocation.x, entityLocation.y, entityLocation.z + SPHERE_RADIUS * 1.1);
89  			GlColor color = new GlColor(agent.getColor());
90  
91  			gl.glLoadName(agent.getGLName());
92  
93  			renderAgent(gl, color, center);
94  			gl.glLoadName(-1);
95  			renderInfo(gl, color, center);
96  			renderFade(gl, color, agent.getFadeLine());
97  
98  			// TODO(MP:) Not rendering map events.
99  			// for (MapEvent mapEvent : agent.getMapEvents()) {
100 			// if (!mapEvent.shouldFollowPlayer()) {
101 			// renderPlacedMapEvent(gl, color, mapEvent);
102 			// }
103 			// }
104 		} catch (RuntimeException e) {
105 			e.printStackTrace();
106 			// TODO handle situation when an agent disconnects
107 		}
108 	}
109 
110 	/**
111 	 * Render agent at specified position
112 	 * 
113 	 * @param gl
114 	 * @param position
115 	 */
116 	private void renderAgent(GL gla, GlColor color, Location position) {
117 		GL2 gl = gla.getGL2();
118 
119 		gl.glPushMatrix();
120 		{
121 			gl.glTranslated(position.x, position.y, position.z);
122 			// draw sphere
123 			gl.glColor4d(color.r, color.g, color.b, color.a);
124 			glu.gluSphere(quadratic, SPHERE_RADIUS, SPHERE_SLICES, SPHERE_STACKS);
125 
126 			// Render selected agents differently
127 			// ISelectionHandler selectionHandler = getSelectionHandler();
128 			// if (selectionHandler != null) {
129 			// Collection c = selectionHandler.getEnvironmentSelection(map);
130 			// // TODO(MP): Optimize getting agent.
131 			// for (Object o : c) {
132 			// if (agent.getDataSource().equals(o)) {
133 			// gl.glColor3d(0.3, 0.3, 0.3);
134 			// glu.gluDisk(quadratic, SPHERE_RADIUS * 1.2, SPHERE_RADIUS * 1.5,
135 			// 32, 3);
136 			// }
137 			// }
138 			// }
139 		}
140 		gl.glPopMatrix();
141 
142 		Rotation rot = agent.getRotation();
143 		if (rot != null) {
144 			renderRotation(gl, new GlColor(1, 0, 0), position, rot);
145 		}
146 
147 		/*
148 		 * if (window == null) { window = new GLTextWindow(gl, 100, 20, 200, 50,
149 		 * "Hi, this is a test text 1234567890"); } window.render();
150 		 */
151 	}
152 
153 	private ISelectionHandler getSelectionHandler() {
154 		// TODO(MP): Get selection handler.
155 		return null;
156 	}
157 
158 	// private GLTextWindow window;
159 	/**
160 	 * Draw rotation arrow
161 	 * 
162 	 * @param gl
163 	 * @param color
164 	 *            What color should arrow be
165 	 * @param center
166 	 *            Where is center of arrow
167 	 * @param rotation
168 	 *            In what direction does arrow points
169 	 */
170 	private void renderRotation(GL gla, GlColor color, Location center, Rotation rotation) {
171 		GL2 gl = gla.getGL2();
172 
173 		gl.glPushMatrix();
174 		{
175 			gl.glTranslated(center.x, center.y, center.z);
176 
177 			Location endOfArrow = rotation.toLocation().getNormalized().scale(SPHERE_RADIUS * 2.5);
178 
179 			gl.glBegin(GL.GL_LINES);
180 			gl.glColor4d(color.r, color.g, color.b, color.a);
181 			gl.glVertex3d(0, 0, 0);
182 			gl.glVertex3d(endOfArrow.x, endOfArrow.y, endOfArrow.z);
183 			gl.glEnd();
184 
185 			gl.glTranslated(endOfArrow.x, endOfArrow.y, endOfArrow.z);
186 			// XXX: This works only in 2D, not 3D, because I am not in the mood
187 			// to figure out direction of Roll, Yaw and Pitch as well as order
188 			// of
189 			// transformations. And rotation.toLocation() returns 2D coords
190 			// anyway.
191 
192 			double yaw = rotation.getYaw() / 32767 * 180; // left right, aka
193 															// around z
194 			double roll = rotation.getRoll() / 32767 * 180; // clockwise/counter?
195 															// around x
196 			double pitch = rotation.getPitch() / 32767 * 180; // up and down,
197 																// around y
198 
199 			/*
200 			 * gl.glRotated(pitch, ); gl.glRotated(yaw, ); gl.glRotated(roll, );
201 			 */
202 			// return res.mul(pitch).mul(yaw).mul(roll);
203 
204 			if (logger.isLoggable(Level.FINE))
205 				logger.fine(" Rotation: Yaw " + yaw + " roll " + roll + " pitch " + pitch);
206 
207 			// gl.glRotated(roll, 1,0,0);
208 			gl.glRotated(yaw, 0, 0, 1);
209 			// gl.glRotated(pitch, 0,1,0);
210 
211 			gl.glRotated(90, 0, 1, 0);
212 
213 			glut.glutSolidCone(20, 40, 16, 16);
214 		}
215 		gl.glPopMatrix();
216 
217 	}
218 
219 	/**
220 	 * Render passed fade line in passed color.
221 	 * 
222 	 * @param gl
223 	 * @param fadeline
224 	 *            Data about fadeline
225 	 * @param color
226 	 *            what color should fadeline be drawn
227 	 */
228 	private void renderFade(GL gla, GlColor color, IFadeLine fadeline) {
229 		GL2 gl = gla.getGL2();
230 
231 		gl.glEnable(GL_BLEND);
232 		gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
233 
234 		gl.glBegin(GL_QUADS);
235 
236 		Location lastLocation = null;
237 		double lastAlpha = 0; // how much is point opaque
238 
239 		for (long ms = 0; ms < fadeline.getDuration(); ms += 100) {
240 			Location currentLocation = fadeline.getPosition(ms);
241 			double currentAlpha = ((double) ms) / fadeline.getDuration();
242 
243 			if (lastLocation == null) {
244 				lastLocation = currentLocation;
245 				lastAlpha = currentAlpha;
246 				continue;
247 			}
248 
249 			if (currentLocation != null) {
250 				pushFadeQuad(gl, color, currentLocation, lastLocation, currentAlpha, lastAlpha);
251 
252 				lastLocation = currentLocation;
253 				lastAlpha = currentAlpha;
254 			}
255 		}
256 
257 		gl.glEnd();
258 
259 		gl.glDisable(GL_BLEND);
260 	}
261 
262 	/**
263 	 * Queue proper OGL coordinates and colors to render part of fadeline from
264 	 * start to end as quad.
265 	 * 
266 	 * @param gl
267 	 * @param color
268 	 *            What color should be used fo quad.
269 	 * @param start
270 	 *            Starting location
271 	 * @param end
272 	 *            Ending location
273 	 * @param startAlpha
274 	 *            How much opque should be quad at start
275 	 *            0(transparent)-1(opaque)
276 	 * @param endAlpha
277 	 *            How much should quad be at end 0(transparent)-1(opaque)
278 	 */
279 	private void pushFadeQuad(GL gla, GlColor color, Location start, Location end, double startAlpha, double endAlpha) {
280 		GL2 gl = gla.getGL2();
281 
282 		// directional vector from last to current
283 		Location dir = Location.sub(start, end).setZ(0);
284 
285 		if (dir.getLength() == 0) {
286 			return;
287 		}
288 
289 		dir = dir.getNormalized();
290 
291 		// 90 degrees rotated vector
292 		Location trans = new Location(dir.y, -dir.x, 0);
293 		trans = trans.scale(6);
294 
295 		// line at last location
296 		Location p1 = end.sub(trans);
297 
298 		Location p2 = end.add(trans);
299 
300 		gl.glColor4d(color.r, color.g, color.b, 1 - endAlpha);
301 		gl.glVertex3d(p1.x, p1.y, p1.z + SPHERE_RADIUS);
302 		gl.glVertex3d(p2.x, p2.y, p2.z + SPHERE_RADIUS);
303 
304 		// line at current location
305 		Location p3 = start.add(trans);
306 		Location p4 = start.sub(trans);
307 
308 		gl.glColor4d(color.r, color.g, color.b, 1 - startAlpha);
309 		gl.glVertex3d(p3.x, p3.y, p3.z + SPHERE_RADIUS);
310 		gl.glVertex3d(p4.x, p4.y, p4.z + SPHERE_RADIUS);
311 	}
312 
313 	/**
314 	 * Render info about agent (in most cases event messages) and its name.
315 	 * TODO: Ugly code, refactor
316 	 * 
317 	 * @param gl
318 	 * @param color
319 	 *            What color should be used to render info.
320 	 * @param location
321 	 *            position of agent
322 	 */
323 	private void renderInfo(GL gla, GlColor color, Location location) {
324 
325 		GL2 gl = gla.getGL2();
326 
327 		// get text
328 		List<String> infos = new ArrayList<String>(agent.getAssociatedInfo());
329 		infos.add(0, '*' + agent.getName() + '*');
330 
331 		Location topHead = new Location(location.x, location.y, location.z + 2 * SPHERE_RADIUS * 1.1);
332 
333 		Location top2d = GLTools.getScreenCoordinates(gl, glu, topHead);
334 
335 		int lineGap = 12;
336 		int font = GLUT.BITMAP_HELVETICA_10;
337 
338 		int maxWidth = 0;
339 		for (String line : infos) {
340 			int lineWidth = glut.glutBitmapLength(font, line);
341 			if (lineWidth > maxWidth) {
342 				maxWidth = lineWidth;
343 			}
344 		}
345 
346 		// update starting position
347 		top2d = new Location(top2d.x - maxWidth / 2, top2d.y + (infos.size() - 1) * lineGap, top2d.z);
348 
349 		GlColor textColor = color.getMixedWith(new GlColor(0, 0, 0), 80);
350 
351 		gl.glColor3d(textColor.r, textColor.g, textColor.b);
352 		for (int i = 0; i < infos.size(); i++) {
353 			String text = infos.get(i);
354 			if (i == 0) {
355 				gl.glColor3d(color.r, color.g, color.b);
356 			} else {
357 				gl.glColor3d(textColor.r, textColor.g, textColor.b);
358 				// gl.glColor3d(0, 0, 0);
359 			}
360 			Location textPos = GLTools.getWorldCoordinates(gl, glu, top2d);
361 			gl.glRasterPos3d(textPos.x, textPos.y, textPos.z);
362 			glut.glutBitmapString(font, text);
363 
364 			top2d = top2d.addY(-lineGap);
365 		}
366 	}
367 
368 	// /**
369 	// * Render {@link MapEvent} that is placed at fixed place (=not at the
370 	// player).
371 	// *
372 	// * @param gl
373 	// * @param color
374 	// * what color should be map event drawn.
375 	// * @param mapEvent
376 	// * map event to render.
377 	// */
378 	// private void renderPlacedMapEvent(GL gl, GlColor color, MapEvent
379 	// mapEvent) {
380 	// LogMapMark mapMark = mapEvent.getMark();
381 	// Location position = mapMark.getLocation();
382 	//
383 	// if (logger.isLoggable(Level.FINE))
384 	// logger.fine(" MSG: " + mapMark.getMessage() + ", LOC: " + position);
385 	// // gl.glEnable(GL.GL_BLEND);
386 	// // gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
387 	//
388 	// gl.glPushMatrix();
389 	// {
390 	// gl.glTranslated(position.x, position.y, position.z + SPHERE_RADIUS *
391 	// 1.1);
392 	//
393 	// gl.glColor4d(color.r, color.g, color.b, color.a);
394 	// // glu.gluSphere(quadratic, SPHERE_RADIUS * 3, SPHERE_SLICES,
395 	// SPHERE_STACKS);
396 	// glu.gluCylinder(quadratic, SPHERE_RADIUS, SPHERE_RADIUS, 2 *
397 	// SPHERE_RADIUS, 4, 1);
398 	// }
399 	// gl.glPopMatrix();
400 	//
401 	// gl.glDisable(GL.GL_DEPTH_TEST);
402 	// {
403 	// gl.glColor3d(0, 0, 0);
404 	// gl.glRasterPos3d(position.x, position.y, position.z);
405 	// glut.glutBitmapString(GLUT.BITMAP_HELVETICA_12, mapEvent.getMessage());
406 	// gl.glColor3d(1, 1, 1);
407 	// }
408 	// gl.glEnable(GL.GL_DEPTH_TEST);
409 	//
410 	// // gl.glDisable(GL.GL_BLEND);
411 	// }
412 
413 	@Override
414 	public List<BlendTriangle> getBlendedTris() {
415 		return new LinkedList<BlendTriangle>();
416 	}
417 }