/** * Copyright 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.test.junit.graph.demos; import java.io.IOException; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAnimatorControl; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLException; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.curve.opengl.TextRegionUtil; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; import com.jogamp.newt.Window; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.PMVMatrix; /** * * GPURendererListenerBase01 Keys: * - 1/2: zoom in/out * - 6/7: 2nd pass texture size * - 0/9: rotate * - v: toggle v-sync * - s: screenshot * * Additional Keys: * - 3/4: font +/- * - h: toogle draw 'font set' * - f: toggle draw fps * - space: toggle font (ubuntu/java) * - i: live input text input (CR ends it, backspace supported) */ public abstract class GPUTextRendererListenerBase01 extends GPURendererListenerBase01 { public final TextRegionUtil textRegionUtil; private final GLRegion regionFPS, regionBottom; int fontSet = FontFactory.UBUNTU; Font font; int headType = 0; boolean drawFPS = true; final float fontSizeFName = 8f; final float fontSizeFPS = 10f; final int[] sampleCountFPS = new int[] { 8 }; float fontSizeHead = 12f; float fontSizeBottom = 16f; float dpiH = 96; final int fontSizeModulo = 100; String fontName; AABBox fontNameBox; String headtext; AABBox headbox; static final String text1 = "abcdefghijklmnopqrstuvwxyz\nABCDEFGHIJKLMNOPQRSTUVWXYZ\n0123456789.:,;(*!?/\\\")$%^&-+@~#<>{}[]"; static final String text2 = "The quick brown fox jumps over the lazy dog"; static final String textX = "JOGAMP graph demo using Resolution Independent NURBS\n"+ "JOGAMP JOGL - OpenGL ES2 profile\n"+ "Press 1/2 to zoom in/out the below text\n"+ "Press 3/4 to incr/decs font size (alt: head, w/o bottom)\n"+ "Press 6/7 to edit texture size if using VBAA\n"+ "Press 0/9 to rotate the below string\n"+ "Press v to toggle vsync\n"+ "Press i for live input text input (CR ends it, backspace supported)\n"+ "Press f to toggle fps. H for different text, space for font type\n"; static final String textX2 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec sapien tellus. \n"+ "Ut purus odio, rhoncus sit amet commodo eget, ullamcorper vel urna. Mauris ultricies \n"+ "quam iaculis urna cursus ornare. Nullam ut felis a ante ultrices ultricies nec a elit. \n"+ "In hac habitasse platea dictumst. Vivamus et mi a quam lacinia pharetra at venenatis est.\n"+ "Morbi quis bibendum nibh. Donec lectus orci, sagittis in consequat nec, volutpat nec nisi.\n"+ "Donec ut dolor et nulla tristique varius. In nulla magna, fermentum id tempus quis, semper \n"+ "in lorem. Maecenas in ipsum ac justo scelerisque sollicitudin. Quisque sit amet neque lorem,\n" + "-------Press H to change text---------"; StringBuilder userString = new StringBuilder(); boolean userInput = false; public GPUTextRendererListenerBase01(final RenderState rs, final int renderModes, final int sampleCount, final boolean blending, final boolean debug, final boolean trace) { // NOTE_ALPHA_BLENDING: We use alpha-blending super(RegionRenderer.create(rs, blending ? RegionRenderer.defaultBlendEnable : null, blending ? RegionRenderer.defaultBlendDisable : null), renderModes, debug, trace); rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED); this.textRegionUtil = new TextRegionUtil(renderModes); this.regionFPS = GLRegion.create(renderModes, null); this.regionBottom = GLRegion.create(renderModes, null); try { this.font = FontFactory.get(fontSet).getDefault(); dumpFontNames(); this.fontName = font.toString(); } catch (final IOException ioe) { System.err.println("Caught: "+ioe.getMessage()); ioe.printStackTrace(); } setMatrix(0, 0, 0, 0f, sampleCount); } void dumpFontNames() { System.err.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); System.err.println(font.getAllNames(null, "\n")); System.err.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } void switchHeadBox() { headType = ( headType + 1 ) % 4 ; switch(headType) { case 0: headtext = null; break; case 1: headtext= textX2; break; case 2: headtext= textX; break; default: headtext = text1; } if(null != headtext) { headbox = font.getMetricBounds(headtext, font.getPixelSize(fontSizeHead, dpiH)); } } @Override public void init(final GLAutoDrawable drawable) { super.init(drawable); final Object upObj = drawable.getUpstreamWidget(); if( upObj instanceof Window ) { final Window window = (Window) upObj; final float[] sDPI = window.getPixelsPerMM(new float[2]); sDPI[0] *= 25.4f; sDPI[1] *= 25.4f; dpiH = sDPI[1]; System.err.println("Using screen DPI of "+dpiH); } else { System.err.println("Using default DPI of "+dpiH); } fontNameBox = font.getMetricBounds(fontName, font.getPixelSize(fontSizeFName, dpiH)); switchHeadBox(); } @Override public void reshape(final GLAutoDrawable drawable, final int xstart, final int ystart, final int width, final int height) { super.reshape(drawable, xstart, ystart, width, height); final float dist = 100f; nearPlaneX0 = nearPlane1Box.getMinX() * dist; nearPlaneY0 = nearPlane1Box.getMinY() * dist; nearPlaneZ0 = nearPlane1Box.getMinZ() * dist; final float xd = nearPlane1Box.getWidth() * dist; final float yd = nearPlane1Box.getHeight() * dist; nearPlaneSx = xd / width; nearPlaneSy = yd / height; nearPlaneS = nearPlaneSy; System.err.printf("Scale: [%f x %f] / [%d x %d] = [%f, %f] -> %f%n", xd, yd, width, height, nearPlaneSx, nearPlaneSy, nearPlaneS); } float nearPlaneX0, nearPlaneY0, nearPlaneZ0, nearPlaneSx, nearPlaneSy, nearPlaneS; @Override public void dispose(final GLAutoDrawable drawable) { regionFPS.destroy(drawable.getGL().getGL2ES2()); regionBottom.destroy(drawable.getGL().getGL2ES2()); super.dispose(drawable); } @Override public void display(final GLAutoDrawable drawable) { final int width = drawable.getSurfaceWidth(); final int height = drawable.getSurfaceHeight(); final GL2ES2 gl = drawable.getGL().getGL2ES2(); gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // final float zDistance0 = 500f; // final float zDistance1 = 400f; // final float[] objPos = new float[3]; // final float[] winZ = new float[1]; // final int[] view = new int[] { 0, 0, drawable.getWidth(), drawable.getHeight() }; final RegionRenderer renderer = getRenderer(); final RenderState rs = renderer.getRenderState(); final PMVMatrix pmv = renderer.getMatrix(); pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f); final float pixelSizeFName = font.getPixelSize(fontSizeFName, dpiH); final float pixelSizeHead = font.getPixelSize(fontSizeHead, dpiH); final float pixelSizeBottom = font.getPixelSize(fontSizeBottom, dpiH); if( drawFPS ) { final float pixelSizeFPS = font.getPixelSize(fontSizeFPS, dpiH); final float lfps, tfps, td; final GLAnimatorControl animator = drawable.getAnimator(); if( null != animator ) { lfps = animator.getLastFPS(); tfps = animator.getTotalFPS(); td = animator.getTotalFPSDuration()/1000f; } else { lfps = 0f; tfps = 0f; td = 0f; } final String modeS = Region.getRenderModeString(regionFPS.getRenderModes()); final String text = String.format("%03.1f/%03.1f fps, v-sync %d, fontSize [head %.1f, bottom %.1f], %s-samples [%d, this %d], td %4.1f, blend %b, alpha-bits %d", lfps, tfps, gl.getSwapInterval(), fontSizeHead, fontSizeBottom, modeS, getSampleCount()[0], sampleCountFPS[0], td, renderer.getRenderState().isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED), drawable.getChosenGLCapabilities().getAlphaBits()); // bottom, half line up pmv.glTranslatef(nearPlaneX0, nearPlaneY0+(nearPlaneS * pixelSizeFPS / 2f), nearPlaneZ0); // No cache, keep region alive! TextRegionUtil.drawString3D(gl, regionFPS, renderer, font, nearPlaneS * pixelSizeFPS, text, null, sampleCountFPS, textRegionUtil.tempT1, textRegionUtil.tempT2); } float dx = width-fontNameBox.getWidth()-2f; float dy = height - 10f; pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0); // System.err.printf("FontN: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy)); textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeFName, fontName, null, getSampleCount()); dx = 10f; dy += -fontNameBox.getHeight() - 10f; if(null != headtext) { pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); // System.err.printf("Head: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy)); pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0); // pmv.glTranslatef(x0, y1, z0); textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeHead, headtext, null, getSampleCount()); } dy += -headbox.getHeight() - font.getLineHeight(pixelSizeBottom); pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); pmv.glTranslatef(nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy), nearPlaneZ0); // System.err.printf("Bottom: [%f %f] -> [%f %f]%n", dx, dy, nearPlaneX0+(dx*nearPlaneSx), nearPlaneY0+(dy*nearPlaneSy)); pmv.glTranslatef(getXTran(), getYTran(), getZTran()); pmv.glRotatef(getAngle(), 0, 1, 0); rs.setColorStatic(0.9f, 0.0f, 0.0f, 1.0f); if( bottomTextUseFrustum ) { regionBottom.setFrustum(pmv.glGetFrustum()); } if(!userInput) { if( bottomTextUseFrustum ) { TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, text2, null, getSampleCount(), textRegionUtil.tempT1, textRegionUtil.tempT2); } else { textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeBottom, text2, null, getSampleCount()); } } else { if( bottomTextUseFrustum ) { TextRegionUtil.drawString3D(gl, regionBottom, renderer, font, nearPlaneS * pixelSizeBottom, userString.toString(), null, getSampleCount(), textRegionUtil.tempT1, textRegionUtil.tempT2); } else { textRegionUtil.drawString3D(gl, renderer, font, nearPlaneS * pixelSizeBottom, userString.toString(), null, getSampleCount()); } } } final boolean bottomTextUseFrustum = true; public void fontBottomIncr(final int v) { fontSizeBottom = Math.abs((fontSizeBottom + v) % fontSizeModulo) ; dumpMatrix(true); } public void fontHeadIncr(final int v) { fontSizeHead = Math.abs((fontSizeHead + v) % fontSizeModulo) ; if(null != headtext) { headbox = font.getMetricBounds(headtext, font.getPixelSize(fontSizeHead, dpiH)); } } public boolean nextFontSet() { try { final int set = ( fontSet == FontFactory.UBUNTU ) ? FontFactory.JAVA : FontFactory.UBUNTU ; final Font _font = FontFactory.get(set).getDefault(); if(null != _font) { fontSet = set; font = _font; fontName = font.getFullFamilyName(null).toString(); fontNameBox = font.getMetricBounds(fontName, font.getPixelSize(fontSizeFName, dpiH)); dumpFontNames(); return true; } } catch (final IOException ex) { System.err.println("Caught: "+ex.getMessage()); } return false; } public boolean setFontSet(final int set, final int family, final int stylebits) { try { final Font _font = FontFactory.get(set).get(family, stylebits); if(null != _font) { fontSet = set; font = _font; fontName = font.getFullFamilyName(null).toString(); fontNameBox = font.getMetricBounds(fontName, font.getPixelSize(fontSizeFName, dpiH)); dumpFontNames(); return true; } } catch (final IOException ex) { System.err.println("Caught: "+ex.getMessage()); } return false; } public boolean isUserInputMode() { return userInput; } void dumpMatrix(final boolean bbox) { System.err.println("Matrix: " + getXTran() + "/" + getYTran() + " x"+getZTran() + " @"+getAngle() +" fontSize "+fontSizeBottom); if(bbox) { System.err.println("bbox: "+font.getMetricBounds(text2, nearPlaneS * font.getPixelSize(fontSizeBottom, dpiH))); } } KeyAction keyAction = null; @Override public void attachInputListenerTo(final GLWindow window) { if ( null == keyAction ) { keyAction = new KeyAction(); window.addKeyListener(keyAction); super.attachInputListenerTo(window); } } @Override public void detachInputListenerFrom(final GLWindow window) { super.detachInputListenerFrom(window); if ( null == keyAction ) { return; } window.removeKeyListener(keyAction); } public void printScreen(final GLAutoDrawable drawable, final String dir, final String tech, final boolean exportAlpha) throws GLException, IOException { final String fn = font.getFullFamilyName(null).toString(); printScreen(drawable, dir, tech, fn.replace(' ', '_'), exportAlpha); } float fontHeadScale = 1f; public class KeyAction implements KeyListener { @Override public void keyPressed(final KeyEvent e) { if(userInput) { return; } final short s = e.getKeySymbol(); if(s == KeyEvent.VK_3) { if( e.isAltDown() ) { fontHeadIncr(1); } else { fontBottomIncr(1); } } else if(s == KeyEvent.VK_4) { if( e.isAltDown() ) { fontHeadIncr(-1); } else { fontBottomIncr(-1); } } else if(s == KeyEvent.VK_H) { switchHeadBox(); } else if(s == KeyEvent.VK_F) { drawFPS = !drawFPS; } else if(s == KeyEvent.VK_SPACE) { nextFontSet(); } else if(s == KeyEvent.VK_I) { userInput = true; setIgnoreInput(true); } } @Override public void keyReleased(final KeyEvent e) { if( !e.isPrintableKey() || e.isAutoRepeat() ) { return; } if(userInput) { final short k = e.getKeySymbol(); if( KeyEvent.VK_ENTER == k ) { userInput = false; setIgnoreInput(false); } else if( KeyEvent.VK_BACK_SPACE == k && userString.length()>0) { userString.deleteCharAt(userString.length()-1); } else { final char c = e.getKeyChar(); if( font.isPrintableChar( c ) ) { userString.append(c); } } } } } }