Package org.osm2world.core.target.jogl

Source Code of org.osm2world.core.target.jogl.JOGLTarget

package org.osm2world.core.target.jogl;

import static java.util.Arrays.asList;
import static javax.media.opengl.GL.*;
import static javax.media.opengl.GL2.*;
import static javax.media.opengl.GL2ES1.*;
import static javax.media.opengl.GL2GL3.*;
import static javax.media.opengl.fixedfunc.GLLightingFunc.*;
import static javax.media.opengl.fixedfunc.GLMatrixFunc.*;
import static org.osm2world.core.target.common.material.Material.multiplyColor;
import static org.osm2world.core.target.common.material.Material.Transparency.*;
import static org.osm2world.core.target.jogl.NonAreaPrimitive.Type.*;

import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.osm2world.core.math.Vector3D;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.common.Primitive;
import org.osm2world.core.target.common.Primitive.Type;
import org.osm2world.core.target.common.PrimitiveTarget;
import org.osm2world.core.target.common.TextureData;
import org.osm2world.core.target.common.TextureData.Wrap;
import org.osm2world.core.target.common.lighting.GlobalLightingParameters;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.material.Material.Lighting;
import org.osm2world.core.target.common.rendering.Camera;
import org.osm2world.core.target.common.rendering.Projection;

import com.jogamp.opengl.util.awt.TextRenderer;
import com.jogamp.opengl.util.texture.Texture;

public final class JOGLTarget extends PrimitiveTarget<RenderableToJOGL> {
 
  /** maximum number of texture layers any material can use */
  public static final int MAX_TEXTURE_LAYERS = 4;
 
  /** globally controls anisotropic filtering for all textures */
  private static final boolean ANISOTROPIC_FILTERING = true;
 
  private final GL2 gl;
 
  private final JOGLTextureManager textureManager;
 
  private List<NonAreaPrimitive> nonAreaPrimitives;
  private PrimitiveBuffer primitiveBuffer;
  private JOGLRenderer renderer;

  private JOGLRenderingParameters renderingParameters;
  private GlobalLightingParameters globalLightingParameters;
 
  private Configuration config = new BaseConfiguration();
 
  /**
   * creates a new JOGLTarget for a given {@link GL2} interface. It is
   * possible to have multiple targets that render to the same gl object.
   *
   * @param renderingParameters  global parameters for rendering;
   *   see {@link #setRenderingParameters(JOGLRenderingParameters)}
   * @param globalLightingParameters  global parameters for lighting;
   *   see {@link #setGlobalLightingParameters(GlobalLightingParameters)}
   */
  public JOGLTarget(GL2 gl, JOGLRenderingParameters renderingParameters,
      GlobalLightingParameters globalLightingParameters) {
   
    this.gl = gl;
    this.renderingParameters = renderingParameters;
    this.globalLightingParameters = globalLightingParameters;
   
    this.textureManager = new JOGLTextureManager(gl);
   
    reset();
   
  }
 
  @Override
  public Class<RenderableToJOGL> getRenderableType() {
    return RenderableToJOGL.class;
  }
   
  /**
   * discards all accumulated draw calls
   */
  public void reset() {
   
    this.nonAreaPrimitives = new ArrayList<NonAreaPrimitive>();
   
    this.primitiveBuffer = new PrimitiveBuffer();
   
    if (renderer != null) {
      renderer.freeResources();
      renderer = null;
    }
   
  }
 
  @Override
  public void render(RenderableToJOGL renderable) {
    renderable.renderTo(this);
  }

  @Override
  protected void drawPrimitive(Primitive.Type type, Material material,
      List<VectorXYZ> vertices, List<VectorXYZ> normals,
      List<List<VectorXZ>> texCoordLists) {
   
    primitiveBuffer.drawPrimitive(type, material, vertices, normals, texCoordLists);
   
  }

  public static void drawPrimitive(GL2 gl, int glPrimitiveType,
      List<VectorXYZ> vertices, List<VectorXYZ> normals,
      List<List<VectorXZ>> texCoordLists) {
   
    assert vertices.size() == normals.size();
   
    gl.glBegin(glPrimitiveType);
   
    for (int i = 0; i < vertices.size(); i++) {
     
      if (texCoordLists != null) {
        for (int texLayer = 0; texLayer < texCoordLists.size(); texLayer++) {
          VectorXZ textureCoord =  texCoordLists.get(texLayer).get(i);
          gl.glMultiTexCoord2d(getGLTextureConstant(texLayer),
              textureCoord.x, textureCoord.z);
        }
      }
     
      VectorXYZ n = normals.get(i);
      gl.glNormal3d(n.x, n.y,  -n.z);
     
      VectorXYZ v = vertices.get(i);
      gl.glVertex3d(v.x, v.y, -v.z);
     
    }
   
    gl.glEnd();
   
  }
 
  private void drawNonAreaPrimitive(NonAreaPrimitive.Type type,
      Color color, int width, List<VectorXYZ> vs) {
   
    nonAreaPrimitives.add(new NonAreaPrimitive(
        type, color, width, vs));
       
  }
 
  public void drawPoints(Color color, VectorXYZ... vs) {
    drawNonAreaPrimitive(POINTS, color, 1, asList(vs));
  }
 
  public void drawLineStrip(Color color, int width, VectorXYZ... vs) {
    drawNonAreaPrimitive(LINE_STRIP, color, width, asList(vs));
  }
 
  public void drawLineStrip(Color color, int width, List<VectorXYZ> vs) {
    drawNonAreaPrimitive(LINE_STRIP, color, width, vs);
  }
 
  public void drawLineLoop(Color color, int width, List<VectorXYZ> vs) {
    drawNonAreaPrimitive(LINE_LOOP, color, width, vs);
  }
 
  /**
   * set global lighting parameters. Using this method affects all primitives
   * (even those from previous draw calls).
   *
   * @param parameters  parameter object; null disables lighting
   */
  public void setGlobalLightingParameters(
      GlobalLightingParameters parameters) {
   
    this.globalLightingParameters = parameters;
   
  }
 
  /**
   * set global rendering parameters. Using this method affects all primitives
   * (even those from previous draw calls).
   */
  public void setRenderingParameters(
      JOGLRenderingParameters renderingParameters) {
   
    this.renderingParameters = renderingParameters;
   
  }
 
  public void setConfiguration(Configuration config) {
    this.config = config;
  }
 
  /**
   * prepares a scene, based on the accumulated draw calls, for rendering.
   */
  @Override
  public void finish() {
   
    if (isFinished()) return;
   
    if ("DisplayList".equals(config.getString("joglImplementation"))) {
      renderer = new JOGLRendererDisplayList(
          gl, textureManager, primitiveBuffer);
    } else {
      renderer = new JOGLRendererVBO(
          gl, textureManager, primitiveBuffer);
    }
   
  }
 
  public boolean isFinished() {
    return renderer != null;
  }
 
  public void render(Camera camera, Projection projection) {
    renderPart(camera, projection, 0, 1, 0, 1);
  }
 
  /**
   * similar to {@link #render(Camera, Projection)},
   * but allows rendering only a part of the "normal" image.
   * For example, with xStart=0, xEnd=0.5, yStart=0 and yEnd=1,
   * only the left half of the full image will be rendered,
   * but it will be stretched to cover the available space.
   *
   * Only supported for orthographic projections!
   */
  public void renderPart(Camera camera, Projection projection,
      double xStart, double xEnd, double yStart, double yEnd) {
   
    if (renderer == null) {
      throw new IllegalStateException("finish must be called first");
    }
   
    /* apply camera and projection information */
   
    applyProjectionMatricesForPart(gl, projection,
        xStart, xEnd, yStart, yEnd);
   
    applyCameraMatrices(gl, camera);
   
    /* apply global rendering parameters */
   
    applyRenderingParameters(gl, renderingParameters);
    applyLightingParameters(gl, globalLightingParameters);
   
    /* render primitives */
   
    renderer.render(camera, projection);
   
    for (NonAreaPrimitive nonAreaPrimitive : nonAreaPrimitives) {
     
      gl.glLineWidth(nonAreaPrimitive.width);
     
      Color c = nonAreaPrimitive.color;
      gl.glColor3f(c.getRed()/255f, c.getGreen()/255f, c.getBlue()/255f);
     
      gl.glBegin(getGLConstant(nonAreaPrimitive.type));
         
      for (VectorXYZ v : nonAreaPrimitive.vs) {
            gl.glVertex3d(v.getX(), v.getY(), -v.getZ());
      }
     
          gl.glEnd();
     
    }
   
  }
 
  public void freeResources() {
   
    textureManager.releaseAll();
   
    reset();
   
  }
 
  static final void applyCameraMatrices(GL2 gl, Camera camera) {
   
      gl.glLoadIdentity();
   
    VectorXYZ pos = camera.getPos();
    VectorXYZ lookAt = camera.getLookAt();
    VectorXYZ up = camera.getUp();
    new GLU().gluLookAt(
        pos.x, pos.y, -pos.z,
        lookAt.x, lookAt.y, -lookAt.z,
        up.x, up.y, -up.z);
   
  }
 
  static final void applyProjectionMatrices(GL2 gl, Projection projection) {
    applyProjectionMatricesForPart(gl, projection, 0, 1, 0, 1);
  }

  /**
   * similar to {@link #applyProjectionMatrices(GL2, Projection)},
   * but allows rendering only a part of the "normal" image.
   */
  static final void applyProjectionMatricesForPart(GL2 gl, Projection projection,
      double xStart, double xEnd, double yStart, double yEnd) {
   
    if ((xStart != 0 || xEnd != 1 || yStart != 0 || yEnd != 1)
        && !projection.isOrthographic()) {
      throw new IllegalArgumentException("section rendering only supported "
          + "for orthographic projections");
    }
   
    gl.glMatrixMode(GL_PROJECTION);
    gl.glLoadIdentity();
   
    if (projection.isOrthographic()) {

      double volumeWidth = projection.getAspectRatio() * projection.getVolumeHeight();
     
      gl.glOrtho(
          (-0.5 + xStart) * volumeWidth,
          (-0.5 + xEnd  ) * volumeWidth,
          (-0.5 + yStart) * projection.getVolumeHeight(),
          (-0.5 + yEnd  ) * projection.getVolumeHeight(),
          projection.getNearClippingDistance(),
          projection.getFarClippingDistance());
     
    } else { //perspective

      new GLU().gluPerspective(
          projection.getVertAngle(),
          projection.getAspectRatio(),
          projection.getNearClippingDistance(),
          projection.getFarClippingDistance());
     
    }

    gl.glMatrixMode(GL_MODELVIEW);
   
  }
 
  static final void applyRenderingParameters(GL2 gl,
      JOGLRenderingParameters parameters) {
   
    /* backface culling */
   
    if (parameters.frontFace == null) {
      gl.glDisable(GL_CULL_FACE);
    } else {
      gl.glFrontFace(GL_CCW);
      gl.glCullFace(GL_BACK);
      gl.glEnable (GL_CULL_FACE);
    }
   
    /* wireframe mode */
   
    if (parameters.wireframe) {
        gl.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      } else {
        gl.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      }
   
    /* z buffer */
   
    if (parameters.useZBuffer) {
      gl.glEnable(GL_DEPTH_TEST);
    } else {
      gl.glDisable(GL_DEPTH_TEST);
    }
       
  }
 
  static final void applyLightingParameters(GL2 gl,
      GlobalLightingParameters lighting) {
   
    if (lighting == null) {

      gl.glDisable(GL_LIGHT0);
      gl.glDisable(GL_LIGHTING);
     
    } else {
     
      gl.glLightModelfv(GL_LIGHT_MODEL_AMBIENT,
          getFloatBuffer(lighting.globalAmbientColor));
     
      gl.glLightfv(GL_LIGHT0, GL_AMBIENT,
          getFloatBuffer(Color.BLACK));
      gl.glLightfv(GL_LIGHT0, GL_DIFFUSE,
          getFloatBuffer(lighting.lightColorDiffuse));
      gl.glLightfv(GL_LIGHT0, GL_SPECULAR,
          getFloatBuffer(lighting.lightColorSpecular));
     
      gl.glLightfv(GL_LIGHT0, GL_POSITION, new float[] {
            (float)lighting.lightFromDirection.x,
            (float)lighting.lightFromDirection.y,
            -(float)lighting.lightFromDirection.z,
            0.0f}, 0);
     
      gl.glEnable(GL_LIGHT0);
      gl.glEnable(GL_LIGHTING);
     
    }
   
  }
 
  static final void setMaterial(GL2 gl, Material material,
      JOGLTextureManager textureManager) {
   
    int numTexLayers = 0;
    if (material.getTextureDataList() != null) {
      numTexLayers = material.getTextureDataList().size();
    }
   
    /* set lighting */
   
    if (material.getLighting() == Lighting.SMOOTH) {
      gl.glShadeModel(GL_SMOOTH);
    } else {
      gl.glShadeModel(GL_FLAT);
    }
   
    /* set color */
   
    if (numTexLayers == 0 || material.getTextureDataList().get(0).colorable) {
     
      //TODO: glMaterialfv could be redundant if color was used for ambient and diffuse
      Color c = material.getColor();
      gl.glColor3f(c.getRed()/255f, c.getGreen()/255f, c.getBlue());
     
      gl.glMaterialfv(GL_FRONT, GL_AMBIENT,
          getFloatBuffer(material.ambientColor()));
      gl.glMaterialfv(GL_FRONT, GL_DIFFUSE,
          getFloatBuffer(material.diffuseColor()));
     
    } else {
     
      gl.glColor3f(1, 1, 1);
     
      gl.glMaterialfv(GL_FRONT, GL_AMBIENT, getFloatBuffer(
          multiplyColor(Color.WHITE, material.getAmbientFactor())));
      gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, getFloatBuffer(
          multiplyColor(Color.WHITE, material.getDiffuseFactor())));
     
    }
   
    /* set textures and associated parameters */
   
    for (int i = 0; i < MAX_TEXTURE_LAYERS; i++) {
     
      gl.glActiveTexture(getGLTextureConstant(i));
           
      if (i >= numTexLayers) {
       
        gl.glDisable(GL_TEXTURE_2D);
               
      } else {
       
        gl.glEnable(GL_TEXTURE_2D);
       
        if (material.getTransparency() == TRUE) {
          gl.glEnable(GL.GL_BLEND);
          gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
        } else if (material.getTransparency() == BINARY) {
          gl.glAlphaFunc(GL_GREATER, 0.5f);
          gl.glEnable(GL_ALPHA_TEST);
        } else {
          gl.glDisable(GL.GL_BLEND);
          gl.glDisable(GL_ALPHA_TEST);
        }
       
        TextureData textureData = material.getTextureDataList().get(i);
       
        Texture texture = textureManager.getTextureForFile(textureData.file);
            texture.enable(gl); //TODO: should this be called every time?
            texture.bind(gl);
           
        /* enable anisotropic filtering (note: this could be a
         * per-texture setting, but currently isn't) */
       
            if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) {
         
              if (ANISOTROPIC_FILTERING) {
           
            float max[] = new float[1];
            gl.glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max, 0);
           
            gl.glTexParameterf(GL_TEXTURE_2D,
                GL_TEXTURE_MAX_ANISOTROPY_EXT,
                max[0]);
           
          } else {
           
            gl.glTexParameterf(GL_TEXTURE_2D,
                GL_TEXTURE_MAX_ANISOTROPY_EXT,
                1.0f);
           
          }
         
            }
       
        /* wrapping behavior */
           
        int wrap = 0;
       
        switch (textureData.wrap) {
        case CLAMP: wrap = GL_CLAMP; break;
        case REPEAT: wrap = GL_REPEAT; break;
        case CLAMP_TO_BORDER: wrap = GL_CLAMP_TO_BORDER; break;
        }
       
        gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
            gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
           
           
            if (textureData.wrap == Wrap.CLAMP_TO_BORDER) {
             
              /* TODO: make the RGB configurable -  for some reason,
               * it shows up in lowzoom even if fully transparent */
              gl.glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
                  getFloatBuffer(new Color(1f, 1f, 1f, 0f)));
             
            }
       
            /* combination of texture layers */
           
            if (i == 0) {
              gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            } else {
             
              gl.glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
              gl.glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );

              gl.glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
              gl.glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );

              gl.glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
              gl.glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );

              gl.glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_TEXTURE );
              gl.glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA );

            }
           
      }
     
    }
   
  }

  static final FloatBuffer getFloatBuffer(Color color) {
    float colorArray[] = {0, 0, 0, color.getAlpha() / 255f};
    color.getRGBColorComponents(colorArray);
    return FloatBuffer.wrap(colorArray);
  }
 
  static final int getGLConstant(Type type) {
    switch (type) {
    case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
    case TRIANGLE_FAN: return GL_TRIANGLE_FAN;
    case TRIANGLES: return GL_TRIANGLES;
    case CONVEX_POLYGON: return GL_POLYGON;
    default: throw new Error("programming error: unhandled primitive type");
    }
  }

  static final int getGLConstant(NonAreaPrimitive.Type type) {
    switch (type) {
    case POINTS: return GL_POINTS;
    case LINES: return GL_LINES;
    case LINE_STRIP: return GL_LINE_STRIP;
    case LINE_LOOP: return GL_LINE_LOOP;
    default: throw new Error("programming error: unhandled primitive type");
    }
  }

  static final int getGLTextureConstant(int textureNumber) {
    switch (textureNumber) {
    case 0: return GL_TEXTURE0;
    case 1: return GL_TEXTURE1;
    case 2: return GL_TEXTURE2;
    case 3: return GL_TEXTURE3;
    default: throw new Error("programming error: unhandled texture number");
    }
  }
 
  /**
   * clears the rendering surface and the z buffer
   *
   * @param clearColor  background color before rendering any primitives;
   *                     null uses a previously defined clear color
   */
  public static final void clearGL(GL2 gl, Color clearColor) {
   
    if (clearColor != null) {
      float[] c = {0f, 0f, 0f};
      clearColor.getColorComponents(c);
      gl.glClearColor(c[0], c[1], c[2], 1.0f);
    }
   
    gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
       
  }
 
  private static final TextRenderer textRenderer = new TextRenderer(
      new Font("SansSerif", Font.PLAIN, 12), true, false);
  //needs quite a bit of memory, so it must not create an instance for each use!
 
  public static final void drawText(String string, Vector3D pos, Color color) {
    textRenderer.setColor(color);
    textRenderer.begin3DRendering();
    textRenderer.draw3D(string,
        (float)pos.getX(), (float)pos.getY(), -(float)pos.getZ(),
        0.05f);
  }

  public static final void drawText(String string, int x, int y,
      int screenWidth, int screenHeight, Color color) {
    textRenderer.beginRendering(screenWidth, screenHeight);
    textRenderer.setColor(color);
    textRenderer.draw(string, x, y);
    textRenderer.endRendering();
  }
 
  public static final void drawBackgoundImage(GL2 gl, File backgroundImage,
      int startPixelX, int startPixelY,
      int pixelWidth, int pixelHeight,
      JOGLTextureManager textureManager) {
   
    gl.glMatrixMode(GL_PROJECTION);
    gl.glPushMatrix();
    gl.glLoadIdentity();
    gl.glOrtho(0, 1, 0, 1, 0, 1);
   
    gl.glMatrixMode(GL_MODELVIEW);
    gl.glPushMatrix();
    gl.glLoadIdentity();
       
    gl.glDepthMask( false );
   
    /* texture binding */

    gl.glEnable(GL_TEXTURE_2D);
    gl.glActiveTexture(GL_TEXTURE0);
   
    gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   
    Texture backgroundTexture =
        textureManager.getTextureForFile(backgroundImage);

    backgroundTexture.enable(gl);
    backgroundTexture.bind(gl);
   
    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   
    int texWidth = backgroundTexture.getImageWidth();
    int texHeight = backgroundTexture.getImageHeight();
   
    /* draw quad */
       
    gl.glBegin( GL_QUADS ); {
      gl.glTexCoord2f(
          (float) startPixelX / texWidth,
          (float) startPixelY / texHeight );
      gl.glVertex2f( 0, 0 );
      gl.glTexCoord2f(
          (float) (startPixelX + pixelWidth) / texWidth,
          (float) startPixelY / texHeight );
      gl.glVertex2f( 1f, 0 );
      gl.glTexCoord2f(
          (float) (startPixelX + pixelWidth) / texWidth,
          (float) (startPixelY + pixelHeight) / texHeight );
      gl.glVertex2f( 1f, 1f );
      gl.glTexCoord2f(
          (float) startPixelX / texWidth,
          (float) (startPixelY + pixelHeight) / texHeight );
      gl.glVertex2f( 0, 1f );
    } gl.glEnd();
   
    /* restore some settings */
   
    gl.glDepthMask( true );
   
    gl.glPopMatrix();
    gl.glMatrixMode(GL_PROJECTION);
    gl.glPopMatrix();
    gl.glMatrixMode(GL_MODELVIEW);
   
  }
 
}
TOP

Related Classes of org.osm2world.core.target.jogl.JOGLTarget

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.