View Javadoc

1   package nl.tudelft.goal.ut2004.visualizer.timeline.map;
2   
3   import java.awt.Point;
4   import java.nio.IntBuffer;
5   import java.util.Arrays;
6   import java.util.Collections;
7   import java.util.Comparator;
8   import java.util.LinkedList;
9   import java.util.List;
10  
11  import javax.media.opengl.GL;
12  import javax.media.opengl.GL2;
13  import javax.media.opengl.GLAutoDrawable;
14  import javax.media.opengl.GLEventListener;
15  import javax.media.opengl.glu.GLU;
16  import javax.vecmath.Vector3d;
17  import static javax.media.opengl.GL.*; // GL constants
18  import static javax.media.opengl.GL2.*; // GL2 constants
19  import com.jogamp.opengl.util.gl2.GLUT;
20  import nl.tudelft.goal.ut2004.visualizer.map.BlendTriangle;
21  
22  import com.jogamp.common.nio.Buffers;
23  
24  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
25  
26  /**
27   * Renderer renders the environment of the unreal map according to passed
28   * arguments in the constructor. It renders the map, agents inside from
29   * specified viewpoint.
30   * 
31   * @author Honza
32   */
33  public class EnvironmentRenderer implements GLEventListener {
34  	private static GLU glu = new GLU();
35  	private static GLUT glut = new GLUT();
36  
37  	private MapViewpoint viewpoint;
38  	private GLRendererCollection<IRenderableUTAgent> agentRenderes;
39  	private GLRendererCollection<IRenderableWorldObject> objectRenderes;
40  	private MapRenderer mapRenderer;
41  
42  	public EnvironmentRenderer(MapViewpoint viewpoint, GLRendererCollection<IRenderableUTAgent> agentRenderes,
43  			GLRendererCollection<IRenderableWorldObject> objectRenderes, MapRenderer mapRenderer) {
44  		this.viewpoint = viewpoint;
45  		this.agentRenderes = agentRenderes;
46  		this.objectRenderes = objectRenderes;
47  		this.mapRenderer = mapRenderer;
48  
49  	}
50  
51  	@Override
52  	public synchronized void init(GLAutoDrawable glDrawable) {
53  		GL2 gl = glDrawable.getGL().getGL2();
54  
55  		gl.glMatrixMode(GL_PROJECTION);
56  		gl.glLoadIdentity();
57  		gl.glMatrixMode(GL_MODELVIEW);
58  		gl.glLoadIdentity();
59  
60  		gl.glEnable(GL.GL_DEPTH_TEST);
61  		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
62  
63  		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
64  		gl.glClearDepth(1.0);
65  		gl.glShadeModel(GL_SMOOTH); // try setting this to GL_FLAT and see
66  										// what happens.
67  
68  		/*
69  		 * float[] lightAmbient = new float[]{0.5f, 0.5f, 0.5f, 1.0f}; float[]
70  		 * lightDiffuse = new float[]{1.0f, 1.0f, 1.0f, 1.0f}; float[]
71  		 * lightPosition = new float[]{ 0, 0, 0, //
72  		 * (float)levelBox.getFlag().getCenterX(), //
73  		 * (float)levelBox.getFlag().getCenterY(), //
74  		 * (float)levelBox.getFlag().getCenterZ() +
75  		 * (float)levelBox.getFlag().getDeltaZ(), 1.0f};
76  		 * gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT,
77  		 * FloatBuffer.wrap(lightAmbient)); gl.glLightfv(GL.GL_LIGHT0,
78  		 * GL.GL_DIFFUSE, FloatBuffer.wrap(lightDiffuse));
79  		 * gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION,
80  		 * FloatBuffer.wrap(lightPosition)); gl.glEnable(GL.GL_LIGHT0);
81  		 */
82  		gl.glDisable(GL_LIGHTING);
83  
84  		// Now preprocess our data for rendering
85  		agentRenderes.prepare(gl);
86  		objectRenderes.prepare(gl);
87  		mapRenderer.prepare(gl);
88  	}
89  
90  	@Override
91  	public synchronized void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
92  		GL2 gl = drawable.getGL().getGL2();
93  		setProjection(gl, x, y, width, height);
94  	}
95  
96  	/**
97  	 * Set projection, viewpoinbt and so on. Used every rendered frame.
98  	 */
99  	private synchronized void setProjection(GL2 gl, int x, int y, int width, int height) {
100 		if (height <= 0) { // avoid a divide by zero error!
101 			height = 1;
102 		}
103 		float h = (float) width / (float) height;
104 		gl.glViewport(0, 0, width, height);
105 		gl.glMatrixMode(GL_PROJECTION);
106 		gl.glLoadIdentity();
107 		glu.gluPerspective(viewpoint.getViewAngle(), h, 10.0, 100000.0);
108 		gl.glMatrixMode(GL_MODELVIEW);
109 		gl.glLoadIdentity();
110 	}
111 
112 	/**
113 	 * Clear the screen, set up correct observer position and other stuff
114 	 * 
115 	 * @param gl
116 	 */
117 	private synchronized void prepareCanvas(GL2 gl) {
118 		// Clear the drawing area
119 		gl.glEnable(GL_DEPTH_TEST); // GL.GL_NORMALIZE|
120 		gl.glClearColor(0.45f, 0.45f, 0.45f, 0f);
121 
122 		gl.glClear(GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
123 
124 		// Reset the modelview to the "identity"
125 		gl.glMatrixMode(GL_MODELVIEW);
126 		gl.glLoadIdentity();
127 
128 		// Move the "drawing cursor" around
129 		Location observerLoc = viewpoint.getLocation();
130 		Location eyeLoc = viewpoint.getEye();
131 		Vector3d upVec = viewpoint.getUp();
132 		// gl.glTranslated(observerLocation.x, observerLocation.y,
133 		// observerLocation.z);
134 		glu.gluLookAt(eyeLoc.x, eyeLoc.y, eyeLoc.z, observerLoc.x, observerLoc.y, observerLoc.z, upVec.x, upVec.y,
135 				upVec.z);
136 
137 		gl.glScaled(1., 1., -1.);
138 	}
139 
140 	int loops = 0;
141 
142 	@Override
143 	public synchronized void display(GLAutoDrawable glDrawable) {
144 		GL2 gl = glDrawable.getGL().getGL2();
145 
146 		int viewport[] = new int[4];
147 		gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
148 
149 		// render using selection mode
150 
151 		// Assign a buffer for selection mode values, the buffer is later used
152 		// to retrieve name stack. Has to be prepared before GL_SELECT mode
153 		int[] selectBufferArray = new int[512];
154 		IntBuffer selectBuffer = Buffers.newDirectIntBuffer(selectBufferArray.length);
155 
156 		gl.glSelectBuffer(selectBufferArray.length, selectBuffer); // size of
157 																	// buffer,
158 																	// buffer
159 																	// itself
160 
161 		gl.glRenderMode(GL_SELECT); // has to be enabled before any
162 										// manipulation with selection buffer
163 
164 		// initialize name stack to empty stack
165 		gl.glInitNames();
166 		gl.glPushName(-1); // because glLoadName replaces top position on stack,
167 							// we have to have something there.
168 
169 		// pick only in small view volume
170 		gl.glMatrixMode(GL_PROJECTION);
171 		gl.glPushMatrix();
172 		{
173 			gl.glLoadIdentity();
174 			// synchronized because setSelectpoint can occur and we want only
175 			// one thing
176 			glu.gluPickMatrix(selectPoint.x, viewport[3] - selectPoint.y, 1.0f, 1.0f, viewport, 0);
177 			glu.gluPerspective(viewpoint.getViewAngle(), (float) viewport[2] / (float) viewport[3], 10.0, 100000.0);
178 
179 			prepareCanvas(gl);
180 			mapRenderer.render(gl);
181 			agentRenderes.render(gl);
182 			objectRenderes.render(gl);
183 			gl.glMatrixMode(GL_PROJECTION);
184 		}
185 		gl.glPopMatrix();
186 
187 		gl.glMatrixMode(GL_MODELVIEW);
188 		// now that everything was rendered I should have all objects that would
189 		// be in view volume in selectBuffer in following format:
190 		// * [i*4+0] - number of names on the name stack when the hit occured
191 		// * [i*4+1] - minimum z
192 		// * [i*4+2] - maximum z
193 		// * [i*4+3] - name of object (integer id)
194 		int numOfHits = gl.glRenderMode(GL_RENDER);
195 		selectBuffer.get(selectBufferArray);
196 
197 		// stuff it to selectedObjects
198 		selectedObjects = new int[numOfHits];
199 		for (int hitIndex = 0; hitIndex < numOfHits; hitIndex++) {
200 			selectedObjects[hitIndex] = selectBufferArray[hitIndex * 4 + 3];
201 		}
202 
203 		this.selectDirtyFlag = false;
204 
205 		prepareCanvas(gl);
206 
207 		// Render all stuff to screen
208 		mapRenderer.render(gl);
209 		agentRenderes.render(gl);
210 		objectRenderes.render(gl);
211 
212 		gl.glEnable(GL_BLEND);
213 		gl.glEnable(GL_LIGHTING);
214 		gl.glBegin(GL_TRIANGLES);
215 
216 		// render blended triangles, unsorted for now
217 		List<BlendTriangle> blendPolys = painterSort(mapRenderer.getBlendedTris());
218 
219 		for (BlendTriangle triangle : blendPolys) {
220 			for (int i = 0; i < 3; i++) {
221 				GlColor col = triangle.getVerts()[i].getColor();
222 				gl.glColor4d(col.r, col.g, col.b, col.a);
223 
224 				Location loc = triangle.getVerts()[i].getLocation();
225 				gl.glVertex3d(loc.x, loc.y, loc.z);
226 			}
227 		}
228 		gl.glEnd();
229 		gl.glDisable(GL_LIGHTING);
230 		gl.glDisable(GL_BLEND);
231 
232 		// render debug text
233 		String res = "[" + selectPoint.x + ", " + selectPoint.y + "] # " + numOfHits;
234 		for (int objId : selectedObjects) {
235 			res += ":" + objId;
236 		}
237 		renderText(gl, res, 0, 0, GLUT.BITMAP_HELVETICA_12);
238 
239 		loops++;
240 	}
241 
242 	/**
243 	 * Sort polygons in the list so they can be correctly rendered from
244 	 * back-to-front. Basically preparation for painter's algorithm.
245 	 * 
246 	 * XXX: Implement painter correctly, this is mererly placeholder, based on
247 	 * max z-value of poly
248 	 * 
249 	 * @param polys
250 	 *            List of polygons that are supposed to be sorted
251 	 * @return List of triangles that can be rendered back-to-front without
252 	 *         problems
253 	 */
254 	private List<BlendTriangle> painterSort(List<BlendTriangle> orgPolys) {
255 		List<BlendTriangle> polys = new LinkedList<BlendTriangle>(orgPolys);
256 		final Location eyeLoc = this.viewpoint.getEye();
257 
258 		Collections.sort(polys, new Comparator<BlendTriangle>() {
259 			@Override
260 			public int compare(BlendTriangle o1, BlendTriangle o2) {
261 				if (maxDistance(o1) < maxDistance(o2))
262 					return -1;
263 				if (maxDistance(o1) > maxDistance(o2))
264 					return 1;
265 
266 				return 0;
267 			}
268 
269 			/**
270 			 * Return max euclidian distance between eyeLoc and some vertex of
271 			 * triangle
272 			 */
273 			private double maxDistance(BlendTriangle tri) {
274 				double max = Double.MIN_VALUE;
275 
276 				for (int i = 0; i < 3; i++) {
277 					Location vertloc = tri.getVerts()[i].getLocation();
278 					if (Location.getDistance(vertloc, eyeLoc) > max) {
279 						max = Location.getDistance(vertloc, eyeLoc);
280 					}
281 				}
282 				return max;
283 			}
284 
285 		});
286 
287 		return polys;
288 	}
289 
290 	private void renderText(GL2 gl, String text, int x, int y, int font) {
291 		int viewport[] = new int[4];
292 		gl.glGetIntegerv(GL_VIEWPORT, viewport, 0);
293 
294 		gl.glMatrixMode(GL_MODELVIEW);
295 		gl.glPushMatrix();
296 		gl.glLoadIdentity();
297 		gl.glMatrixMode(GL_PROJECTION);
298 		gl.glPushMatrix();
299 		gl.glLoadIdentity();
300 		{
301 			gl.glOrtho(0, viewport[2], 0, viewport[3], -1, 1);
302 			gl.glColor3d(1, 1, 1);
303 			gl.glRasterPos3d(0, 0, 0);
304 			glut.glutBitmapString(font, text);
305 		}
306 		gl.glMatrixMode(GL_PROJECTION);
307 		gl.glPopMatrix();
308 		gl.glMatrixMode(GL_MODELVIEW);
309 		gl.glPopMatrix();
310 
311 	}
312 
313 	// Flag if the stored selected objects are valid for select point
314 	private boolean selectDirtyFlag = true;
315 	// what point in viewport do we want objects from. In window mode, so change
316 	// when want to use
317 	private Point selectPoint = new Point();
318 	// list of objects from selectPoint, if selectDirtyFlag is false
319 	private int[] selectedObjects = new int[0];
320 
321 	public synchronized int[] getSelectedObjects() throws IllegalStateException {
322 		if (selectDirtyFlag) {
323 			throw new IllegalStateException(
324 					"Not objects from selected point. Did you call display() after setSelectPoint()?");
325 		}
326 
327 		return Arrays.copyOf(selectedObjects, selectedObjects.length);
328 	}
329 
330 	/**
331 	 * Set select point you want rendered objects list from.
332 	 * 
333 	 * @param point
334 	 *            point we want objects from. In Window mode = left top is [0,0]
335 	 */
336 	public synchronized void setSelectPoint(Point point) {
337 		if (point == null) {
338 			throw new IllegalArgumentException("Point cannot be null");
339 		}
340 
341 		this.selectPoint = new Point(point);
342 		this.selectDirtyFlag = true;
343 	}
344 
345 	SelectionHit hi = new SelectionHit(loops);
346 
347 	public static class SelectionHit {
348 		/*
349 		 * public int prev = selectBufferArray[hitIndex * 4 + 0]; public int
350 		 * minZ = selectBufferArray[hitIndex * 4 + 1]; public int maxZ =
351 		 * selectBufferArray[hitIndex * 4 + 2]; public int name =
352 		 * selectBufferArray[hitIndex * 4 + 3];
353 		 */
354 		private SelectionHit(int z) {
355 
356 		}
357 	}
358 
359 	@Override
360 	public void dispose(GLAutoDrawable arg0) {
361 		// TODO Auto-generated method stub
362 
363 	}
364 }