/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotoolkit.display3d; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.opengl.glu.GLU; import javax.vecmath.Vector3d; import java.util.*; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.vecmath.Vector3f; import org.apache.sis.util.logging.Logging; import org.geotoolkit.display.canvas.AbstractCanvas; import org.geotoolkit.display.primitive.SceneNode; import org.geotoolkit.display3d.phase.Phase; import org.geotoolkit.display3d.scene.ContextContainer3D; import org.geotoolkit.display3d.scene.Scene3D; import org.geotoolkit.display3d.scene.SceneNode3D; import org.geotoolkit.display3d.scene.Updater; import org.geotoolkit.display3d.scene.camera.TrackBallCamera; import org.geotoolkit.display3d.scene.light.Light; import org.geotoolkit.display3d.scene.light.LightsManager; import org.geotoolkit.factory.Hints; import org.geotoolkit.math.XMath; /** * Custom GLEventListener * * @author Thomas Rouby (Geomatys) */ public class Map3D extends AbstractCanvas<Scene3D> implements GLEventListener { public static final Logger LOGGER = Logging.getLogger("org.geotoolkit.display3d"); /** * Keep it, we might need it for dispose. */ private GLAutoDrawable drawable; private final List<Phase> phases = new ArrayList<>(); private final TrackBallCamera camera; private final LightsManager lights = new LightsManager(); private float rotation = 0.0f; private float azimut = 45.0f; //COUNTER to reduce number of OpenGL actions made. private int action = 0; private final int MAX_ACTION = 2; public Map3D(){ super(new Hints()); setContainer(new ContextContainer3D(this)); this.camera = new TrackBallCamera(this, 0.0f, 0.0f, 1000.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); final Light light0 = lights.addLight(0, null); if (light0 != null) { light0.setPosition(new Vector3f(0.0f, 0.0f, 1.0f), true); light0.setAmbient(new float[]{0.2f, 0.2f, 0.2f, 1.0f}); light0.setDiffuse(new float[]{1.0f, 1.0f, 1.0f, 1.0f}); light0.setSpecular(new float[]{1.0f, 1.0f, 1.0f, 1.0f}); } } public boolean doAction(){ if (action<MAX_ACTION){ return true; } return false; } public void addAction(){ action++; } /** * TODO, should not be here. */ public GLAutoDrawable getDrawable() { return drawable; } public LightsManager getLightManager() { return this.lights; } @Override public void init(GLAutoDrawable gLDrawable){ drawable = gLDrawable; try { final GL2 gl2 = gLDrawable.getGL().getGL2(); gl2.glEnable(GL.GL_DEPTH_TEST); gl2.glEnable(GL.GL_CULL_FACE); gl2.glEnable(GL2.GL_NORMALIZE); gl2.glClearColor(0.3f, 0.3f, 0.3f, 1.0f); gl2.glDepthFunc(GL.GL_LEQUAL); gl2.glClearDepth(Float.MAX_VALUE); gl2.glCullFace(GL.GL_BACK); } catch (Exception ex) { getLogger().log(Level.WARNING, ex.getMessage(), ex); } } @Override public void display(GLAutoDrawable glDrawable) { this.action = 0; final GL2 gl2 = glDrawable.getGL().getGL2(); //update camera gl2.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl2.glLoadIdentity(); final GLU glu = new GLU(); final Vector3f eye = camera.getEye(); final Vector3f center = camera.getCenter(); glu.gluPerspective(camera.getFovy(), camera.getAspect(), camera.getNear(), (camera.getLength() + 6378000.0f)); glu.gluLookAt(eye.x, eye.y, eye.z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); gl2.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); gl2.glLoadIdentity(); //clean the canvas gl2.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Move the scene (camera management) gl2.glRotated((double) camera.getRotateX(), 1.0, 0.0, 0.0); gl2.glRotated((double) camera.getRotateZ(), 0.0, 0.0, 1.0); final Vector3d scale3d = camera.getScale3d(); gl2.glScaled(scale3d.x / 100.0, scale3d.y / 100.0, scale3d.z / 100.0); gl2.glTranslatef(-center.x, -center.y, -center.z); //loop on all scene nodes for rendering final SceneNode root = getContainer().getRoot(); display(root, glDrawable); // light management if (!lights.isEmpty() && lights.isEnable()) { gl2.glEnable(GL2.GL_LIGHTING); gl2.glPushMatrix(); gl2.glRotatef(rotation, 0.0f, 0.0f, 1.0f); gl2.glRotatef(90.0f-azimut, 1.0f, 0.0f, 0.0f); lights.updateMaterial(glDrawable); lights.updateLight(glDrawable); gl2.glPopMatrix(); } else { gl2.glDisable(GL2.GL_LIGHTING); } gl2.glFlush(); // update other phases for (Phase phase : phases.toArray(new Phase[0])) { phase.update(gl2); } } /** * Loop on scene nodes and render. */ private void display(SceneNode node, GLAutoDrawable glDrawable){ if(node instanceof SceneNode3D){ final SceneNode3D n3d = (SceneNode3D) node; n3d.init(glDrawable); //won't do anythis if already initialized final Updater updater = n3d.getUpdater(); if(updater != null) updater.update(glDrawable); n3d.draw(glDrawable); } //explore childrens, defensive copy to reduce concurency modifications for(Object child : node.getChildren().toArray()){ display((SceneNode)child, glDrawable); } } @Override public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, int height) { try { if (height <= 0) { height = 1; } this.getCamera().setWidth(width); this.getCamera().setHeight(height); } catch (Exception ex) { getLogger().log(Level.WARNING, ex.getMessage(), ex); } } @Override public synchronized void dispose() { dispose(drawable); } @Override public void dispose(GLAutoDrawable drawable) { getContainer().dispose(); } public int addPhase(Phase phase) { if (phase == null){ return -1; } int index = phases.indexOf(phase); if(index < 0){ phase.setMap(this); index = this.phases.size(); this.phases.add(phase); } return index; } public boolean removePhase(Phase phase) { if (phase == null) { return false; } phase.setMap(null); return this.phases.remove(phase); } public float getLightRotation() { return rotation; } public void setLightRotation(float rotation) { if (rotation < 0.0f) { this.rotation = 360.0f - rotation%360.0f; } else { this.rotation = rotation%360.0f; } } public float getLightAzimut() { return azimut; } public void setLightAzimut(float azimut) { this.azimut = XMath.clamp(azimut, 0.0f, 90.0f); } public TrackBallCamera getCamera() { return this.camera; } public double getDistForScale(double scale) { final double a = scale*camera.getWidth()*(camera.getScale3d().x/100.0); final double fov = camera.getFovy()*camera.getAspect(); final double dist = (a/2.0) / Math.toRadians(fov / 2.0); return dist; } }