fr.ign.cogit.geoxygene.appli.render.primitive.DensityFieldPrimitiveRenderer.java Source code

Java tutorial

Introduction

Here is the source code for fr.ign.cogit.geoxygene.appli.render.primitive.DensityFieldPrimitiveRenderer.java

Source

/*******************************************************************************
 * 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);
        }

    }
}