summaryrefslogtreecommitdiffstats
path: root/src/demos/xtrans/XTDesktopPane.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/demos/xtrans/XTDesktopPane.java')
-rwxr-xr-xsrc/demos/xtrans/XTDesktopPane.java446
1 files changed, 446 insertions, 0 deletions
diff --git a/src/demos/xtrans/XTDesktopPane.java b/src/demos/xtrans/XTDesktopPane.java
new file mode 100755
index 0000000..8582e71
--- /dev/null
+++ b/src/demos/xtrans/XTDesktopPane.java
@@ -0,0 +1,446 @@
+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.*;
+
+import com.sun.opengl.impl.windows.*;
+
+/** 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);
+ }
+}