MeshViewer: better double-buffering
[anneal.git] / src / edu / berkeley / qfat / MeshViewer.java
1 package edu.berkeley.qfat;
2 import java.io.*;
3 import java.nio.*;
4 import java.awt.*;
5 import java.awt.event.*;
6 import javax.swing.*;
7 import javax.media.opengl.*;
8 import javax.media.opengl.glu.*;
9 import com.sun.opengl.util.*;
10 import java.util.*;
11 import edu.berkeley.qfat.bind.*;
12 import edu.berkeley.qfat.geom.*;
13 import edu.berkeley.qfat.geom.Point;
14
15 /**
16  *  A basic MeshViewer displays zero or more meshes to the user, in
17  *  wireframe or shaded panels.
18  */
19 public class MeshViewer extends JPanel implements GLEventListener, MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {
20
21     Main main;
22
23     private float tz = 0;
24     private float anglex = 0;
25     private float angley = 0;
26
27     boolean drawEdge = false;
28
29     private Mesh.Vertex closest = null;
30     private Mesh.E      closestEdge = null;
31     private Point       closestOriginallyAt = null;
32     private int         mousex;
33     private int         mousey;
34     private Matrix      projection = null;
35     private Point       clickPoint = null;
36     private GLCanvas    glcanvas;
37     private boolean     updateVisibilities = false;
38     private boolean     updateClosest = false;
39     private boolean     mouseInside = false;
40
41     private HashSet<Mesh> meshes = new HashSet<Mesh>();
42
43     public synchronized void addMesh(Mesh m) { meshes.add(m); }
44     public synchronized void removeMesh(Mesh m) { meshes.remove(m); }
45
46     public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
47     public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { }
48
49     public void addKeyListener(KeyListener kl) { glcanvas.addKeyListener(kl); }
50
51     public synchronized void init(GLAutoDrawable gld) {
52         GL gl = glcanvas.getGL();//gld.getGL();
53
54         gl.glEnable(gl.GL_DOUBLEBUFFER);
55
56         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
57         gl.glViewport(0, 0, 500, 300);
58         gl.glEnable(GL.GL_DEPTH_TEST);
59         gl.glClearDepth(1.0);
60         gl.glDepthFunc(GL.GL_LEQUAL);
61         gl.glMatrixMode(GL.GL_PROJECTION);
62         gl.glLoadIdentity();
63         gl.glMatrixMode(GL.GL_MODELVIEW);
64
65         float mat_specular[] = { 0.5f, 0.5f, 0.5f, 0.5f };
66         float mat_shininess[] = { 50.0f };
67         gl.glShadeModel(GL.GL_SMOOTH);
68         gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, new float[] { 1.0f,    4.0f,  -10.0f, 0.0f }, 0);
69         gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, new float[] { -10.0f, 10.0f,   10.0f, 0.0f }, 0);
70         gl.glLightfv(GL.GL_LIGHT2, GL.GL_POSITION, new float[] { 10.0f, -10.0f,   10.0f, 0.0f }, 0);
71         gl.glLightfv(GL.GL_LIGHT3, GL.GL_POSITION, new float[] { 10.0f,  10.0f,  -10.0f, 0.0f }, 0);
72         gl.glLightfv(GL.GL_LIGHT4, GL.GL_POSITION, new float[] { -10.0f, 10.0f,  -10.0f, 0.0f }, 0);
73         gl.glLightfv(GL.GL_LIGHT5, GL.GL_POSITION, new float[] { 10.0f, -10.0f,  -10.0f, 0.0f }, 0);
74         gl.glEnable(GL.GL_LIGHTING);
75         gl.glEnable(GL.GL_LIGHT0);
76
77         gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE);
78         gl.glEnable(GL.GL_COLOR_MATERIAL);
79
80         display(gld);
81
82         // hack to get around Mac OS bug
83         IntBuffer buf = ByteBuffer.allocateDirect(9*4*4).order(ByteOrder.nativeOrder()).asIntBuffer();
84         gl.glReadPixels(0,0, 1, 1, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf);
85     }
86
87     public synchronized final void display(GLAutoDrawable drawable) {
88         glcanvas.setSize(glcanvas.getParent().getWidth(), glcanvas.getParent().getHeight());
89
90         GL gl = glcanvas.getGL();//drawable.getGL();
91         GLU glu = new GLU();
92         gl.glDrawBuffer(GL.GL_BACK);
93
94         if (updateVisibilities) {
95             updateVisibilities = false;
96             // update vertex visibilities
97             updateVisibility(gl);
98         }
99         gl.glDrawBuffer(GL.GL_BACK);
100
101         if (!mouseInside) gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
102         else gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
103
104         gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
105         gl.glPointSize(5.0f);
106         gl.glLoadIdentity();
107         glu.gluPerspective(50, ((float)drawable.getWidth())/drawable.getHeight(), 0.5, 10);
108
109         glu.gluLookAt(0, 0, -((tz/10)-1), 0, 0, 0, 0, 1, 0);
110         gl.glRotatef(anglex/3, 0, 1, 0);
111         gl.glRotatef(-(angley/3), 1, 0, 0);
112
113         gl.glEnable(GL.GL_LIGHTING);
114         gl.glShadeModel(GL.GL_SMOOTH);
115         for(Mesh mesh : meshes) {
116             mesh.render(gl, Matrix.ONE);
117             if (main != null && main.whichNeighbor>0 && main.transforms!=null && !mesh.option_wireframe) {
118                 mesh.option_wireframe = true;
119                 mesh.render(gl, main.transforms[main.whichNeighbor-1]);
120                 mesh.option_wireframe = false;
121             }
122         }
123
124         // highlight the point closest to the mouse; we do this here to avoid flicker
125         if (closest != null) {
126             gl.glDisable(GL.GL_LIGHTING);
127             gl.glShadeModel(GL.GL_FLAT);
128
129             if (drawEdge) {
130                 for(Mesh.E e : (Iterable<Mesh.E>)closestEdge.getBoundPeers()) {
131                     gl.glColor3f(0,1,0);
132                     gl.glBegin(gl.GL_LINES);
133                     e.glVertices(gl);
134                     gl.glEnd();
135                     gl.glColor3f(1,0,0);
136                     gl.glBegin(gl.GL_LINES);
137                     e.pair.glVertices(gl);
138                     gl.glEnd();
139                 }
140                 /*
141                 if (closestEdge != null) {
142                     gl.glColor3f(1,1,0);
143                     gl.glBegin(gl.GL_TRIANGLES);
144                     Mesh.T t = closestEdge.t;
145                     t.p1().glVertex(gl);
146                     t.p2().glVertex(gl);
147                     t.p3().glVertex(gl);
148                     gl.glColor3f(0,1,1);
149                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p1()).glVertex(gl);
150                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p2()).glVertex(gl);
151                     main.transforms[main.whichNeighbor-1].times(closestEdge.t.p3()).glVertex(gl);
152                     System.out.println("t="
153                                        +t.p1()+"\n  "
154                                        +t.p2()+"\n  "
155                                        +t.p3()+"\n  "
156                                        );
157                     System.out.println("x="
158                                        +main.transforms[main.whichNeighbor-1].times(t.p1())+"\n  "
159                                        +main.transforms[main.whichNeighbor-1].times(t.p2())+"\n  "
160                                        +main.transforms[main.whichNeighbor-1].times(t.p3())+"\n  "
161                                        );
162                     gl.glEnd();
163                 }
164                 */
165             }
166
167             if (closest.visible) {
168                 gl.glDisable(GL.GL_DEPTH_TEST);
169                 gl.glColor3f(1,1,0);
170                 gl.glBegin(gl.GL_POINTS);
171                 closest.getPoint().glVertex(gl);
172                 gl.glColor3f(0.5f,0.5f,0);
173                 for(Mesh.Vertex v : (Iterable<Mesh.Vertex>)closest.getBoundPeers())
174                     if (v!=closest)
175                         v.getPoint().glVertex(gl);
176                 gl.glEnd();
177
178                 if (closest.getBindingConstraint() instanceof Plane) {
179                     Plane p = (Plane)closest.getBindingConstraint();
180                     Vec v = p.norm();
181                     gl.glEnable(gl.GL_BLEND);
182                     gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA);
183                     gl.glColor4f(1,1,0,0.3f);
184                     gl.glBegin(gl.GL_LINES);
185                     closest.getPoint().glVertex(gl);
186                     v.plus(closest.getPoint()).glVertex(gl);
187                     closest.getPoint().glVertex(gl);
188                     v.times(-1).plus(closest.getPoint()).glVertex(gl);
189                     gl.glEnd();
190                     gl.glDisable(gl.GL_BLEND);
191                 }
192
193                 gl.glEnable(GL.GL_DEPTH_TEST);
194             }
195         }
196
197         projection = Matrix.getProjectionMatrix(gl);
198
199         if (updateClosest) {
200             updateClosest = false;
201             double dist = Double.MAX_VALUE;
202             double distE = Double.MAX_VALUE;
203             closest = null;
204             closestOriginallyAt = null;
205             closestEdge = null;
206             for(Mesh mesh : meshes)
207                 if (mesh.option_selectable)
208                     for(Mesh.Vertex v : mesh.vertices()) {
209                         if (!v.visible) continue;
210                         Point p = projection.times(v.getPoint());
211                         int x = (int)p.x;
212                         int y = (int)p.y;
213                         int mousex = (int)getMouse().x;
214                         int mousey = (int)getMouse().y;
215                         if (closest==null || (x-mousex)*(x-mousex)+(y-mousey)*(y-mousey) < dist) {
216                             dist = (x-mousex)*(x-mousex)+(y-mousey)*(y-mousey);
217                             closest = v;
218                         }
219                         for(Mesh.E e = v.e; e!=null; e=e.pair.next==v.e?null:e.pair.next) {
220                             if (!e.v2.visible) continue;
221                             Segment s =
222                                 new Segment(projection.times(e.v1.getPoint()),
223                                             projection.times(e.v2.getPoint()));
224                             double dist2 = s.distance(getMouse());
225                             if (dist2 < distE) {
226                                 distE = dist2;
227                                 closestEdge = e;
228                             }
229                         }
230                     }
231         }
232         gl.glFlush();
233         glcanvas.setAutoSwapBufferMode(false);
234         glcanvas.swapBuffers();
235     }
236
237     protected synchronized void updateVisibility(GL gl) {
238         IntBuffer buf = ByteBuffer.allocateDirect(9*4*4).order(ByteOrder.nativeOrder()).asIntBuffer();
239         gl.glFlush();
240         gl.glDrawBuffer(GL.GL_BACK);
241         gl.glReadBuffer(GL.GL_BACK);
242         gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);
243         gl.glFlush();
244         gl.glDisable(GL.GL_LIGHTING);
245         gl.glShadeModel(GL.GL_FLAT);
246         gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
247         gl.glColor3f(0,0,0);
248         for(Mesh mesh : meshes) mesh.render(gl, Matrix.ONE, true);
249         for(Mesh mesh : meshes)
250             if (mesh.option_selectable)
251                 for(Mesh.Vertex v : mesh.vertices()) {
252                     Point p = v.getPoint();
253                     Point projected = projection.times(p);
254                     boolean vis = false;
255
256                     gl.glColor3f(1,1,1);
257                     gl.glBegin(gl.GL_POINTS);
258                     p.glVertex(gl);
259                     gl.glEnd();
260                     gl.glFlush();
261
262                     vis = false;
263                     gl.glReadPixels((int)projected.x-1, (int)projected.y-1, 3, 3, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, buf);
264                     for(int j=0; j<9*4; j++) vis |= buf.get(j)!=0;
265
266                     v.visible = vis;
267                     if (vis) {
268                         gl.glColor3f(0,0,0);
269                         gl.glBegin(gl.GL_POINTS);
270                         p.glVertex(gl);
271                         gl.glEnd();
272                     }
273                 }
274         gl.glShadeModel(GL.GL_SMOOTH);
275         gl.glEnable(GL.GL_LIGHTING);
276         gl.glDrawBuffer(GL.GL_FRONT);
277     }
278
279     /** return the position of the mouse as a point in window-space */
280     public Point getMouse() {
281         return new Point(mousex, glcanvas.getHeight()-mousey, 0);
282     }
283
284     /** return the position where the mouse button was pressed, or null if it is not currently pressed */
285     public Point getMouseClick() {
286         return clickPoint;
287     }
288
289     public void mouseWheelMoved(MouseWheelEvent e) {
290         tz -= e.getWheelRotation();
291     }
292
293     public void keyTyped(KeyEvent e)  { }
294     public void keyPressed(KeyEvent e)  { }
295     public void keyReleased(KeyEvent e) { }
296
297     public void mouseClicked(MouseEvent e) { }
298     public void mouseEntered(MouseEvent e) { mouseInside = true; }
299     public void mouseExited(MouseEvent e) { mouseInside = false; }
300     public void mousePressed(MouseEvent e) {
301         clickPoint = getMouse();
302     }
303
304     public void mouseReleased(MouseEvent e) {
305         clickPoint = null;
306     }
307
308     public void mouseMoved(MouseEvent e) {
309         mousex = e.getX();
310         mousey = e.getY();
311
312         if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
313             updateVisibilities = true;
314             drawEdge = true;
315             updateClosest = true;
316         }
317     }
318
319     public void mouseDragged(MouseEvent e) {
320         if ((e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
321             updateVisibilities = true;
322             drawEdge = true;
323             if (closest != null && projection != null) {
324                 synchronized(this) {
325                     if (closestOriginallyAt==null) closestOriginallyAt = closest.getPoint();
326                     Vec d1 = projection.inverse().times(getMouse()).minus(projection.inverse().times(clickPoint));
327                     Vec delta = d1.plus(closestOriginallyAt).minus(closest.getPoint());
328                     closest.move(delta, false);
329                 }
330             }
331         } else if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
332             // move edge
333         } else {
334             updateVisibilities = true;
335             anglex -= mousex - e.getX();
336             angley += mousey - e.getY();
337         }
338         mousex = e.getX();
339         mousey = e.getY();
340     }
341
342     public MeshViewer() {
343         glcanvas = new GLCanvas();
344         glcanvas.addGLEventListener(this);
345         setLayout(new BorderLayout());
346         this.add(glcanvas, BorderLayout.CENTER);
347         glcanvas.addMouseListener(this);
348         glcanvas.addMouseMotionListener(this);
349         glcanvas.addMouseWheelListener(this);
350         glcanvas.addKeyListener(this);
351     }
352
353     public void repaint() {
354         if (glcanvas != null) glcanvas.repaint();
355     }
356
357 }