001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/**
031 *
032 */
033package org.openimaj.vis;
034
035import java.awt.Dimension;
036import java.util.ArrayList;
037import java.util.List;
038
039import javax.media.opengl.GL;
040import javax.media.opengl.GL2;
041import javax.media.opengl.GL2ES1;
042import javax.media.opengl.GL2GL3;
043import javax.media.opengl.GLAutoDrawable;
044import javax.media.opengl.GLEventListener;
045import javax.media.opengl.fixedfunc.GLLightingFunc;
046import javax.media.opengl.fixedfunc.GLMatrixFunc;
047import javax.media.opengl.glu.gl2.GLUgl2;
048
049import org.openimaj.image.MBFImage;
050import org.openimaj.vis.general.CameraPositionProvider;
051import org.openimaj.vis.general.JOGLWindow;
052
053import com.jogamp.opengl.util.Animator;
054import com.jogamp.opengl.util.gl2.GLUT;
055
056/**
057 *
058 *
059 *      @author David Dupplaw (dpd@ecs.soton.ac.uk)
060 *  @created 11 Jul 2013
061 *      @version $Author$, $Revision$, $Date$
062 *      @param <D> Data type
063 */
064public abstract class Visualisation3D<D> implements
065        GLEventListener, Visualisation<D>,
066        AnimatedVisualisationProvider
067{
068        /** The GLU library we'll use */
069        protected final GLUgl2 glu = new GLUgl2();
070
071        /** The GLUT library we'll use */
072        protected final GLUT glut = new GLUT();
073
074        /** The JOGL Window (NEWT) */
075        protected JOGLWindow window;
076
077        /** Animation listeners */
078        private final List<AnimatedVisualisationListener> listeners =
079                        new ArrayList<AnimatedVisualisationListener>();
080
081        /** The animation */
082        private final Animator animator;
083
084        /** Object that provide the camera position over time */
085        protected CameraPositionProvider cameraPosition;
086
087        /** The data! */
088        protected D data;
089
090        /** Whether lighting should be enabled */
091        private boolean enableLights = false;
092
093
094        /**
095         *      Render the visualisation into the drawable
096         *      @param drawable The drawable
097         */
098        protected abstract void renderVis( GLAutoDrawable drawable );
099
100
101        /**
102         *      @param width
103         *      @param height
104         */
105        public Visualisation3D( final int width, final int height )
106        {
107                this.window = new JOGLWindow( width, height );
108
109                if( this.window.getDrawableSurface() == null )
110                        throw new RuntimeException( "Unable to get OpenGL surface." );
111
112                this.window.getDrawableSurface().addGLEventListener( this );
113
114                this.animator = new Animator( this.window.getDrawableSurface() );
115                this.animator.add( this.window.getDrawableSurface() );
116                this.animator.start();
117        }
118
119        /**
120         *      Closes the window and cleans up
121         *
122         *      {@inheritDoc}
123         *      @see java.lang.Object#finalize()
124         */
125        @Override
126        protected void finalize() throws Throwable
127        {
128                this.animator.stop();
129                this.window.close();
130        };
131
132        @Override
133        public void addAnimatedVisualisationListener( final AnimatedVisualisationListener avl )
134        {
135                this.listeners.add( avl );
136        }
137
138        @Override
139        public void removeAnimatedVisualisationListener( final AnimatedVisualisationListener avl )
140        {
141                this.listeners.remove( avl );
142        }
143
144        @Override
145        public void init( final GLAutoDrawable drawable )
146        {
147                final GL2 gl = drawable.getGL().getGL2();
148                gl.setSwapInterval( 1 );
149                gl.glEnable( GL.GL_DEPTH_TEST );
150        gl.glDepthFunc( GL.GL_LEQUAL );
151        gl.glShadeModel( GLLightingFunc.GL_SMOOTH );
152        gl.glHint( GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST );
153//              gl.glEnable( GL.GL_BLEND );
154//              gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA );
155                gl.glEnable( GL2GL3.GL_POLYGON_SMOOTH );
156
157                final float w = this.window.getDrawableSurface().getWidth();
158                final float h = this.window.getDrawableSurface().getHeight();
159
160                // Set the projection matrix (only done once - just here)
161                gl.glMatrixMode( GLMatrixFunc.GL_PROJECTION );
162                gl.glLoadIdentity();
163                this.glu.gluPerspective( 50, (w / h), 0.01, 10 );
164
165                // Set the initial model matrix
166                gl.glMatrixMode( GLMatrixFunc.GL_MODELVIEW );
167                gl.glLoadIdentity();
168                gl.glViewport( 0, 0, (int) w, (int) h ); /* viewport size in pixels */
169        }
170
171        /**
172         * {@inheritDoc}
173         *
174         * @see javax.media.opengl.GLEventListener#display(javax.media.opengl.GLAutoDrawable)
175         */
176        @Override
177        public void display( final GLAutoDrawable drawable )
178        {
179                this.updateVis();
180
181                final GL2 gl = drawable.getGL().getGL2();
182                gl.glMatrixMode( GLMatrixFunc.GL_MODELVIEW );
183                gl.glLoadIdentity();
184
185                if( this.cameraPosition != null )
186                {
187                        final float[] pos = this.cameraPosition.getCameraPosition();
188                        this.glu.gluLookAt( pos[0], pos[1], pos[2], pos[3], pos[4], pos[5], pos[6], pos[7], pos[8] );
189                }
190
191        // Prepare light parameters.
192                if( this.enableLights )
193                {
194                final float SHINE_ALL_DIRECTIONS = 1;
195                final float[] lightPos = {-30, 0, 0, SHINE_ALL_DIRECTIONS};
196                final float[] lightColorAmbient = {0.2f, 0.2f, 0.2f, 1f};
197                final float[] lightColorSpecular = {0.5f, 0.5f, 0.5f, 1f};
198
199                // Set light parameters.
200                gl.glLightfv( GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_POSITION, lightPos, 0 );
201                gl.glLightfv( GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT, lightColorAmbient, 0 );
202                gl.glLightfv( GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR, lightColorSpecular, 0 );
203
204                // Enable lighting in GL.
205                gl.glEnable( GLLightingFunc.GL_LIGHT1 );
206                gl.glEnable( GLLightingFunc.GL_LIGHTING );
207                }
208
209                gl.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
210
211                this.renderVis( drawable );
212        }
213
214        /**
215         *      {@inheritDoc}
216         *      @see javax.media.opengl.GLEventListener#dispose(javax.media.opengl.GLAutoDrawable)
217         */
218        @Override
219        public void dispose( final GLAutoDrawable drawable )
220        {
221                drawable.removeGLEventListener( this );
222        }
223
224        /**
225         * {@inheritDoc}
226         *
227         * @see javax.media.opengl.GLEventListener#reshape(javax.media.opengl.GLAutoDrawable,
228         *      int, int, int, int)
229         */
230        @Override
231        public void reshape( final GLAutoDrawable drawable, final int arg1, final int arg2, final int arg3, final int arg4 )
232        {
233                final GL2 gl = drawable.getGL().getGL2();
234                final float w = this.window.getDrawableSurface().getWidth();
235                final float h = this.window.getDrawableSurface().getHeight();
236                gl.glMatrixMode( GLMatrixFunc.GL_PROJECTION );
237                gl.glLoadIdentity();
238                this.glu.gluPerspective( 50, (w / h), 0.01, 10 );
239        }
240
241        /**
242         *      {@inheritDoc}
243         *      @see org.openimaj.vis.VisualisationImageProvider#getVisualisationImage()
244         */
245        @Override
246        public MBFImage getVisualisationImage()
247        {
248                // TODO: Need to convert the GL buffer to a MBFImage
249                return null;
250        }
251
252        @Override
253        public void setRequiredSize( final Dimension d )
254        {
255        }
256
257        /**
258         *      {@inheritDoc}
259         *      @see org.openimaj.vis.Visualisation#setData(java.lang.Object)
260         */
261        @Override
262        public void setData( final D data )
263        {
264                this.data = data;
265        }
266
267
268        /**
269         *      @return the enableLights
270         */
271        public boolean isEnableLights()
272        {
273                return this.enableLights;
274        }
275
276
277        /**
278         *      @param enableLights the enableLights to set
279         */
280        public void setEnableLights( final boolean enableLights )
281        {
282                this.enableLights = enableLights;
283        }
284}