/** * Draw a cube with the orientation labeled on the sides. */ /* Copyright 2016-2017 Will Winder This file is part of Universal Gcode Sender (UGS). UGS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. UGS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with UGS. If not, see <http://www.gnu.org/licenses/>. */ package com.willwinder.ugs.nbm.visualizer.renderables; import static com.jogamp.opengl.GL.GL_CULL_FACE; import static com.jogamp.opengl.GL.GL_DEPTH_TEST; import com.jogamp.opengl.GL2; import static com.jogamp.opengl.GL2ES3.GL_QUADS; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.util.awt.TextRenderer; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; import com.willwinder.ugs.nbm.visualizer.shared.Renderable; import java.awt.Font; import java.awt.geom.Rectangle2D; import javax.vecmath.Point3d; public class OrientationCube extends Renderable { private final float size; private final float[] color = {0.8f, 0.8f, 0.8f}; private final float[] border = {0.2f, 0.2f, 0.2f}; private TextRenderer renderer; private float textScaleFactor; public OrientationCube(float s) { super(Integer.MIN_VALUE); size = s; } @Override public void reloadPreferences(VisualizerOptions vo) { } @Override public boolean rotate() { return true; } @Override public boolean center() { return false; } @Override public void init(GLAutoDrawable drawable) { renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 72)); renderer.setColor(0.2f, 0.2f, 0.2f, 1f); // Compute the scale factor of the largest string which will make // them all fit on the faces of the cube Rectangle2D bounds = renderer.getBounds("Z+"); float w = (float) bounds.getWidth(); textScaleFactor = size / (w * 1.7f); } @Override public void draw(GLAutoDrawable drawable, boolean idle, Point3d workCoord, Point3d focusMin, Point3d focusMax, double scaleFactor, Point3d mouseCoordinates, Point3d rotation) { GL2 gl = drawable.getGL().getGL2(); int ySize = drawable.getDelegatedDrawable().getSurfaceHeight(); int xSize = drawable.getDelegatedDrawable().getSurfaceWidth(); // Set viewport to the corner. float fromEdge = 0.8f; int squareSize = ySize-(int)(ySize*fromEdge); gl.glViewport(0, (int)(ySize*fromEdge), squareSize, squareSize); gl.glPushMatrix(); gl.glMatrixMode(gl.GL_PROJECTION); gl.glLoadIdentity(); gl.glOrtho(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5); //, maxSide, maxSide, maxSide, maxSide, maxSide); gl.glMatrixMode(gl.GL_MODELVIEW); // Setup lighting float[] lmodel_ambient = { 0.5f, 0.5f, 0.5f, 1.0f }; gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, lmodel_ambient, 0); gl.glEnable(GL2.GL_LIGHTING); drawCube(gl); gl.glDisable(gl.GL_LIGHTING); gl.glPopMatrix(); gl.glViewport(0, 0, xSize, ySize); } private void drawCube(GL2 gl) { // Six faces of cube // Top face gl.glPushMatrix(); gl.glRotatef(-90, 1, 0, 0); gl.glRotatef(180, 0, 0, 1); drawFace(gl, size, color, border, "Y+"); gl.glPopMatrix(); // Front face drawFace(gl, size, color, border, "Z+"); // Right face gl.glPushMatrix(); gl.glRotatef(90, 0, 1, 0); gl.glPushMatrix(); gl.glRotatef(90, 0, 0, 1); drawFace(gl, size, color, border, "X+"); gl.glPopMatrix(); // Back face gl.glRotatef(90, 0, 1, 0); gl.glPushMatrix(); gl.glRotatef(180, 0, 0, 1); drawFace(gl, size, color, border, "Z-"); gl.glPopMatrix(); // Left face gl.glRotatef(90, 0, 1, 0); gl.glRotatef(-90, 0, 0, 1); drawFace(gl, size, color, border, "X+"); gl.glPopMatrix(); // Bottom face gl.glPushMatrix(); gl.glRotatef(90, 1, 0, 0); drawFace(gl, size, color, border, "Y-"); gl.glPopMatrix(); } private void drawFace(GL2 gl, float faceSize, float[] color, float[] border, String text) { float halfFaceSize = faceSize / 2; float borderSize = halfFaceSize * 0.8f; float layer2 = halfFaceSize + 0.001f; // Face is centered around the local coordinate system's z axis, // at a z depth of faceSize / 2 gl.glColor3f(border[0], border[1], border[2]); gl.glBegin(GL_QUADS); gl.glVertex3f(-halfFaceSize, -halfFaceSize, halfFaceSize); gl.glVertex3f( halfFaceSize, -halfFaceSize, halfFaceSize); gl.glVertex3f( halfFaceSize, halfFaceSize, halfFaceSize); gl.glVertex3f(-halfFaceSize, halfFaceSize, halfFaceSize); gl.glEnd(); gl.glColor3f(color[0], color[1], color[2]); gl.glBegin(GL_QUADS); gl.glVertex3f(-borderSize, -borderSize, layer2); gl.glVertex3f( borderSize, -borderSize, layer2); gl.glVertex3f( borderSize, borderSize, layer2); gl.glVertex3f(-borderSize, borderSize, layer2); gl.glEnd(); // Now draw the overlaid text. In this setting, we don't want the // text on the backward-facing faces to be visible, so we enable // back-face culling; and since we're drawing the text over other // geometry, to avoid z-fighting we disable the depth test. We // could plausibly also use glPolygonOffset but this is simpler. // Note that because the TextRenderer pushes the enable state // internally we don't have to reset the depth test or cull face // bits after we're done. renderer.begin3DRendering(); gl.glDisable(GL_DEPTH_TEST); gl.glEnable(GL_CULL_FACE); // Note that the defaults for glCullFace and glFrontFace are // GL_BACK and GL_CCW, which match the TextRenderer's definition // of front-facing text. Rectangle2D bounds = renderer.getBounds(text); float w = (float) bounds.getWidth(); float h = (float) bounds.getHeight(); renderer.draw3D(text, w / -2.0f * textScaleFactor, h / -2.0f * textScaleFactor, layer2, textScaleFactor); renderer.end3DRendering(); } }