package com.ericsson.tic.vi; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GL; import javax.media.opengl.glu.GLU; import com.sun.opengl.util.texture.Texture; import com.sun.opengl.util.texture.TextureIO; import javax.media.opengl.glu.GLUquadric; import java.io.File; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.IOException; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import com.sun.opengl.util.GLUT; import java.nio.FloatBuffer; import java.nio.ByteBuffer; import com.sun.opengl.util.BufferUtil; import com.sun.opengl.util.j2d.TextRenderer; import java.awt.Font; import java.awt.geom.Rectangle2D; import java.util.Arrays; import com.sun.opengl.util.texture.TextureData; import java.text.NumberFormat; /** * This class handles all OpenGL calls and rendering. * * @author Sami Matilainen * @version 1.0 (2008-12-05) */ public class Renderer implements GLEventListener { /** The camera associated with the view which is being rendered. */ public Camera camera; /** Globe radius. */ public float globeRadius = 1.0f; /** Scale of the plane. */ public float planeScale = 1.0f; /** A counter for node movement. */ public int nodeMoveCounter = 0; /** The renderer id. */ protected int id; /** The rotation angle of the skybox. */ private float skyboxRotation; /** The OpenGL rendering context. */ public GL gl; /** The GLU rendering context. */ public GLU glu; /** A glut rendering context. */ public GLUT glut; /** A GLUquadric. */ public GLUquadric quadric; /** The parent panel of this renderer. pv stands for Parent View. */ private ViewPanel pv; /** A text renderer field. */ private TextRenderer textRenderer; /** A text renderer field. */ private TextRenderer labelTextRenderer; /** Used by animator. deprecated (?) */ public boolean readyForAnimator = false; /** A drawable. Used by animator. deprecated (?) */ public GLAutoDrawable drawableForAnimator; /** Specifies the number formatting of floats. */ public NumberFormat form; /** An array with the allowed line width ranges of this OGL environment. */ float lineWidthRanges[] = new float[2]; /** A counter used for the limit rendering functionality. */ private int limitedRenderCounter = 0; /** Used for shading/lighting. */ private float diffuseMaterial[] = {0.8f, 0.8f, 0.8f, 1.0f}; /** Used for shading/lighting. */ private float matSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f}; /** Used for shading/lighting. */ private float lightAmbient0[] = {0.0f, 0.0f, 0.4f, 1.0f}; /** Used for shading/lighting. */ private float lightDiffuse0[] = {1.0f, 1.0f, 1.0f, 1.0f}; /** Used for shading/lighting. */ private float lightSpecular0[] = {1.0f, 1.0f, 1.0f, 1.0f}; /** Used for shading/lighting. */ private float lightPos0[] = {0.0f, 3.0f, 3.0f, 0.0f}; /** Used for shading/lighting. */ private float lightAmbient1[] = {0.0f, 0.0f, 0.4f, 1.0f}; /** Used for shading/lighting. */ private float lightDiffuse1[] = {0.8f, 0.8f, 1.f, 1.0f}; /** Used for shading/lighting. */ private float lightSpecular1[] = {0.6f, 0.6f, 1.0f, 1.0f}; /** Used for shading/lighting. */ private float lightPos1[] = {0.0f, -4.0f, -2.0f, 0.0f}; /** Used for shading/lighting. */ private float fogColor[] = {0.0f, 0.0f, 0.2f, 1.0f}; /** * Sets up a renderer. * * @param camera The camera associated with the view. * @param id The renderer id. * @param parentView The parent ViewPanel of this renderer. */ public Renderer(int id, Camera camera, ViewPanel parentView) { readyForAnimator = false; this.camera = camera; this.id = id; this.pv = parentView; } /** * Called by the OpenGL rendering engine when the scene is initated. * @param drawable The Drawable. */ public void init(GLAutoDrawable drawable) { // possible optimizations: // set rendering hints to something less than "GL_NICEST" // disable texturing, shading, lighting, blending etc. // set fog to something less than "GL_LINEAR" textRenderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 56)); labelTextRenderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 18)); form = NumberFormat.getInstance(); form.setMaximumFractionDigits(4); skyboxRotation = 0.0f; GL gl = drawable.getGL(); // Enable VSync gl.setSwapInterval(VI.verticalSync); gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, diffuseMaterial, 0); gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, matSpecular, 0); gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 30.0f); gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, lightAmbient0, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, lightDiffuse0, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, lightSpecular0, 0); gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lightPos0, 0); gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, lightAmbient1, 0); gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, lightDiffuse1, 0); gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, lightSpecular1, 0); gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, lightPos1, 0); gl.glColorMaterial(GL.GL_FRONT, GL.GL_DIFFUSE); gl.glEnable(GL.GL_CULL_FACE); gl.glEnable(GL.GL_LIGHTING); gl.glEnable(GL.GL_LIGHT0); gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_COLOR_MATERIAL); gl.glDisable(GL.GL_TEXTURE_2D); gl.glFogi(GL.GL_FOG_MODE, GL.GL_LINEAR); gl.glFogfv(GL.GL_FOG_COLOR, fogColor, 0); gl.glFogf(GL.GL_FOG_DENSITY, 0.00001f); gl.glFogf(GL.GL_FOG_START, 2.0f); gl.glFogf(GL.GL_FOG_END, 5.0f); gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); gl.glHint(GL.GL_FOG_HINT, GL.GL_NICEST); float values[] = new float[2]; gl.glGetFloatv(GL.GL_LINE_WIDTH_GRANULARITY, values, 0); gl.glGetFloatv(GL.GL_LINE_WIDTH_RANGE, values, 0); gl.glEnable(GL.GL_LINE_SMOOTH); gl.glEnable(GL.GL_POLYGON_SMOOTH); gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST); gl.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST); // Setup the drawing area and shading mode gl.glClearColor(0.0f, 0.0f, 0.05f, 1.0f); gl.glShadeModel(GL.GL_SMOOTH); gl = drawable.getGL(); glu = new GLU(); glut = new GLUT(); quadric = glu.gluNewQuadric(); // set normal orientation to outside glu.gluQuadricOrientation(quadric, GLU.GLU_OUTSIDE); // creates texture coords glu.gluQuadricTexture(quadric, true); if (!VI.texturesLoaded) { loadTextures(); } drawable.setAutoSwapBufferMode(false); drawableForAnimator = drawable; readyForAnimator = true; } /** * Called by the OpenGL rendering engine every time the scene is redrawn. * @param drawable The Drawable. */ public void display(GLAutoDrawable drawable) { if (VI.betterLightingOn) { gl.glEnable(GL.GL_LIGHT1); } else { gl.glDisable(GL.GL_LIGHT1); } if (VI.fogOn && (pv.globeViewOn || pv.transitionStateOn)) { gl.glEnable(GL.GL_FOG); } else { gl.glDisable(GL.GL_FOG); } if (pv.limitRendering) { limitedRenderCounter++; if (limitedRenderCounter >= 10) { limitedRenderCounter = 0; } } if (limitedRenderCounter == 0 || !pv.limitRendering) { gl = drawable.getGL(); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); glu.gluLookAt( camera.camX, camera.camY, camera.camZ, camera.lookX, camera.lookY, camera.lookZ, camera.upX, camera.upY, camera.upZ); drawScene(drawable); gl.glFlush(); } } /** * Draws the scene. * @param drawable The Drawable. */ private void drawScene(GLAutoDrawable drawable) { gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); if (pv.picking) { gl.glPushMatrix(); gl.glRotatef(camera.rotAngle, 0.0f, 1.0f, 0.0f); pick(); gl.glFlush(); gl.glPopMatrix(); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); pv.picking = false; } gl.glPushMatrix(); if (VI.skyboxOn) { gl.glPushMatrix(); gl.glScalef(10.0f, 10.0f, 10.0f); gl.glRotatef(skyboxRotation, 0.0f, 1.0f, 0.0f); skyboxRotation = skyboxRotation + 0.03f; drawSkybox(); gl.glPopMatrix(); } drawView(); //drawNav(); // comment out when not in use if (pv.viewTitleOn) { labelTextRenderer.beginRendering( drawable.getWidth(), drawable.getHeight()); labelTextRenderer.setColor(0.2f, 0.7f, 1.0f, 0.8f); labelTextRenderer.draw("View " + pv.id, 5, 5); labelTextRenderer.endRendering(); } gl.glPopMatrix(); drawable.swapBuffers(); } /** * Draws the objects specific to this view. */ public void drawView() { gl.glRotatef(camera.rotAngle, 0.0f, 1.0f, 0.0f); drawLabels(false); if (pv.transitionStateOn) { // transition state if (pv.globePhasingOut || pv.globePhasingIn) { drawGlobe(false); } if (pv.planePhasingOut || pv.planePhasingIn) { drawPlane(false); } } else { // stable state if (pv.globeViewOn) { drawGlobe(false); } else if (pv.planeViewOn) { drawPlane(false); } if (pv.barsOn) { drawCumulativeBars(); } } drawNodes(false); if (pv.graphRelationsOn) { drawRelations(false); } } /** * Draws all labels. * @param picking if true, renders using the picking color. */ public void drawLabels(boolean picking) { if (pv.labelsOn) { gl.glPushMatrix(); gl.glDisable(GL.GL_LIGHTING); for (int i = 0; i < pv.nodeArr.length; i++) { if (pv.nodeArr[i].highlighted) { drawNodeLabel(pv.nodeArr[i], picking); } } gl.glEnable(GL.GL_LIGHTING); gl.glPopMatrix(); } } /** * Draws a node label. * @param node The node which this label is associated with. * @param picking if true, renders using the picking color. */ public void drawNodeLabel(GraphNode node, boolean picking) { gl.glPushMatrix(); gl.glColor3f(1.0f, 1.0f, 1.0f); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f( node.x, node.y, node.z); gl.glVertex3f( node.label.x, node.label.y, node.label.z); gl.glEnd(); gl.glPopMatrix(); drawFloatingWindow(node, picking); } /** * Draws a globe. * @param picking if true, renders using the picking color. */ private void drawGlobe(boolean picking) { gl.glPushMatrix(); if (picking && pv.texturesOn) { gl.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); gl.glColor3f(0.0f, 0.0f, 0.0f); glu.gluSphere(quadric, globeRadius, 64, 64); } else if (pv.texturesOn) { VI.worldTex.bind(); gl.glEnable(GL.GL_TEXTURE_2D); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // rotate the sphere to present 'upright' gl.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); glu.gluSphere(quadric, globeRadius, 64, 64); gl.glDisable(GL.GL_TEXTURE_2D); } gl.glPopMatrix(); } /** * Draws a plane. * @param picking if true, renders using the picking color. */ private void drawPlane(boolean picking) { gl.glPushMatrix(); if (picking) { gl.glColor3f(0.0f, 0.0f, 0.0f); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-2.0f, -1.0f, -0.01f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(2.0f, -1.0f, -0.01f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(2.0f, 1.0f, -0.01f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-2.0f, 1.0f, -0.01f); gl.glEnd(); } else if (pv.texturesOn) { gl.glPushMatrix(); gl.glScalef(planeScale, planeScale, 1.0f); gl.glEnable(GL.GL_TEXTURE_2D); gl.glDisable(GL.GL_LIGHTING); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); VI.worldTex.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-2.0f, -1.0f, -0.01f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(2.0f, -1.0f, -0.01f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(2.0f, 1.0f, -0.01f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-2.0f, 1.0f, -0.01f); gl.glEnd(); gl.glEnable(GL.GL_LIGHTING); gl.glDisable(GL.GL_TEXTURE_2D); gl.glPopMatrix(); drawBackPlane(picking); } else { drawBackPlane(picking); } gl.glPopMatrix(); } /** * Draws a neutral background for the plane view to block the stars. * @param picking if true, renders using the picking color. */ private void drawBackPlane(boolean picking) { gl.glPushMatrix(); // do this allways! if (picking || !picking) { gl.glScalef(planeScale * 12.0f, planeScale * 12.0f, 1.0f); gl.glDisable(GL.GL_LIGHTING); gl.glColor3f(0.0f, 0.0f, 0.0f); gl.glBegin(GL.GL_QUADS); gl.glVertex3f(-2.0f, -1.0f, -0.03f); gl.glVertex3f(2.0f, -1.0f, -0.03f); gl.glVertex3f(2.0f, 1.0f, -0.03f); gl.glVertex3f(-2.0f, 1.0f, -0.03f); gl.glEnd(); gl.glEnable(GL.GL_LIGHTING); } gl.glPopMatrix(); } /** * Draw the skybox. */ private void drawSkybox() { gl.glPushMatrix(); gl.glEnable(GL.GL_TEXTURE_2D); gl.glDisable(GL.GL_LIGHTING); gl.glDisable(GL.GL_FOG); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // top VI.skyboxTexTop.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, 1.0f); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, 1.0f, -1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, 1.0f, -1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 1.0f); gl.glEnd(); // bottom VI.skyboxTexBottom.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, -1.0f, -1.0f); gl.glEnd(); // side1 (front) VI.skyboxTexSide1.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, -1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, -1.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, -1.0f); gl.glEnd(); // side2 (left) VI.skyboxTexSide2.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, -1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 1.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, -1.0f); gl.glEnd(); // side3 (back) VI.skyboxTexSide3.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, 1.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(1.0f, 1.0f, 1.0f); gl.glEnd(); // side4 (right) VI.skyboxTexSide4.bind(); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, 1.0f); gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f(-1.0f, -1.0f, -1.0f); gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, -1.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-1.0f, 1.0f, 1.0f); gl.glEnd(); if (VI.fogOn && (pv.globeViewOn || pv.transitionStateOn)) { gl.glEnable(GL.GL_FOG); } gl.glEnable(GL.GL_LIGHTING); gl.glDisable(GL.GL_TEXTURE_2D); gl.glPopMatrix(); } /** * Draws three balls which show the axes to make navigation easier in * debug mode. x = red, y = green and z = blue (as in "xyz = RGB") */ private void drawNav() { gl.glPushMatrix(); gl.glDisable(GL.GL_LIGHTING); gl.glTranslatef(1.0f, 0.0f, 1.0f); gl.glPushMatrix(); gl.glColor4f(1.0f, 0.0f, 0.0f, 1.0f); // red = x gl.glTranslatef(0.5f, 0.0f, 0.0f); gl.glScalef(0.05f, 0.05f, 0.05f); glu.gluSphere(quadric, 1.0f, 32, 32); gl.glPopMatrix(); gl.glPushMatrix(); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(0.5f, 0.0f, 0.0f); gl.glEnd(); gl.glPopMatrix(); gl.glPushMatrix(); gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f); // green = y gl.glTranslatef(0.0f, 0.5f, 0.0f); gl.glScalef(0.05f, 0.05f, 0.05f); glu.gluSphere(quadric, 1.0f, 32, 32); gl.glPopMatrix(); gl.glPushMatrix(); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(0.0f, 0.5f, 0.0f); gl.glEnd(); gl.glPopMatrix(); gl.glPushMatrix(); gl.glColor4f(0.0f, 0.0f, 1.0f, 1.0f); // blue = z gl.glTranslatef(0.0f, 0.0f, 0.5f); gl.glScalef(0.05f, 0.05f, 0.05f); glu.gluSphere(quadric, 1.0f, 32, 32); gl.glPopMatrix(); gl.glPushMatrix(); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(0.0f, 0.0f, 0.5f); gl.glEnd(); gl.glPopMatrix(); gl.glPushMatrix(); gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // white = origin gl.glTranslatef(0.0f, 0.0f, 0.0f); gl.glScalef(0.05f, 0.05f, 0.05f); glu.gluSphere(quadric, 1.0f, 32, 32); gl.glPopMatrix(); gl.glEnable(GL.GL_LIGHTING); gl.glPopMatrix(); } /** * Draws the nodes. * @param picking if true, renders using the picking color. */ private void drawNodes(boolean picking) { float[] color = new float[3]; float shadingModifier = 1.0f; for (int i = 0; i < pv.nodeArr.length; i++) { gl.glPushMatrix(); if (!VI.nodeShadingOn) { gl.glDisable(GL.GL_LIGHTING); } if (picking) { gl.glDisable(GL.GL_LIGHTING); gl.glColor3b( pv.nodeArr[i].pid[0], pv.nodeArr[i].pid[1], pv.nodeArr[i].pid[2]); } else { if (!pv.labelsOn && pv.nodeArr[i].highlighted) { gl.glDisable(GL.GL_LIGHTING); gl.glColor3f(1.0f, 0.0f, 0.0f); } else { if (pv.defaultNodeColorsOn && VI.dataArr[pv.nodeData].parseName.equals("null")) { color[0] = pv.nodeArr[i].type.color[0]; color[1] = pv.nodeArr[i].type.color[1]; color[2] = pv.nodeArr[i].type.color[2]; } else { color = VI.normalizedColor( VI.dataArr[pv.nodeData].colors[0], VI.dataArr[pv.nodeData].colors[1], VI.dataArr[pv.nodeData].vn[VI.nodeArr[i].id-1][VI.dataArr[pv.nodeData].category.id][0]); } if (pv.levelNodeShadingOn) { if (pv.nodeArr[i].type.level == 0) { shadingModifier = 1.0f; } else if (pv.nodeArr[i].type.level == 1) { shadingModifier = 1.5f; } else { shadingModifier = 2.0f * pv.nodeArr[i].type.level; } color[0] = color[0]/shadingModifier; color[1] = color[1]/shadingModifier; color[2] = color[2]/shadingModifier; } gl.glColor3f(color[0], color[1], color[2]); } } gl.glTranslatef(pv.nodeArr[i].x, pv.nodeArr[i].y, pv.nodeArr[i].z); gl.glScalef(0.02f, 0.02f, 0.02f); float sizeByLevel = 2.0f - 0.5f*((float)pv.nodeArr[i].type.level); sizeByLevel = 0.1f + sizeByLevel*camera.zoom*0.2f; if (pv.graphNodesOn && pv.nodeOfTypeOn[pv.nodeArr[i].type.id-1]) { // todo: add setting for adjusting the amount of divs // todo: better control of the size of the nodes // todo: better sizeByLevel /* // below: kinda ugly if (pv.nodeArr[i].type.level >= 3) { gl.glColor3f(color[0]/4.0f, color[1]/4.0f, color[2]/4.0f); glu.gluSphere(quadric, sizeByLevel, 8, 8); } else if (pv.nodeArr[i].type.level == 2) { gl.glColor3f(color[0]/2.0f, color[1]/2.0f, color[2]/2.0f); glu.gluSphere(quadric, sizeByLevel, 8, 8); } else if (pv.nodeArr[i].type.level == 1) { gl.glColor3f(color[0]/1.5f, color[1]/1.5f, color[2]/1.5f); glu.gluSphere(quadric, sizeByLevel, 8, 8); } else if (pv.nodeArr[i].type.level == 0) { //gl.glPushMatrix(); //glut.glutWireSphere(sizeByLevel, 8, 8); //gl.glPopMatrix(); glu.gluSphere(quadric, sizeByLevel, 16, 16); //glut.glutSolidDodecahedron(); //gl.glPushMatrix(); //billboardCheatSphericalBegin(); //glut.glutSolidTorus(sizeByLevel/120.0f, sizeByLevel*2.0f/120.0f, 16, 16); //billboardCheatSphericalEnd(); //gl.glPopMatrix(); } */ // use spheres for now // todo: create different objects and import them // a star shape should work pretty well glu.gluSphere(quadric, sizeByLevel, 16, 16); } if (!pv.labelsOn && pv.nodeArr[i].highlighted) { gl.glEnable(GL.GL_LIGHTING); } if (picking) { gl.glEnable(GL.GL_LIGHTING); } if (!VI.nodeShadingOn) { gl.glEnable(GL.GL_LIGHTING); } gl.glPopMatrix(); } } /** * Draws all relations (lines). * @param picking if true, renders using the picking color. */ private void drawRelations(boolean picking) { gl.glPushMatrix(); gl.glDisable(GL.GL_LIGHTING); if (VI.lineAAOn) { gl.glEnable(GL.GL_LINE_SMOOTH); gl.glEnable(GL.GL_POLYGON_SMOOTH); gl.glEnable(GL.GL_BLEND); } gl.glGetFloatv(GL.GL_LINE_WIDTH_RANGE, lineWidthRanges, 0); float[] color = new float[3]; float[] color4 = new float[4]; if (picking) { color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; } else { color[0] = 1.0f; color[1] = 1.0f; color[2] = 1.0f; } float minLineWidth = 1.0f; if (VI.lineAAOn) { minLineWidth = 2.0f; } if (lineWidthRanges[0] > minLineWidth) { minLineWidth = lineWidthRanges[0]; } for (int i = 0; i < pv.nodeRelArr.length; i++) { FloatBuffer controlPointBuf = FloatBuffer.allocate(12); controlPointBuf.put(pv.nodeRelArr[i].a.x); controlPointBuf.put(pv.nodeRelArr[i].a.y); controlPointBuf.put(pv.nodeRelArr[i].a.z); controlPointBuf.put(pv.nodeRelArr[i].controlPointA[0]); controlPointBuf.put(pv.nodeRelArr[i].controlPointA[1]); controlPointBuf.put(pv.nodeRelArr[i].controlPointA[2]); controlPointBuf.put(pv.nodeRelArr[i].controlPointB[0]); controlPointBuf.put(pv.nodeRelArr[i].controlPointB[1]); controlPointBuf.put(pv.nodeRelArr[i].controlPointB[2]); controlPointBuf.put(pv.nodeRelArr[i].b.x); controlPointBuf.put(pv.nodeRelArr[i].b.y); controlPointBuf.put(pv.nodeRelArr[i].b.z); color = VI.normalizedColor( VI.dataArr[pv.relData].colors[0], VI.dataArr[pv.relData].colors[1], VI.dataArr[pv.relData].vn[VI.nodeRelArr[i].id-1][VI.dataArr[pv.relData].category.id][0]); color4[0] = color[0]; color4[1] = color[1]; color4[2] = color[2]; color4[3] = 0.75f; if (pv.nodeOfTypeOn[pv.nodeRelArr[i].a.type.id-1] && pv.nodeOfTypeOn[pv.nodeRelArr[i].b.type.id-1]) { if (VI.dataArr[pv.relData].dimension == 2) { gl.glLineWidth(minLineWidth + lineWidthRanges[1] * VI.dataArr[pv.relData].vn[VI.nodeRelArr[i].id-1][VI.dataArr[pv.relData].category.id][1]); } else { gl.glLineWidth(minLineWidth); } drawBezierCurve(controlPointBuf, color4); gl.glLineWidth(1.0f); } } if (VI.lineAAOn) { gl.glDisable(GL.GL_BLEND); gl.glDisable(GL.GL_POLYGON_SMOOTH); gl.glDisable(GL.GL_LINE_SMOOTH); } gl.glEnable(GL.GL_LIGHTING); gl.glPopMatrix(); } /** * Draw all cumulative bars */ public void drawCumulativeBars() { if (VI.brushingOn && pv.brushingMarkersOn) { drawBrushingMarkers(); } if (!VI.barShadingOn) { gl.glDisable(GL.GL_LIGHTING); } for (int i = 0; i < pv.nodeArr.length; i++) { float[][] color3f = getCategoryColors(VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].category.id); float height = 0.2f; int barType = VI.barType; if (VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].parseStrat != 2) { barType = 0; } if (pv.nodeOfTypeOn[pv.nodeArr[i].type.id-1]) { if (!VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].parseName.equals("null")) { drawCumulativeBar( pv.nodeArr[i], barType, VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].vn[pv.nodeArr[i].id-1], color3f); } } } if (!VI.barShadingOn) { gl.glDisable(GL.GL_LIGHTING); } } /** * Draws a cumulative bar * @param node A node. * @param barType The type of the bar. this is a value between 0 and 9. * @param height The expected height of the bar. * @param color3f An array of colors to represent the multiple dimensional (cumulative) bar. */ public void drawCumulativeBar( GraphNode node, int barType, float[][] height, float[][] color3f) { float barRadius = (camera.zoom*0.002f) + 0.001f; float zoomHeightScale = 0.2f*camera.zoom; gl.glPushMatrix(); gl.glTranslatef(node.x, node.y, node.z); gl.glPushMatrix(); if (pv.globeViewOn) { gl.glRotatef(node.lon, 0.0f, 1.0f, 0.0f); gl.glRotatef(node.lat, -1.0f, 0.0f, 0.0f); } else if (pv.planeViewOn) { gl.glRotatef(90.0f, -1.0f, 0.0f, 0.0f); } float total = 0; float used = 0; float[] shadedColor = new float[3]; // barType 0 is the default type for (int i = 0; i < height.length; i++) { gl.glPushMatrix(); gl.glColor3f(color3f[i][0], color3f[i][1], color3f[i][2]); int div = height.length*height[0].length; if (barType == 1 || barType == 2) { glut.glutSolidCylinder(barRadius, (zoomHeightScale*height[i][0])/div, 8, 1); gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, ((zoomHeightScale*height[i][0]))/div); total = total + zoomHeightScale*height[i][0]/div + zoomHeightScale*height[i][1]/div; used = used + zoomHeightScale*height[i][0]/div; } else if (barType == 3 || barType == 4) { glut.glutSolidCylinder(barRadius, (zoomHeightScale*height[i][0])/div, 8, 1); gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, (zoomHeightScale*height[i][0]/div + zoomHeightScale*height[i][1]/div)); total = total + zoomHeightScale*height[i][0]/div + zoomHeightScale*height[i][1]/div; used = used + zoomHeightScale*height[i][0]/div; } else if (barType == 5) { // height = x/(x+y) float val = zoomHeightScale*(height[i][0]/(height[i][0] + height[i][1]))/div; glut.glutSolidCylinder(barRadius, val, 8, 1); gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, val); } else if (barType == 6) { float subHeight = 0; for (int j = 0; j < height[i].length; j++) { subHeight = subHeight + zoomHeightScale*(height[i][j])/div; gl.glPushMatrix(); if (height[i][j] != 0.0f) { glut.glutSolidCylinder(barRadius, zoomHeightScale*(height[i][j])/div, 8, 1); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, (zoomHeightScale*(height[i][j]))/div); gl.glTranslatef(0.0f, 0.0f, 0.005f); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, subHeight + 0.02f); } else if (barType == 7) { float subHeight = 0; for (int j = 0; j < height[i].length; j++) { shadedColor[0] = (color3f[i][0]/height[i].length) * (height[i].length-j); shadedColor[1] = (color3f[i][1]/height[i].length) * (height[i].length-j); shadedColor[2] = (color3f[i][2]/height[i].length) * (height[i].length-j); gl.glColor3f(shadedColor[0], shadedColor[1], shadedColor[2]); subHeight = subHeight + (zoomHeightScale*height[i][j])/div; gl.glPushMatrix(); if (height[i][j] != 0.0f) { glut.glutSolidCylinder(barRadius, (zoomHeightScale*height[i][j])/div, 8, 1); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, (zoomHeightScale*height[i][j])/div); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, subHeight); } else if (barType == 8) { float subHeight = 0; for (int j = 0; j < height[i].length; j++) { subHeight = subHeight + (zoomHeightScale*height[i][j])/div; gl.glPushMatrix(); float barRadiusMod = (barRadius/height[i].length) * (height[i].length-j); if (height[i][j] != 0.0f) { glut.glutSolidCylinder(barRadiusMod, (zoomHeightScale*height[i][j])/div, 8, 1); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, ((zoomHeightScale*height[i][j]))/div); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, subHeight); } else if (barType == 9) { float subHeight = 0; for (int j = 0; j < height[i].length; j++) { shadedColor[0] = (color3f[i][0]/height[i].length) * (height[i].length-j); shadedColor[1] = (color3f[i][1]/height[i].length) * (height[i].length-j); shadedColor[2] = (color3f[i][2]/height[i].length) * (height[i].length-j); gl.glColor3f(shadedColor[0], shadedColor[1], shadedColor[2]); subHeight = subHeight + (zoomHeightScale*height[i][j])/div; gl.glPushMatrix(); float barRadiusMod = (barRadius/height[i].length) * (height[i].length-j); if (height[i][j] != 0.0f) { glut.glutSolidCylinder(barRadiusMod, (zoomHeightScale*height[i][j])/div, 8, 1); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, (zoomHeightScale*height[i][j])/div); } gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, subHeight); } else { glut.glutSolidCylinder(barRadius, (zoomHeightScale*height[i][0])/div, 8, 1); gl.glPopMatrix(); gl.glTranslatef(0.0f, 0.0f, (zoomHeightScale*height[i][0])/div); } } float[] clr = {0.7f, 0.7f, 0.7f}; if (barType == 1) { gl.glPushMatrix(); gl.glColor3f(clr[0], clr[1], clr[2]); gl.glTranslatef(0.0f, 0.0f, -used); gl.glLineWidth(4.0f); gl.glBegin(GL.GL_LINES); gl.glVertex3f(0, 0, 0); gl.glVertex3f(0, 0, total); gl.glEnd(); gl.glLineWidth(1.0f); gl.glPopMatrix(); } else if (barType == 2) { gl.glColor3f(clr[0], clr[1], clr[2]); gl.glPushMatrix(); gl.glTranslatef(0.0f, 0.0f, -used); glut.glutSolidCylinder(barRadius/1.6f, total, 8, 1); gl.glPopMatrix(); } else if (barType == 3) { gl.glColor3f(clr[0], clr[1], clr[2]); gl.glPushMatrix(); gl.glTranslatef(0.0f, 0.0f, -total); glut.glutSolidCylinder(barRadius/1.6f, total, 8, 1); gl.glPopMatrix(); } else if (barType == 4) { gl.glColor3f(clr[0], clr[1], clr[2]); gl.glPushMatrix(); gl.glTranslatef(-0.01f, 0.0f, -total); glut.glutSolidCylinder(barRadius/1.6f, total, 8, 1); gl.glPopMatrix(); } gl.glPopMatrix(); gl.glPopMatrix(); } /** * Draw brushingMarkers */ public void drawBrushingMarkers() { float[] color3f = new float[3]; color3f = VI.brushingPrimaryColor.getColorComponents(color3f); float scale = 0.003f + 0.003f*camera.zoom; float zoomHeightScale = 0.2f*camera.zoom; float gapScale = 0.005f + 0.005f*camera.zoom; float barLength = 0.0f; for (int i = 0; i < pv.nodeArr.length; i++) { if (VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].category.id != 0) { for (int k = 0; k < VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].category.cats.length; k++) { if (k == VI.brushedCategory) { if (hasPositiveValue(VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].value, i, k)) { gl.glPushMatrix(); gl.glTranslatef( pv.nodeArr[i].x, pv.nodeArr[i].y, pv.nodeArr[i].z); gl.glPushMatrix(); if (pv.globeViewOn) { gl.glRotatef(pv.nodeArr[i].lon, 0.0f, 1.0f, 0.0f); gl.glRotatef(pv.nodeArr[i].lat, -1.0f, 0.0f, 0.0f); } else if (pv.planeViewOn) { gl.glRotatef(90.0f, -1.0f, 0.0f, 0.0f); } barLength = 0.0f; for (int j = 0; j < VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].value[VI.nodeArr[i].id-1].length; j++) { barLength = barLength + (VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].vn[VI.nodeArr[i].id-1][j][0])/VI.dataArr[pv.barData[pv.nodeArr[i].type.id-1]].value[VI.nodeArr[i].id-1].length; } barLength = barLength*zoomHeightScale + gapScale; gl.glTranslatef(0.0f, 0.0f, barLength); gl.glScalef(scale, scale, scale); gl.glDisable(GL.GL_LIGHTING); gl.glColor3f(color3f[0], color3f[1], color3f[2]); glu.gluSphere(quadric, 1.0f, 8, 8); gl.glEnable(GL.GL_LIGHTING); gl.glPopMatrix(); gl.glPopMatrix(); } } } } } } /** * Determine if the array has any positive values. Used by the brushing algorithm. * @param v An array of values. */ public boolean hasPositiveValue(float[][][] v, int node, int cat) { //for (int i = 0; i < v.length; i++) { //for (int j = 0; j < v[0].length; j++) { for (int k = 0; k < v[0][0].length; k++) { if (v[node][cat][k] > 0.0f) { return true; } } //} //} return false; } /** * Draws a floating window frame. * @param node The node this floating window is tied to. * @param pos the position of the floating window in 3D-space. * @param picking if true, renders using the picking color. * @param frameColor The color of the frame. * @param borderColor The color of the border. * @param width the width of the floating window. * @param height the height of the floating window. */ public void drawFloatingWindowFrame( GraphNode node, float[] pos, boolean picking, float[] frameColor, float[] borderColor, float width, float height) { gl.glPushMatrix(); gl.glTranslatef(pos[0], pos[1], pos[2]); if (true) { if (picking) { gl.glColor3b( node.label.pid[0], node.label.pid[1], node.label.pid[2]); gl.glBegin(GL.GL_QUADS); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(width, 0.0f, 0.0f); gl.glVertex3f(width, height, 0.0f); gl.glVertex3f(0.0f, height, 0.0f); gl.glEnd(); } else { gl.glColor3f(frameColor[0], frameColor[1], frameColor[2]); gl.glBegin(GL.GL_QUADS); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(width, 0.0f, 0.0f); gl.glVertex3f(width, height, 0.0f); gl.glVertex3f(0.0f, height, 0.0f); gl.glEnd(); if (node.label.highlighted) { gl.glColor3f(1.0f, 1.0f, 0.0f); } else { gl.glColor3f(borderColor[0], borderColor[1], borderColor[2]); } gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.0f, 0.0f, 0.0001f); gl.glVertex3f(width, 0.0f, 0.0001f); gl.glVertex3f(width, 0.0f, 0.0001f); gl.glVertex3f(width, height, 0.0001f); gl.glVertex3f(width, height, 0.0001f); gl.glVertex3f(0.0f, height, 0.0001f); gl.glVertex3f(0.0f, height, 0.0001f); gl.glVertex3f(0.0f, 0.0f, 0.0001f); } gl.glEnd(); } gl.glPopMatrix(); } /** * Draws the closing button for the floating window. * * @param pos the relative position of the button in 3D-space. * @param picking if true, renders using the picking color. * @param pickingID A list of picking ID's. */ public void drawFloatingWindowCloseButton( float[] pos, byte[] pickingID, boolean picking) { float[] frameColor = {0.5f, 0.5f, 0.5f}; float[] borderColor = {1.0f, 1.0f, 1.0f}; float width = 0.04f; float height = 0.04f; gl.glPushMatrix(); gl.glTranslatef(pos[0], pos[1], pos[2]); if (true) { if (picking) { gl.glColor3b( pickingID[0], pickingID[1], pickingID[2]); } else { gl.glColor3f(frameColor[0], frameColor[1], frameColor[2]); } gl.glBegin(GL.GL_QUADS); gl.glVertex3f(0.0f, 0.0f, 0.0f); gl.glVertex3f(width, 0.0f, 0.0f); gl.glVertex3f(width, height, 0.0f); gl.glVertex3f(0.0f, height, 0.0f); gl.glEnd(); gl.glColor3f(borderColor[0], borderColor[1], borderColor[2]); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.0f, 0.0f, 0.001f); gl.glVertex3f(width, 0.0f, 0.001f); gl.glVertex3f(width, 0.0f, 0.001f); gl.glVertex3f(width, height, 0.001f); gl.glVertex3f(width, height, 0.001f); gl.glVertex3f(0.0f, height, 0.001f); gl.glVertex3f(0.0f, height, 0.001f); gl.glVertex3f(0.0f, 0.0f, 0.001f); gl.glEnd(); } gl.glPopMatrix(); } /** * Draws a floating window. * * @param node The node this floating window is tied to. * @param picking if true, renders using the picking color. */ public void drawFloatingWindow(GraphNode node, boolean picking) { float[] c = {0.0f, 0.0f, 0.0f}; float[] b = {1.0f, 1.0f, 1.0f}; float[] p = {0.0f, -0.32f, 0.0f}; float scale = 0.001f; gl.glPushMatrix(); gl.glTranslatef( node.label.x, node.label.y, node.label.z); billboardCheatSphericalBegin(); Rectangle2D rec = textRenderer.getBounds( node.label.title); float titleWidth = scale * (float)rec.getWidth(); if (titleWidth < 0.25f) { titleWidth = 0.25f; } float titleHeight = scale * (float)rec.getHeight(); gl.glTranslatef(0.0f, 0.32f, 0.0f); gl.glTranslatef(0.0f - (titleWidth + 0.04f + 0.06f + 0.03f)/2.0f, 0.0f, 0.0f); drawFloatingWindowFrame(node, p, picking, c, b, titleWidth + 0.04f + 0.06f + 0.03f, titleHeight + 0.04f + 0.32f); if (!picking) { textRenderer.begin3DRendering(); textRenderer.setColor(1.0f, 1.0f, 1.0f, 1.0f); textRenderer.draw3D( node.label.title, 0.02f, 0.02f, 0.0001f, scale); textRenderer.end3DRendering(); } p[0] = p[0] + titleWidth + 0.04f + 0.03f; p[1] = p[1] + 0.02f + 0.32f; p[2] = p[2] + 0.00001f; drawFloatingWindowCloseButton(p, node.pid, picking); float[][] color3f = getCategoryColors(VI.dataArr[pv.histData[node.type.id-1]].category.id); p[0] = p[0] - titleWidth - 0.04f - 0.03f + 0.03f; p[1] = p[1] - 0.32f; if (!picking) { drawHistogram(p, titleWidth + 0.04f + 0.04f, 0.28f, false, VI.dataArr[pv.histData[node.type.id-1]].vn[node.id-1], VI.dataArr[pv.histData[node.type.id-1]].value[node.id-1], color3f); } billboardCheatSphericalEnd(); gl.glPopMatrix(); } /** * Draws a histogram. * @param pos the position of the histogram in 3D-space. * @param ordered Set to true to have bars ordered. (deprecated) * @param values Values to be used for building up the componenets of the bars. * @param realValues the real values used for the labels. * @param color3f The color of the border. * @param width the width of the floating window. * @param height the height of the floating window. */ public void drawHistogram( float[] pos, float width, float height, boolean ordered, float[][] values, float[][] realValues, float[][] color3f) { gl.glPushMatrix(); gl.glTranslatef(pos[0], pos[1], pos[2]); gl.glPushMatrix(); width = width - 0.05f; height = height - 0.01f; float barWidth = width/values.length; // this is flawed: // trying to put the bars in order of height // fails when there are bars of equal height /* float[] vals = new float[values.length]; for (int i = 0; i < values.length; i++) { vals[i] = values[i][0]; } float[][] sortedValues; for(int i = 0; i < values[0].length; i++) { sortedValues[i] = Arrays.copyOf(values[i], values.length); } Arrays.sort(sortedValues); int[] sortedIndex = new int[sortedValues.length]; for (int i = 0; i < sortedValues.length; i++) { for (int j = 0; j < values.length; j++) { for (int k = 0; k < values[0].length; k++) { if (sortedValues[i] == values[j][k]) { sortedIndex[i] = j; } } } } float highVal = 0.0f; for (int i = 0; i < values.length; i++) { if (highVal <= values[i][0]) { highVal = values[i]; } } float sf = 0.0f; if (sortedValues[values.length-1] != 0.0f) { sf = height/highVal; } sf = height/highVal; */ float highestVal = Float.MIN_VALUE; float highestRealVal = Float.MIN_VALUE; float highestValArr[] = new float[realValues[0].length]; for (int i = 0; i < realValues.length; i++) { for (int j = 0; j < realValues[i].length; j++) { if (highestVal <= values[i][j]) { highestVal = values[i][j]; highestRealVal = realValues[i][j]; } } } float sf = height/highestVal; if (highestVal != 0.0f) { gl.glPushMatrix(); gl.glPopMatrix(); gl.glBegin(GL.GL_LINES); gl.glLineWidth(1.0f); gl.glVertex3f(0.04f, height, 0.0f); gl.glVertex3f(0.02f, height, 0.0f); gl.glVertex3f(0.04f, 0.0f, 0.0f); gl.glVertex3f(0.02f, 0.0f, 0.0f); gl.glVertex3f(0.04f, 0.0f, 0.0f); gl.glVertex3f(0.04f, height, 0.0f); gl.glEnd(); gl.glTranslatef(0.05f, 0.0f, 0.0f); for (int i = 0; i < values.length; i++) { gl.glPushMatrix(); float currentL = 0.0f; float currentH = (barWidth-0.01f)/(values[i].length)-0.005f; for (int j = 0; j < values[i].length; j++) { float[] shadedColor = new float[3]; if (VI.barType == 7 || VI.barType == 9) { shadedColor[0] = (color3f[i][0]/values[i].length) * (values[i].length-j); shadedColor[1] = (color3f[i][1]/values[i].length) * (values[i].length-j); shadedColor[2] = (color3f[i][2]/values[i].length) * (values[i].length-j); } else { shadedColor[0] = color3f[i][0]; shadedColor[1] = color3f[i][1]; shadedColor[2] = color3f[i][2]; } gl.glColor3f(shadedColor[0], shadedColor[1], shadedColor[2]); gl.glBegin(GL.GL_QUADS); gl.glVertex3f(currentL, 0.0f, 0.0f); gl.glVertex3f(currentH, 0.0f, 0.0f); gl.glVertex3f(currentH, values[i][j]*sf, 0.0f); gl.glVertex3f(currentL, values[i][j]*sf, 0.0f); gl.glEnd(); gl.glPushMatrix(); gl.glRotatef(90.0f, 0.0f, 0.0f, 1.0f); gl.glPushMatrix(); textRenderer.begin3DRendering(); textRenderer.setColor(0.0f, 0.0f, 0.0f, 1.0f); textRenderer.draw3D(form.format(realValues[i][j]), 0.009f, 0.0f - (currentL + 0.025f/values[i].length - (currentL-currentH)/2.2f), 0.0007f, 0.0009f/values[i].length); textRenderer.setColor(1.0f, 1.0f, 1.0f, 1.0f); textRenderer.draw3D(form.format(realValues[i][j]), 0.01f, 0.0f - (currentL + 0.02f/values[i].length - (currentL-currentH)/2.2f), 0.001f, 0.0009f/values[i].length); gl.glPopMatrix(); textRenderer.end3DRendering(); gl.glPopMatrix(); currentL = currentH + 0.005f; currentH = currentH + (barWidth-0.01f)/(values[i].length); } gl.glPopMatrix(); gl.glTranslatef(barWidth, 0.0f, 0.0f); } } else { // do nothing - all bars are of zero height!!! } gl.glPopMatrix(); gl.glPushMatrix(); gl.glRotatef(90.0f, 0.0f, 0.0f, 1.0f); gl.glPushMatrix(); textRenderer.begin3DRendering(); textRenderer.setColor(1.0f, 1.0f, 1.0f, 1.0f); textRenderer.draw3D(form.format(highestRealVal), 0.2f, -0.01f, 0.0f, 0.0007f); gl.glPopMatrix(); textRenderer.end3DRendering(); gl.glPopMatrix(); gl.glColor3f(1.0f, 1.0f, 1.0f); gl.glPopMatrix(); } /** * Draws a bezier curve. * @param start point controlling the bezier curve. * @param controlStart point controlling the bezier curve. * @param end point controlling the bezier curve. * @param controlEnd point controlling the bezier curve. * @param color4f the color of the curve. */ public void drawBezierCurve( float[] start, float[] controlStart, float[] end, float[] controlEnd, float[] color4f) { gl.glPushMatrix(); FloatBuffer controlPointBuf = FloatBuffer.allocate(12); controlPointBuf.put(start[0]); controlPointBuf.put(start[1]); controlPointBuf.put(start[2]); controlPointBuf.put(controlStart[0]); controlPointBuf.put(controlStart[1]); controlPointBuf.put(controlStart[2]); controlPointBuf.put(end[0]); controlPointBuf.put(end[1]); controlPointBuf.put(end[2]); controlPointBuf.put(controlEnd[0]); controlPointBuf.put(controlEnd[1]); controlPointBuf.put(controlEnd[2]); controlPointBuf.rewind(); gl.glClear(GL.GL_COLOR_BUFFER_BIT); gl.glMap1f(GL.GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, 4, controlPointBuf); gl.glEnable(GL.GL_MAP1_VERTEX_3); gl.glColor4f(color4f[0], color4f[1], color4f[2], color4f[3]); gl.glBegin(GL.GL_LINE_STRIP); for (int i = 0; i <= 30; i++) { gl.glEvalCoord1f((float)i/30.0f); } gl.glEnd(); gl.glPopMatrix(); } /** * Draws a bezier curve. * @param points Control points for the bezier curve. * @param color4f the color of the curve. */ public void drawBezierCurve(FloatBuffer points, float[] color4f) { gl.glPushMatrix(); points.rewind(); gl.glMap1f(GL.GL_MAP1_VERTEX_3, 0.0f, 1.0f, 3, 4, points); gl.glEnable(GL.GL_MAP1_VERTEX_3); gl.glColor4f(color4f[0], color4f[1], color4f[2], color4f[3]); gl.glBegin(GL.GL_LINE_STRIP); for (int i = 0; i <= 30; i++) { gl.glEvalCoord1f((float)i/30.0f); } gl.glEnd(); gl.glPopMatrix(); } /** * Renders objects so they are facing the camera head on (billboarding). * I made this function for a project at KTH. */ public void billboardCheatSphericalBegin() { float[] modelview = new float[16]; // save the current modelview matrix gl.glPushMatrix(); // get the current modelview matrix gl.glGetFloatv(GL.GL_MODELVIEW_MATRIX , modelview, 0); // undo all rotations // beware all scaling is lost as well for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { if (i == j) { modelview[i*4+j] = 1.0f; } else { modelview[i*4+j] = 0.0f; } } } // set the modelview with no rotations gl.glLoadMatrixf(modelview, 0); } /** * Renders objects so they are facing the camera head on (billboarding). * I made this function for a project at KTH. */ public void billboardCheatSphericalEnd() { // restore the previously // stored modelview matrix gl.glPopMatrix(); } /** * Load all predefined textures. */ public void loadTextures() { TextureData wTexData; TextureData bTexData; TextureData sbTopTexData; TextureData sbBottomTexData; TextureData sb1TexData; TextureData sb2TexData; TextureData sb3TexData; TextureData sb4TexData; try { BufferedImage img = ImageIO.read(new File("images/map.png")); BufferedImage imgBlank = ImageIO.read(new File("images/blank.png")); BufferedImage imgSB1 = ImageIO.read(new File("images/skybox_1.png")); BufferedImage imgSB2 = ImageIO.read(new File("images/skybox_2.png")); BufferedImage imgSB3 = ImageIO.read(new File("images/skybox_3.png")); BufferedImage imgSB4 = ImageIO.read(new File("images/skybox_4.png")); BufferedImage imgSBTop = ImageIO.read(new File("images/skybox_top.png")); BufferedImage imgSBBottom = ImageIO.read(new File("images/skybox_bottom.png")); AffineTransform tx = AffineTransform.getScaleInstance(1, -1); tx.translate(0, -img.getHeight(null)); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); img = op.filter(img, null); wTexData = TextureIO.newTextureData(img, true); VI.worldTex = TextureIO.newTexture(wTexData); bTexData = TextureIO.newTextureData(imgBlank, true); VI.blankTex = TextureIO.newTexture(bTexData); sb1TexData = TextureIO.newTextureData(imgSB1, true); VI.skyboxTexSide1 = TextureIO.newTexture(sb1TexData); sb2TexData = TextureIO.newTextureData(imgSB2, true); VI.skyboxTexSide2 = TextureIO.newTexture(sb2TexData); sb3TexData = TextureIO.newTextureData(imgSB3, true); VI.skyboxTexSide3 = TextureIO.newTexture(sb3TexData); sb4TexData = TextureIO.newTextureData(imgSB4, true); VI.skyboxTexSide4 = TextureIO.newTexture(sb4TexData); sbTopTexData = TextureIO.newTextureData(imgSBTop, true); VI.skyboxTexTop = TextureIO.newTexture(sbTopTexData); sbBottomTexData = TextureIO.newTextureData(imgSBBottom, true); VI.skyboxTexBottom = TextureIO.newTexture(sbBottomTexData); } catch (IOException e) { e.printStackTrace(); } } /** * Retrieve the colors used for categories. * @param i An index denoting the category set. */ public float[][] getCategoryColors(int i) { float[][] color3f = new float[VI.categoryArr[i].cats.length][3]; if (VI.brushingOn) { for (int j = 0; j < VI.categoryArr[i].cats.length; j++) { if (j == VI.brushedCategory) { color3f[j] = VI.brushingPrimaryColor.getColorComponents(color3f[j]); } else { color3f[j] = VI.brushingOffColor.getColorComponents(color3f[j]); } } } else { for (int j = 0; j < VI.categoryArr[i].cats.length; j++) { color3f[j][0] = VI.categoryArr[i].cats[j].color[0]; color3f[j][1] = VI.categoryArr[i].cats[j].color[1]; color3f[j][2] = VI.categoryArr[i].cats[j].color[2]; } } return color3f; } /** * Handles picking (selection) of objects using a color based algorithm. */ public void pick() { if (!pv.transitionStateOn) { drawPickingView(); ByteBuffer pixel = BufferUtil.newByteBuffer(3); pixel.rewind(); int[] viewport = new int[4]; gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0); gl.glReadPixels(camera.mouseX, viewport[3] - camera.mouseY, 1, 1, GL.GL_RGB, GL.GL_BYTE, pixel); for (int i = 0; i < pv.nodeArr.length; i++) { pixel.rewind(); byte[] pix = new byte[3]; pix[0] = pixel.get(); pix[1] = pixel.get(); pix[2] = pixel.get(); if (pv.nodeArr[i].pid[0] == pix[0] && pv.nodeArr[i].pid[1] == pix[1] && pv.nodeArr[i].pid[2] == pix[2] ) { pv.nodeArr[i].highlighted = !pv.nodeArr[i].highlighted; } if (pv.nodeArr[i].label.pid[0] == pix[0] && pv.nodeArr[i].label.pid[1] == pix[1] && pv.nodeArr[i].label.pid[2] == pix[2] ) { pv.nodeArr[i].label.highlighted = !pv.nodeArr[i].label.highlighted; } } } else { // do no picking in the transition state } } /** * Draws the pickable and blocking objects only. In picking colors. */ public void drawPickingView() { gl.glDisable(GL.GL_FOG); gl.glDisable(GL.GL_LIGHTING); gl.glDisable(GL.GL_TEXTURE_2D); if (pv.globeViewOn) { drawGlobe(true); } else if (pv.planeViewOn) { drawPlane(true); } else { // we should never end up here System.out.println("Unreachable error? Renderer.drawPickingView()"); } if (pv.graphNodesOn) { drawNodes(true); } if (pv.labelsOn) { drawLabels(true); } if (pv.graphRelationsOn) { drawRelations(true); } if (pv.barsOn) { // drawCumulativeBars(true); } gl.glEnable(GL.GL_LIGHTING); if (VI.fogOn && (pv.globeViewOn || pv.transitionStateOn)) { gl.glEnable(GL.GL_FOG); } } /** * Called by the OpenGL rendering engine when the window is resized. * Note: the Java implementation of OpenGL does not use this! */ public void displayChanged( GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { // Not used by JOGL! } /** * Called by the OpenGL rendering engine when the scene is reshaped * when the window is resized. */ public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height) { gl = drawable.getGL(); if (height <= 0) { height = 1; } final float h = (float) width / (float) height; gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); glu.gluPerspective(45.0f, h, 0.1, 30.0); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); } }