Java tutorial
/******************************************************************************* * This file is part of the GeOxygene project source files. * * GeOxygene aims at providing an open framework which implements OGC/ISO * specifications for the development and deployment of geographic (GIS) * applications. It is a open source contribution of the COGIT laboratory at the * Institut Gographique National (the French National Mapping Agency). * * See: http://oxygene-project.sourceforge.net * * Copyright (C) 2005 Institut Gographique National * * 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; either version 2.1 of the License, or any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this library (see file LICENSE if present); if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA *******************************************************************************/ package fr.ign.cogit.geoxygene.appli.render.primitive; import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D; import static org.lwjgl.util.glu.GLU.GLU_TESS_BEGIN; import static org.lwjgl.util.glu.GLU.GLU_TESS_COMBINE; import static org.lwjgl.util.glu.GLU.GLU_TESS_END; import static org.lwjgl.util.glu.GLU.GLU_TESS_VERTEX; import static org.lwjgl.util.glu.GLU.gluNewTess; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.text.DecimalFormat; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import javax.vecmath.Point2d; import org.apache.log4j.Logger; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; import org.lwjgl.util.glu.GLUtessellator; import fr.ign.cogit.geoxygene.appli.GeOxygeneEventManager; import fr.ign.cogit.geoxygene.appli.Viewport; import fr.ign.cogit.geoxygene.appli.gl.DensityFieldGenerationTask; import fr.ign.cogit.geoxygene.appli.render.RenderingException; import fr.ign.cogit.geoxygene.appli.task.Task; import fr.ign.cogit.geoxygene.appli.task.TaskListener; import fr.ign.cogit.geoxygene.appli.task.TaskState; import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_Polygon; import fr.ign.cogit.geoxygene.spatial.geomprim.GM_Primitive; import fr.ign.cogit.geoxygene.util.gl.GLTools; import fr.ign.cogit.geoxygene.util.gl.TessCallback; /** * @author JeT * Render drawing primitives by computing density field texture * coordinates. This class generates textures asynchronously */ public class DensityFieldPrimitiveRenderer extends AbstractPrimitiveRenderer implements TaskListener { private static Logger logger = Logger.getLogger(DensityFieldPrimitiveRenderer.class.getName()); // this renderer is added just for pre-visualization during this renderer development. // FIXME: remove it when finished // private final GLPrimitiveRenderer basicRenderer = new GLPrimitiveRenderer(new Color(50, 100, 20), Color.green); private final Map<GM_Primitive, DensityFieldParameterizer> parameterizers = new HashMap<GM_Primitive, DensityFieldParameterizer>(); // map of polygon / asynchronous task for texture generation private final Map<GM_Primitive, DensityFieldGenerationTask> tasks = new HashMap<GM_Primitive, DensityFieldGenerationTask>(); private final Map<GM_Primitive, Integer> textureIds = new HashMap<GM_Primitive, Integer>(); private String sourceTextureFilename = "./src/main/resources/textures/stripes.jpg"; // private BufferedImage sourceTextureImage = null; // texture image of the textureFilename file // image generated by applying texture image to texture coordinates // private final Map<GM_Primitive, BufferedImage> generatedTextureImages = new HashMap<GM_Primitive, BufferedImage>(); private static final DecimalFormat df = new DecimalFormat("#.0000"); private DensityFieldVisualizationType visualizationType = DensityFieldVisualizationType.TEXTURE; /** * constructor */ public DensityFieldPrimitiveRenderer() { } public void invalidateTextures() { this.textureIds.clear(); this.tasks.clear(); } /** * @return the visualizationType */ public DensityFieldVisualizationType getVisualizationType() { return this.visualizationType; } /** * @param visualizationType * the visualizationType to set */ public void setVisualizationType(DensityFieldVisualizationType visualizationType) { this.visualizationType = visualizationType; } /** * @param polygon * polygon for which this method returns the texture id * @param viewport * viewport is only used for the texture generation if it is not * already created * @return the current texture index assigned to the given polygon * */ private Integer getTextureId(ParameterizedPolygon polygon, final Viewport viewport) { if (this.sourceTextureFilename == null) { return null; } Integer textureId = this.textureIds.get(polygon.getGM_Polygon()); if (textureId == null) { DensityFieldGenerationTask task = this.getGeneratedTextureASync(polygon, viewport); // System.err.println("Task parameterizer: " + task.getParameterizer()); BufferedImage generatedTexture = task.getGeneratedTextureImage(); if (generatedTexture == null) { return null; } polygon.generateParameterization(task.getParameterizer()); textureId = GLTools.loadTexture(generatedTexture); this.textureIds.put(polygon.getGM_Polygon(), textureId); } return textureId; } // sync version // private Integer getTextureId(ParameterizedPolygon polygon, final Viewport viewport) { // if (this.sourceTextureFilename == null) { // return null; // } // if (textureId < 0) { // System.err.println("polygon " + polygon.hashCode() + " texture id is not set. Try to get it from generator."); // BufferedImage generatedTexture = this.getGeneratedTextureASync(polygon, viewport); // if (generatedTexture == null) { // System.err.println("polygon " + polygon.hashCode() + " has not yet a generated texture. Still waiting..."); // return textureId; // } // System.err.println("polygon " + polygon.hashCode() + " texture id set. Load it into GL context"); // textureId = GLTools.loadTexture(generatedTexture); // } // return textureId; // } // /** // * get the texture generated by applying a texture file on the parameterized // * image texture // * // * @return the generated image texture filled with the texture // */ // private BufferedImage getGeneratedTextureSync(ParameterizedPolygon polygon, final Viewport viewport) { // BufferedImage generatedTextureImage = this.generatedTextureImages.get(polygon.getGM_Polygon()); // if (generatedTextureImage == null) { // DensityFieldParameterizer parameterizer = this.getParameterizer(polygon, viewport); // generatedTextureImage = TextureImageUtil.applyTexture(parameterizer.getTextureImage(), this.getSourceTextureImage()); // // /* ****************************************************************************** // * SAVE Image for DEBUG purpose only // * TODO: TBDeleted // */ // DecimalFormat df = new DecimalFormat("#.0000"); // try { // ImageIO.write(generatedTextureImage, "png", new File("./z-" + df.format(polygon.getGM_Polygon().area()) + "-gentex-sync")); // } catch (IOException e) { // e.printStackTrace(); // } // // this.generatedTextureImages.put(polygon.getGM_Polygon(), generatedTextureImage); // } // return generatedTextureImage; // } /** * get the texture generated by applying a texture file on the parameterized * image texture * * @return the generated image texture filled with the texture */ private DensityFieldGenerationTask getGeneratedTextureASync(ParameterizedPolygon polygon, final Viewport viewport) { synchronized (this.tasks) { DensityFieldGenerationTask task = this.tasks.get(polygon.getGM_Polygon()); if (task == null) { DensityFieldParameterizer parameterizer = this.getParameterizer(polygon, viewport); task = new DensityFieldGenerationTask("TexGen-pol-" + df.format(polygon.getGM_Polygon().area()), parameterizer, this.getSourceTextureFilename(), this.getVisualizationType()); this.tasks.put(polygon.getGM_Polygon(), task); task.start(); // listen to task to handle end of task task.addTaskListener(this); GeOxygeneEventManager.getInstance().getApplication().getTaskManager().addTask(task); } return task; } } /** * get the parameterizer associated with the given polygon * * @param polygon * polygon to which the parameterizer is associated * @param viewport * used for the parameterizer generation if it has not been * created yet * @return */ private DensityFieldParameterizer getParameterizer(ParameterizedPolygon polygon, final Viewport viewport) { // TODO: [JeT] Those lines should not be here. parameterization should be part of the SLD description // As I don't already have modified the SLD, I just had to find a place where to compute parameterization // comparable lines can be found in GLPrimitiveRenderer but both may be combined in only one once parameterization // will be done elsewhere (described in the SLD ?) DensityFieldParameterizer parameterizer = this.parameterizers.get(polygon.getGM_Polygon()); if (parameterizer == null) { parameterizer = new DensityFieldParameterizer(viewport, polygon); this.parameterizers.put(polygon.getGM_Polygon(), parameterizer); } return parameterizer; } // /** // * @return the image read from textureImageFilename file // */ // private BufferedImage getSourceTextureImage() { // if (this.sourceTextureImage == null) { // try { // this.sourceTextureImage = ImageIO.read(new File(this.getSourceTextureFilename())); // } catch (IOException e) { // logger.error("Cannot load texture image file " + this.getSourceTextureFilename()); // e.printStackTrace(); // } // } // // return this.sourceTextureImage; // } /** * @return the textureFilename */ public String getSourceTextureFilename() { return this.sourceTextureFilename; } /** * @param textureFilename * the textureFilename to set */ public void setTextureFilename(final String textureFilename) { this.sourceTextureFilename = textureFilename; this.textureIds.clear(); } @Override public void initializeRendering() throws RenderingException { // this.basicRenderer.initializeRendering(); } @Override public void render() throws RenderingException { if (this.getViewport() == null) { throw new RenderingException("viewport is not set"); } // // render using basic gl renderer // this.basicRenderer.setPrimitives(this.getPrimitives()); // this.basicRenderer.setViewport(this.getViewport()); // this.basicRenderer.render(); // render using this renderer for (DrawingPrimitive primitive : this.getPrimitives()) { this.render(primitive, this.getViewport()); } } /** * Render one drawing primitive * * @param primitive * @throws RenderingException */ private void render(final DrawingPrimitive primitive, final Viewport viewport) throws RenderingException { if (!primitive.isLeaf()) { MultiDrawingPrimitive multiPrimitive = (MultiDrawingPrimitive) primitive; for (DrawingPrimitive childPrimitive : multiPrimitive.getPrimitives()) { // System.err.println("render child primitive #" + index + " " + childPrimitive.getClass().getSimpleName()); this.render(childPrimitive, viewport); } return; } else if (primitive instanceof ParameterizedPolyline) { logger.warn(this.getClass().getSimpleName() + " does not handle lines"); return; } else if (primitive instanceof ParameterizedPolygon) { ParameterizedPolygon polygon = (ParameterizedPolygon) primitive; this.renderSurface(viewport, polygon, this.getTextureId(polygon, viewport)); return; } logger.warn(this.getClass().getSimpleName() + " do not know how to paint primitives " + primitive.getClass().getSimpleName()); } /** * Rendering of polygons * * @param viewport * @param polygon */ private void renderSurface(Viewport viewport, ParameterizedPolygon polygon, Integer texIndex) { boolean texCoord = polygon.hasTextureCoordinates() && texIndex != null; // System.err.println("pol " + polygon.hashCode() + " " + polygon.hasTextureCoordinates() + " " + texIndex); if (texCoord) { GL11.glColor4f(1.f, 1.f, 1.f, 1.f); GL11.glEnable(GL_TEXTURE_2D); GL11.glBindTexture(GL_TEXTURE_2D, texIndex); } else { GLTools.glColor(GLTools.glRandomColor()); GL11.glDisable(GL_TEXTURE_2D); } // tesselation GLUtessellator tesselator = gluNewTess(); // Set callback functions TessCallback callback = new TessCallback(); tesselator.gluTessCallback(GLU_TESS_VERTEX, callback); tesselator.gluTessCallback(GLU_TESS_BEGIN, callback); tesselator.gluTessCallback(GLU_TESS_END, callback); tesselator.gluTessCallback(GLU_TESS_COMBINE, callback); tesselator.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_POSITIVE); // System.err.println("DensityFieldPrimitiveRenderer : outer frontier points count = " + polygon.getOuterFrontier().size()); tesselator.gluTessBeginPolygon(null); // outer frontier tesselator.gluTessBeginContour(); for (int outerFrontierPointIndex = 0; outerFrontierPointIndex < polygon.getOuterFrontier() .size(); outerFrontierPointIndex++) { Point2d outerFrontierPoint = polygon.getOuterFrontier().get(outerFrontierPointIndex); // screen coordinates // point coordinates double coords[] = new double[6]; coords[0] = outerFrontierPoint.x; coords[1] = outerFrontierPoint.y; coords[2] = 0; // use internal texture coordinates if (texCoord) { Point2d outerFrontierTextureCoordinates = polygon .getOuterFrontierTextureCoordinates(outerFrontierPointIndex); coords[3] = outerFrontierTextureCoordinates.x; coords[4] = outerFrontierTextureCoordinates.y; coords[5] = 0; } else { coords[3] = 0; coords[4] = 0; coords[5] = 0; } // System.err.println("------------------------------------------------ " + outerFrontierTextureCoordinates); tesselator.gluTessVertex(coords, 0, coords); } tesselator.gluTessEndContour(); for (int innerFrontierIndex = 0; innerFrontierIndex < polygon .getInnerFrontierCount(); innerFrontierIndex++) { List<Point2d> innerFrontier = polygon.getInnerFrontier(innerFrontierIndex); tesselator.gluTessBeginContour(); for (int innerFrontierPointIndex = 0; innerFrontierPointIndex < innerFrontier .size(); innerFrontierPointIndex++) { Point2d innerFrontierPoint = innerFrontier.get(innerFrontierPointIndex); double[] coords = new double[6]; // point coordinates coords[0] = innerFrontierPoint.x; coords[1] = innerFrontierPoint.y; coords[2] = 0; // use internal texture coordinates if (texCoord) { Point2d innerFrontierTextureCoordinates = polygon .getInnerFrontierTextureCoordinates(innerFrontierIndex, innerFrontierPointIndex); coords[3] = innerFrontierTextureCoordinates.x; coords[4] = innerFrontierTextureCoordinates.y; coords[5] = 0; } else { coords[3] = 0; coords[4] = 0; coords[5] = 0; } // System.err.println(innerFrontierPoint.x + ";" + innerFrontierTextureCoordinates.x + ";;" + innerFrontierPoint // + innerFrontierTextureCoordinates.y); // coords[3] = texCoord ? innerFrontierTextureCoordinates.x / 1000. : innerFrontierPoint.x; // coords[4] = texCoord ? innerFrontierTextureCoordinates.y / 1000. : innerFrontierPoint.y; coords[5] = 0; tesselator.gluTessVertex(coords, 0, coords); } tesselator.gluTessEndContour(); } tesselator.gluTessEndPolygon(); } @Override public void finalizeRendering() throws RenderingException { // this.basicRenderer.finalizeRendering(); } /** * Actions performed when a task is finished or error terminated */ @Override public void onStateChange(Task task, TaskState oldState) { if (task.getState() == TaskState.FINISHED) { DensityFieldGenerationTask densityFieldTask = (DensityFieldGenerationTask) task; GM_Polygon gmPolygon = densityFieldTask.getParameterizer().getPolygon().getGM_Polygon(); BufferedImage generatedTextureImage = densityFieldTask.getGeneratedTextureImage(); /* ****************************************************************************** * SAVE Image for DEBUG purpose only * TODO: TBDeleted */ DecimalFormat df = new DecimalFormat("#.0000"); try { ImageIO.write(generatedTextureImage, "png", new File("./z-" + df.format(gmPolygon.area()) + "-gentex-async.png")); } catch (IOException e) { e.printStackTrace(); } GeOxygeneEventManager.getInstance().getApplication().getMainFrame().getGui().repaint(); // // store generated texture in cache map // synchronized (generatedTextureImage) { // this.generatedTextureImages.put(gmPolygon, generatedTextureImage); // } // FIXME: the task as to be removed from the listener list, but not in the listener callback ! task.removeTaskListener(this); } else if (task.getState() == TaskState.ERROR) { logger.error("ASynchronous texture generation finished with an error"); logger.error(task.getError().getMessage()); task.removeTaskListener(this); } } }