diff options
Diffstat (limited to 'src/demos/xtrans/XTDesktopPane.java')
-rwxr-xr-x | src/demos/xtrans/XTDesktopPane.java | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/demos/xtrans/XTDesktopPane.java b/src/demos/xtrans/XTDesktopPane.java new file mode 100755 index 0000000..2df6c5e --- /dev/null +++ b/src/demos/xtrans/XTDesktopPane.java @@ -0,0 +1,444 @@ +package demos.xtrans; + +import java.awt.*; +import java.awt.geom.*; +import java.util.*; +import javax.swing.*; +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import com.sun.opengl.impl.*; + +/** A JDesktopPane subclass supporting Accelerated Transitions (XT) of + the components contained within. */ + +public class XTDesktopPane extends OffscreenDesktopPane { + private GLContext j2dContext; + private Object j2dContextSurfaceIdentifier; + + private Rectangle oglViewport; + + private XTTransitionManager transitionManager = new XTBasicTransitionManager(); + + private boolean reallyRemove; + + private boolean alwaysRedraw; + + static class TransitionInfo { + boolean isIn; + Component target; + long startTime; + XTTransition trans; + + TransitionInfo(boolean isIn, + Component target, + long startTime, + XTTransition trans) { + this.isIn = isIn; + this.target = target; + this.startTime = startTime; + this.trans = trans; + } + } + + private java.util.List/*<TransitionInfo>*/ transitions = new ArrayList(); + + private float TRANSITION_DURATION = 300.0f; + + private int textureTarget = GL.GL_TEXTURE_2D; + private GLU glu = new GLU(); + + /** Creates a new accelerated transition desktop pane. */ + public XTDesktopPane() { + super(); + if (!Java2D.isOGLPipelineActive()) { + throw new RuntimeException("XTDesktopPane requires new Java2D/JOGL support in Java SE 6 and -Dsun.java2d.opengl=true"); + } + setDesktopManager(new XTDesktopManager()); + } + + /** Overridden to use a transition to display the given + component. */ + protected void addImpl(Component c, Object constraints, int index) { + super.addImpl(c, constraints, index); + getOffscreenDesktopManager().layoutOffscreenBuffer(this); + + // When animating the component's transition, center the + // perspective projection around the center of the newly-added + // component so that the perspective effects appear symmetric. + // This amounts to moving the viewport so the component is in the + // center. + addTransition(true, c, + transitionManager.createTransitionForComponent(c, + true, + getOGLViewport(), + computeViewportOffsetToCenterComponent(c, getOGLViewport()), + getXTDesktopManager().getOpenGLTextureCoords(c))); + } + + /** Overridden to use an animated transition to remove the passed + component. */ + public void remove(int index) { + if (reallyRemove) { + super.remove(index); + } else { + addRemoveTransition(getRealComponent(getComponent(index))); + } + } + + /** Overridden to use an animated transition to remove the passed + component. */ + public void remove(Component c) { + if (reallyRemove) { + super.remove(c); + } else { + addRemoveTransition(getRealComponent(c)); + } + } + + /** Causes the given component to really be removed from this + desktop pane. Called when the removal transition is complete. */ + protected void removeImpl(Component c) { + reallyRemove = true; + try { + remove(c); + } finally { + reallyRemove = false; + } + } + + /** Overridden to draw the child components, including any animated + transitions, using OpenGL. */ + protected void paintChildren(final Graphics g) { + // FIXME: this is a hack to get repainting behavior to work + // properly when we specify that optimized drawing is disabled (so + // that childrens' repaint requests will trickle up to us via the + // Animator) but need to descend to repaint our children -- + // currently don't know how to distinguish between repaint events + // propagated up to us and those initiated by the children (which + // typically go through the OffscreenComponentWrapper's + // getGraphics() method and implicitly cause a redraw of all child + // components as well as the desktop) + if (alwaysRedraw) { + getOffscreenDesktopManager().setNeedsRedraw(); + } + + // Update desktop manager's offscreen buffer if necessary + getOffscreenDesktopManager().updateOffscreenBuffer(this); + + // Draw textured quads using JOGL over current contents of back + // buffer + final Component[] components = getRealChildComponents(); + final ArrayList expiredTransitions = new ArrayList(); + Java2D.invokeWithOGLContextCurrent(g, new Runnable() { + public void run() { + // Get valid Java2D context + if (j2dContext == null || + j2dContextSurfaceIdentifier != Java2D.getOGLSurfaceIdentifier(g)) { + j2dContext = GLDrawableFactory.getFactory().createExternalGLContext(); + j2dContext.setGL(new DebugGL(j2dContext.getGL())); + j2dContextSurfaceIdentifier = Java2D.getOGLSurfaceIdentifier(g); + } + + j2dContext.makeCurrent(); // No-op + try { + GL gl = j2dContext.getGL(); + + // Figure out where JDesktopPane is on the Swing back buffer + Rectangle oglRect = Java2D.getOGLViewport(g, getWidth(), getHeight()); + // Cache this value for adding transitions later + oglViewport = new Rectangle(oglRect); + + // Set up perspective projection so we can do some subtle + // 3D effects. We set up the view volume so that at z=0 + // the lower-left coordinates of the desktop are (0, 0) + // and the upper right coordinates are + // (oglRect.getWidth(), oglRect.getHeight()). The key here + // is to decide on the field of view and then figure out + // how far back we have to put the eye point in order for + // this to occur. + double fovy = 30.0; // degrees + double w = oglRect.getWidth(); + double h = oglRect.getHeight(); + // d is the distance from the eye point to the image plane + // (z=0) + double d = (h / 2) / Math.tan(Math.toRadians(fovy) / 2); + double near = d - (h / 2); + double far = d + (h / 2); + gl.glViewport(oglRect.x, oglRect.y, oglRect.width, oglRect.height); + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPushMatrix(); + gl.glLoadIdentity(); + glu.gluPerspective(fovy, (w / h), near, far); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + double eyeX = w / 2; + double eyeY = h / 2; + // Object x and y are the same as eye x and y since we're + // looking in the -z direction + glu.gluLookAt(eyeX, eyeY, d, + eyeX, eyeY, 0, + 0, 1, 0); + + // Set up a scissor box so we don't blow away other + // components if we shift around the viewport to get the + // animated transitions' perspective effects to be + // centered + gl.glEnable(GL.GL_SCISSOR_TEST); + Rectangle r = Java2D.getOGLScissorBox(g); + if (r != null) { + gl.glScissor(r.x, r.y, r.width, r.height); + } + + /* + + // Orthographic projection for debugging + gl.glViewport(oglRect.x, oglRect.y, oglRect.width, oglRect.height); + // Set up coordinate system for easy access + gl.glMatrixMode(GL.GL_PROJECTION); + // System.err.println("oglRect x = " + oglRect.getX()); + // System.err.println("oglRect y = " + oglRect.getY()); + // System.err.println("oglRect w = " + oglRect.getWidth()); + // System.err.println("oglRect h = " + oglRect.getHeight()); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glOrtho(oglRect.getX(), oglRect.getX() + oglRect.getWidth(), + oglRect.getY(), oglRect.getY() + oglRect.getHeight(), + -1, + 1); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + + */ + + // Enable and bind texture corresponding to internal frames' back buffer + gl.glBindTexture(textureTarget, getXTDesktopManager().getOpenGLTextureObject()); + + gl.glEnable(textureTarget); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + + gl.glEnable(GL.GL_BLEND); + gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); + + // Iterate down children in z order bottom-to-top + int compCount = components.length; + long curTime = currentTimeMillis(); + for (int i = compCount - 1; i >= 0; i--) { + Component c = components[i]; + + // Find transition for this component + TransitionInfo info = transitionForComponent(c); + + if (info != null) { + gl.glPushMatrix(); + // When animating the component's transition, center the + // perspective projection around the center of the newly-added + // component so that the perspective effects appear symmetric. + // This amounts to moving the viewport so the component is in the + // center. + Point viewportOffset = computeViewportOffsetToCenterComponent(c, getOGLViewport()); + gl.glViewport(oglRect.x + viewportOffset.x, + oglRect.y + viewportOffset.y, + oglRect.width, + oglRect.height); + + // Update it + float percent = clamp((curTime - info.startTime) / TRANSITION_DURATION, 0.0f, 1.0f); + XTTransition trans = info.trans; + trans.update(percent); + trans.draw(gl); + // See whether the transition has expired + if (percent == 1.0f) { + transitions.remove(info); + expiredTransitions.add(info); + } + gl.glPopMatrix(); + // Put the viewport back where it was + gl.glViewport(oglRect.x, oglRect.y, oglRect.width, oglRect.height); + } else { + // For each one, get the OpenGL texture coordinates on the offscreen OpenGL texture + Rectangle2D oglTexCoords = getXTDesktopManager().getOpenGLTextureCoords(c); + Rectangle bounds = c.getBounds(); + + int cx = bounds.x; + int cy = bounds.y; + int cw = bounds.width; + int ch = bounds.height; + float tx = (float) oglTexCoords.getX(); + float ty = (float) oglTexCoords.getY(); + float tw = (float) oglTexCoords.getWidth(); + float th = (float) oglTexCoords.getHeight(); + float vx = oglRect.x; + float vy = oglRect.y; + float vw = oglRect.width; + float vh = oglRect.height; + + // Draw a quad per component + gl.glBegin(GL.GL_TRIANGLES); + gl.glColor4f(1, 1, 1, 1); + + // Triangle 1 + gl.glTexCoord2f(tx, ty + th); + gl.glVertex3f (cx, vh - cy, 0); + gl.glTexCoord2f(tx, ty); + gl.glVertex3f (cx, vh - cy - ch, 0); + gl.glTexCoord2f(tx + tw, ty + th); + gl.glVertex3f (cx + cw, vh - cy, 0); + // Triangle 2 + gl.glTexCoord2f(tx + tw, ty + th); + gl.glVertex3f (cx + cw, vh - cy, 0); + gl.glTexCoord2f(tx, ty); + gl.glVertex3f (cx, vh - cy - ch, 0); + gl.glTexCoord2f(tx + tw, ty); + gl.glVertex3f (cx + cw, vh - cy - ch, 0); + + gl.glEnd(); + } + } + gl.glFlush(); + gl.glDisable(textureTarget); + gl.glDisable(GL.GL_BLEND); + + gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPopMatrix(); + gl.glFinish(); + } finally { + j2dContext.release(); + } + } + }); + + for (Iterator iter = expiredTransitions.iterator(); iter.hasNext(); ) { + TransitionInfo info = (TransitionInfo) iter.next(); + if (!info.isIn) { + removeImpl(info.target); + repaint(); + } + } + + if (!transitions.isEmpty()) { + repaint(); + } + } + + /** Overridden from parent to disable optimized drawing so that we + get correct rendering results with embedded GLJPanels */ + public boolean isOptimizedDrawingEnabled() { + return false; + } + + /** Returns the XTDesktopManager for this desktop pane. */ + public XTDesktopManager getXTDesktopManager() { + return (XTDesktopManager) getDesktopManager(); + } + + /** Returns the transition manager for this desktop pane. By default + this is an XTBasicTransitionManager. */ + public XTTransitionManager getTransitionManager() { + return transitionManager; + } + + /** Sets the transition manager for this desktop pane. By default + this is an XTBasicTransitionManager. */ + public void setTransitionManager(XTTransitionManager manager) { + transitionManager = manager; + } + + /** Workaround to get painting behavior to work properly in some + situations. */ + public void setAlwaysRedraw(boolean onOrOff) { + alwaysRedraw = onOrOff; + } + + /** Workaround to get painting behavior to work properly in some + situations. */ + public boolean getAlwaysRedraw() { + return alwaysRedraw; + } + + /** Returns the transition corresponding to the passed Component, or + null if no transition is currently active for this component. */ + private TransitionInfo transitionForComponent(Component c) { + for (Iterator iter = transitions.iterator(); iter.hasNext(); ) { + TransitionInfo info = (TransitionInfo) iter.next(); + if (info.target == c) { + return info; + } + } + return null; + } + + /** Adds a transition for the specified component. An "out" + transition will automatically cause the component to be removed + after it has completed running. */ + protected void addTransition(boolean isIn, + Component target, + XTTransition trans) { + TransitionInfo info = new TransitionInfo(isIn, + target, + currentTimeMillis(), + trans); + transitions.add(info); + } + + /** Adds a removal transition for the given component. */ + protected void addRemoveTransition(Component target) { + addTransition(false, + target, + transitionManager.createTransitionForComponent(target, + false, + getOGLViewport(), + computeViewportOffsetToCenterComponent(target, getOGLViewport()), + getXTDesktopManager().getOpenGLTextureCoords(target))); + } + + /** Computes the offset applied to the OpenGL viewport to center the + given component in the viewport. This is used to make the + perspective effects appear symmetric about the component. */ + protected Point computeViewportOffsetToCenterComponent(Component c, + Rectangle oglViewport) { + Rectangle bounds = c.getBounds(); + return new Point(bounds.x + ((bounds.width - oglViewport.width) / 2), + -bounds.y + ((oglViewport.height - bounds.height) / 2)); + } + + /** Clamps the given value between the specified minimum and + maximum. */ + protected static float clamp(float val, float min, float max) { + return Math.min(max, Math.max(min, val)); + } + + /** Returns the current time in milliseconds. */ + protected static long currentTimeMillis() { + // Avoid 1.5 compilation dependencies since no perceived + // improvement by changing this + // return System.nanoTime() / 1000000; + return System.currentTimeMillis(); + } + + /** Returns the OpenGL viewport corresponding to this desktop pane. */ + protected Rectangle getOGLViewport() { + if (oglViewport != null) { + return oglViewport; + } + + Rectangle b = getBounds(); + return new Rectangle(0, 0, b.width, b.height); + } +} |