Package eas.plugins.standard.visualization.visualization3D

Source Code of eas.plugins.standard.visualization.visualization3D.VideoPluginLWJGL

/*
* File name:        VideoPluginODE3D.java (package eas.simulation.spatial.sim3D.physicalSimulation.standardPlugins)
* Author(s):        Fabian
* Java version:     6.0
* Generation date:  26.01.2012 (10:56:13)
*
*
* THIS CLASS USES PARTS OF ODE4J:
* ode4j : Physics library.

Copyright  (c) 2009 Tilmann Zäschke.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.
     
* Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.
     
* Neither the name of the ode4j nor the names of its contributors may be used
      to endorse or promote products derived from this software without specific prior
      written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.*
*
*
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
*   author or licensor (but not in any way that suggests that they endorse
*   you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
*   distribute the resulting work only under the same or a similar license to
*   this one.
*
* + Detailed license conditions (Germany):
*   http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
*   http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/

package eas.plugins.standard.visualization.visualization3D;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
import org.ode4j.math.DMatrix3C;
import org.ode4j.math.DVector3;
import org.ode4j.math.DVector3C;
import org.ode4j.ode.DBody;
import org.ode4j.ode.DBox;
import org.ode4j.ode.DCapsule;
import org.ode4j.ode.DCylinder;
import org.ode4j.ode.DGeom;
import org.ode4j.ode.DSphere;
import org.ode4j.ode.OdeMath;
import org.ode4j.ode.OdeMath.OP;

import eas.plugins.AbstractDefaultPlugin;
import eas.simulation.Wink;
import eas.simulation.event.EASEvent;
import eas.simulation.spatial.sim3D.physicalSimulation.standardAgents.AbstractAgentLWJGLdrawable;
import eas.simulation.spatial.sim3D.physicalSimulation.standardEnvironments.AbstractEnvironmentLWJGLdrawable;
import eas.startSetup.ParCollection;
import eas.startSetup.SingleParameter;
import eas.startSetup.parameterDatatypes.Datatypes;

/**
* Simple graphics plugin. Uses LWJGL to show the environment. Best used with
* ode4j. Some code is by Tilmann Zäschke who wrote ode4j.
*/
public class VideoPluginLWJGL extends
    AbstractDefaultPlugin<AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>>> {

    private static final long serialVersionUID = 2182775492914135478L;

    // The environment to be drawn.
  private static AbstractEnvironmentLWJGLdrawable<?> environment;

  // Store state of Pbuffer
  private static Pbuffer pbuffer = null;

  //
  private static boolean openGLWindowOpen = false;

  // Counter for the frames.
  private static int numberOfFrames = 0;
  // Timer for frame-rate.
  private static long lastTime = System.currentTimeMillis();
  // Timer to show sky in real time.
  private static long skyLastTime = System.currentTimeMillis();

  // Position of the camera.
  private static float[] cam_pos = new float[] { -10, 10, 5 }; // position
                                  // x,y,z
  // Orientation of the camera in HPR-mode.
  private static float[] view_hpr = new float[] { -45, -20, 0 }; // heading,
                                  // pitch,
                                  // roll
                                  // (degrees)
  // Orientation of camera in LookAt-mode;
  private static float[] look_at_pos = new float[] { 0, 0, 0 };

  // private static boolean enableGluLookAt = false;

  private static double rotationCounter = 0;

  // Size of window. 800 by 600 is standard but can be changed with params.
  private static int width = 800;
  private static int height = 600;

  // Stores the last pressed key.
  private static int last_key_pressed;
  private static final double M_PI = Math.PI;

  // textures and shadows
  private static boolean use_textures = true;
  private static boolean use_shadows = true;
  private static Texture sky_texture = null;
  private static Texture ground_texture = null;
  private static Texture wood_texture = null;
  private static Texture checkered_texture = null;
  private static Texture[] texture = new Texture[4 + 1]; // +1 since index 0
                              // is not used

  // light vector. LIGHTZ is implicitly 1
  private static final float LIGHTX = 1.0f;
  private static final float LIGHTY = 0.4f;

  // FloatBuffers for the light.
  private static final FloatBuffer light_position = BufferUtils
      .createFloatBuffer(4);
  private static final FloatBuffer light_ambient = BufferUtils
      .createFloatBuffer(4);
  private static final FloatBuffer light_diffuse = BufferUtils
      .createFloatBuffer(4);
  private static final FloatBuffer light_specular = BufferUtils
      .createFloatBuffer(4);
  static {
    light_position.put(new float[] { LIGHTX, LIGHTY, 1.0f, 0.0f }).flip();
    light_ambient.put(new float[] { 0.5f, 0.5f, 0.5f, 1.0f }).flip();
    light_diffuse.put(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }).flip();
    light_specular.put(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }).flip();
  }

  private static float[] color = { 0, 0, 0, 0 }; // current r,g,b,alpha color
  private static DS_TEXTURE_NUMBER tnum = DS_TEXTURE_NUMBER.DS_NONE; // current
                                    // texture
                                    // number

  // Define sky/far background color.
  private static float skyColorR = (194.f / 255.f);
  private static float skyColorG = (233.f / 255.f);
  private static float skyColorB = (254.f / 255.f);
  private static float skyColorA = (0.f / 255.f);

  // Define whether the ground, the sky, and the coordinate pyramids are to be
  // drawn or not.
  private static boolean enableGround = false;
  private static boolean enableSky = true;
  private static boolean enableCoordinatePyramids = true;
  private static boolean realTime = true;
  private static boolean skyDynamic = false;

  // Window title
  private static String windowTitle = "LWJGL";

  // texture numbers
  public static enum DS_TEXTURE_NUMBER {
    DS_NONE, // = 0, /* uses the current color instead of a texture */
    DS_WOOD, DS_CHECKERED, DS_GROUND, DS_SKY;
  }

  public static enum CAMERA_MODE {
    HPR_CAMERA, LOOK_AT_CAMERA
  }

  private static int camMode = 0;

  public static void setCamMode(CAMERA_MODE cm) {
    camMode = cm.ordinal();
  }

  /*
   * (non-Javadoc)
   *
   * @see eas.plugins.Plugin#getRequiredPlugins()
   */
  @Override
  public List<String> getRequiredPlugins() {
    return null;
  }

  /*
   * (non-Javadoc)
   *
   * @see eas.plugins.Plugin#getParameters()
   */
  @Override
  public List<SingleParameter> getParameters() {
    ArrayList<SingleParameter> list = new ArrayList<SingleParameter>();

    list.add(new SingleParameter("windowWidth", Datatypes.INTEGER, 800,
        "Breite des Fensters (800, " + "falls nichts angegeben).", this
            .id().toUpperCase()));

    list.add(new SingleParameter("windowHeight", Datatypes.INTEGER, 600,
        "Höhe des Fensters (600, " + "falls nichts angegeben).", this
            .id().toUpperCase()));

    list.add(new SingleParameter("drawSky", Datatypes.BOOLEAN, true,
        "Legt fest, ob Himmel gezeichnet werden soll.", this.id()
            .toUpperCase()));

    list.add(new SingleParameter(
        "drawCoordinates",
        Datatypes.BOOLEAN,
        true,
        "Legt fest, ob Koordinatenkreuz am Ursprung gezeichnet werden soll.",
        this.id().toUpperCase()));

    list.add(new SingleParameter("realTime", Datatypes.BOOLEAN, true,
        "Gibt an, ob die Darstellung in Echtzeit erfolgt.", this.id()
            .toUpperCase()));

    return list;
  }

  @Override
  public String id() {
    return "videoPluginLWJGL";
  }

  /*
   * (non-Javadoc)
   *
   * @see eas.plugins.Plugin#runBeforeSim(eas.simulation.environment.
   * AbstractEnvironment, eas.startSetup.ParCollection)
   */
  @Override
  public void runBeforeSimulation(
      AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>> env,
      ParCollection params) {

    environment = env;
    width = params.getParValueInt("windowWidth");
    height = params.getParValueInt("windowHeight");
    enableSky = params.getParValueBoolean("drawSky");
    enableCoordinatePyramids = params.getParValueBoolean("drawCoordinates");
    realTime = params.getParValueBoolean("realTime");
    createMainWindow(width, height);
    loadTextures();
    handleKeyboard();
    handleMouse();
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
    dsDrawFrame();
    Display.update();
    lastTime = System.currentTimeMillis();
    numberOfFrames = 0;
    openGLWindowOpen = true;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * eas.plugins.Plugin#runAfterSim(eas.simulation.environment.AbstractEnvironment
   * , eas.startSetup.ParCollection)
   */
  @Override
  public void runAfterSimulation(
      AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>> env,
      ParCollection params) {
    Keyboard.destroy();
    Mouse.destroy();
    Display.destroy();
  }

  /*
   * (non-Javadoc)
   *
   * @see eas.plugins.Plugin#runDuringSim(eas.simulation.environment.
   * AbstractEnvironment, eas.simulation.Wink, eas.startSetup.ParCollection)
   */
  @Override
  public void runDuringSimulation(
      AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>> env,
      Wink currentSimTime, ParCollection params) {

    handleKeyboard();
    handleMouse();
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
    dsDrawFrame();
    for (int i = 0; i < env.getAgents().size(); i++) {
      AbstractAgentLWJGLdrawable<?> agent = env.getAgents().get(i);
      if (agent.isDrawableInLWJGL()) {
        agent.drawInLWJGL();
      }
    }

    Display.update();

    numberOfFrames++;
    long difference = (System.currentTimeMillis() - lastTime);
    if (difference > 1000) {
      difference /= 1000;
      Display.setTitle(windowTitle + " - " + numberOfFrames / difference
          + " frames per second");
      lastTime = System.currentTimeMillis();
      numberOfFrames = 0;
    }

    if (Display.isCloseRequested()) {
      env.getSimTime().timeTerminate();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see eas.plugins.Plugin#handleEvent(eas.simulation.event.EASEvent,
   * eas.simulation.environment.AbstractEnvironment, eas.simulation.Wink,
   * eas.startSetup.ParCollection)
   */
  @Override
  public void handleEvent(
      EASEvent e,
      AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>> env,
      Wink lastSimTime, ParCollection params) {
  }

  public static void createMainWindow(int _width, int _height) {
    Display.setLocation((Display.getDisplayMode().getWidth() - _width) / 2,
        (Display.getDisplayMode().getHeight() - _height) / 2);
    try {
      Display.setDisplayMode(new DisplayMode(_width, _height));
      Display.setTitle(windowTitle);
      Display.setVSyncEnabled(true); // for VSync (TZ)
      Display.create();
    } catch (LWJGLException e) {
      throw new RuntimeException(e);
    }

    try {
      Keyboard.create();
      Mouse.create();
    } catch (LWJGLException e) {
      throw new RuntimeException(e);
    }

    width = _width;
    height = _height;
    last_key_pressed = 0;

    try {
      Display.makeCurrent();
    } catch (LWJGLException e) {
      throw new RuntimeException(e);
    }
  }

  // Load the textures
  private static void loadTextures() {
    String s = System.getProperty("file.separator");
    String prefix = "sharedDirectory" + s + "textures" + s;

    sky_texture = new Texture(prefix + "sky.ppm");
    texture[DS_TEXTURE_NUMBER.DS_SKY.ordinal()] = sky_texture;

    ground_texture = new Texture(prefix + "ground.ppm");
    texture[DS_TEXTURE_NUMBER.DS_GROUND.ordinal()] = ground_texture;

    wood_texture = new Texture(prefix + "wood.ppm");
    texture[DS_TEXTURE_NUMBER.DS_WOOD.ordinal()] = wood_texture;

    checkered_texture = new Texture(prefix + "checkered.ppm");
    texture[DS_TEXTURE_NUMBER.DS_CHECKERED.ordinal()] = checkered_texture;
  }

  private static void handleKeyboard() {
    Keyboard.poll();

    if (Keyboard.isKeyDown(last_key_pressed)) {
      environment.handleKeyboard((char) last_key_pressed);
    }

    while (Keyboard.next()) {
      char key = (char) Keyboard.getEventKey();
      last_key_pressed = key;
      if (key == Keyboard.KEY_ESCAPE || key == Keyboard.KEY_Q) {
        environment.getSimTime().timeTerminate();
      }
      environment.handleKeyboard(key);
    }
  }

  private static void handleMouse() {
    // iterate all events, use the last button down
    while (Mouse.next()) {
      if (Mouse.getEventButton() != -1) {
        if (Mouse.getEventButtonState()) {
        }
        // lastButton = Mouse.getEventButton();
      }
    }
    updateState();
  }

  // Updates view.
  private static void updateState() {
    int dx = Mouse.getDX();
    int dy = Mouse.getDY();
    int dw = Mouse.getDWheel();

    // get out if no movement
    if (dx == dy && dx == 0 && dw == 0) {
      return;
    }
    {
      float s = (float) Math.sin(Math.toRadians(view_hpr[0]));
      float c = (float) Math.cos(Math.toRadians(view_hpr[0]));
      float h = (float) Math.sin(Math.toRadians(view_hpr[1]));
      cam_pos[0] += c * dw * 0.025f;
      cam_pos[1] += s * dw * 0.025f;
      cam_pos[2] += h * dw * 0.025f;
    }
    // LWJGL: 0=left 1=right 2=middle
    // GL: 0=left 1=middle 2=right

    int mode = 0;
    if (Mouse.isButtonDown(0))
      mode |= 1;
    if (Mouse.isButtonDown(2))
      mode |= 2;
    if (Mouse.isButtonDown(1))
      mode |= 4;
    if (mode != 0) {
      // LWJGL has inverted dy wrt C++/GL
      dsMotion(mode, dx, -dy);
    }

  }

  /*
   * Call this to update the current camera position. the bits in `mode' say
   * if the left (1), middle (2) or right (4) mouse button is pressed, and
   * (deltax,deltay) is the amount by which the mouse pointer has moved.
   */
  private static void dsMotion(int mode, int deltax, int deltay) {
    float side = 0.01f * deltax;
    float fwd = (mode == 4) ? (0.01f * deltay) : 0.0f;
    float s = (float) Math.sin(Math.toRadians(view_hpr[0]));
    float c = (float) Math.cos(Math.toRadians(view_hpr[0]));

    if (mode == 1) {
      view_hpr[0] += (deltax) * 0.5f;
      view_hpr[1] += (deltay) * 0.5f;
    } else {
      cam_pos[0] += -s * side + c * fwd;
      cam_pos[1] += c * side + s * fwd;
      if (mode == 2 || mode == 5)
        cam_pos[2] += 0.01f * (deltay);
    }
    wrapCameraAngles();
  }

  // Wrap cameras angles.
  private static void wrapCameraAngles() {
    for (int i = 0; i < 3; i++) {
      while (view_hpr[i] > 180)
        view_hpr[i] -= 360;
      while (view_hpr[i] < -180)
        view_hpr[i] += 360;
    }
  }

  private static void dsDrawFrame() {
    // setup stuff
    GL11.glEnable(GL11.GL_LIGHTING);
    GL11.glEnable(GL11.GL_LIGHT0);
    GL11.glDisable(GL11.GL_TEXTURE_2D);
    GL11.glDisable(GL11.GL_TEXTURE_GEN_S);
    GL11.glDisable(GL11.GL_TEXTURE_GEN_T);
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LESS);
    GL11.glEnable(GL11.GL_CULL_FACE);
    GL11.glCullFace(GL11.GL_BACK);
    GL11.glFrontFace(GL11.GL_CCW);

    // setup viewport
    GL11.glViewport(0, 0, width, height);
    // 3d
    GL11.glMatrixMode(GL11.GL_PROJECTION);
    // initialize with identity matrix
    GL11.glLoadIdentity();

    // closest distance from camera to be drawn
    final float vnear = 0.1f;
    // final float vfar = 100.0f;
    // farthest distance from camera to be drawn
    final float vfar = 1000.0f;
    // view scale, 1 = +/- 45 degrees
    final float k = 0.8f;

    // define frustum of view (Pyramidenstumpf des zu zeichnenden Raums)
    // s.a. http://www.opengl.org/resources/faq/technical/viewing.htm
    if (width >= height) { // breiter als hoch
      float aspectRatio = (float) height / (float) width;
      GL11.glFrustum(-vnear * k, vnear * k, -vnear * k * aspectRatio,
          vnear * k * aspectRatio, vnear, vfar);
    } else { // höher als breit
      float aspectRatio = (float) width / (float) height;
      GL11.glFrustum(-vnear * k * aspectRatio, vnear * k * aspectRatio,
          -vnear * k, vnear * k, vnear, vfar);
    }

    // setup lights. it makes a difference whether this is done in the
    // GL_PROJECTION matrix mode (lights are scene relative) or the
    // GL_MODELVIEW matrix mode (lights are camera relative, bad!).
    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, light_ambient);
    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, light_diffuse);
    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, light_specular);
    GL11.glColor3f(1.0f, 1.0f, 1.0f);

    // clear the window (this means that the background is drawn
    // on which the rest will be drawn
    GL11.glClearColor(skyColorR, skyColorG, skyColorB, skyColorA);
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

    // snapshot camera position (in MS Windows it is changed by the GUI
    // thread)
    float[] cam_pos_cloned = cam_pos.clone();
    float[] view_hpr_cloned = view_hpr.clone();

    // go to GL_MODELVIEW matrix mode and set the camera
    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glLoadIdentity();

    if (camMode == CAMERA_MODE.HPR_CAMERA.ordinal()) {
      setHPRCamera(cam_pos_cloned[0], cam_pos_cloned[1],
          cam_pos_cloned[2], view_hpr_cloned[0], view_hpr_cloned[1],
          view_hpr_cloned[2]);
    } else if (camMode == CAMERA_MODE.LOOK_AT_CAMERA.ordinal()) {
      GLU.gluLookAt(cam_pos[0], cam_pos[1], cam_pos[2], look_at_pos[0],
          look_at_pos[1], look_at_pos[2], 0, 0, 1);
      cam_pos_cloned = cam_pos.clone();
    }
    // set the light position (for some reason we have to do this in model
    // view.
    // static GLfloat light_position[] = { LIGHTX, LIGHTY, 1.0, 0.0 };
    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, light_position);

    // draw the background (sky and horizon)
    drawSky(cam_pos_cloned);
    // draw the ground
    drawGround();
    // draw the little markers on the ground
    drawPyramidGrid();

    // leave openGL in a known state - flat shaded white, no textures
    GL11.glEnable(GL11.GL_LIGHTING);
    GL11.glDisable(GL11.GL_TEXTURE_2D);
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LESS);
    GL11.glColor3f(1, 1, 1);
    setColor(1, 1, 1, 1);

    // draw the rest of the objects. set drawing state first.
    color[0] = 1;
    color[1] = 1;
    color[2] = 1;
    color[3] = 1;
    tnum = DS_TEXTURE_NUMBER.DS_NONE;
    // if (fn.step)
    // fn.step(pause);
  }

  private static void setHPRCamera(float x, float y, float z, float h,
      float p, float r) {
    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glLoadIdentity();
    GL11.glRotatef(90, 0, 0, 1);
    GL11.glRotatef(90, 0, 1, 0);
    GL11.glRotatef(r, 1, 0, 0);
    GL11.glRotatef(p, 0, 1, 0);
    GL11.glRotatef(-h, 0, 0, 1);
    GL11.glTranslatef(-x, -y, -z);
  }

  // sky texture scale (1/size)
  private static final float sky_scale = 1.0f / 4.0f;
  // sky height above viewpoint
  private static final float sky_height = 1.0f;

  private static float offset = 0.0f;

  private static void drawSky(float[] view_xyz) {
    if (!enableSky) {
      return;
    }
    GL11.glDisable(GL11.GL_LIGHTING);
    if (use_textures) {
      GL11.glEnable(GL11.GL_TEXTURE_2D);
      sky_texture.bind(false);
    } else {
      GL11.glDisable(GL11.GL_TEXTURE_2D);
      GL11.glColor3f(0f, 0.5f, 1.0f);
    }

    // make sure sky depth is as far back as possible
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LEQUAL);
    GL11.glDepthRange(1, 1);

    final float ssize = 1000.0f;

    float x = ssize * sky_scale;
    float z = view_xyz[2] + sky_height;

    if (!skyDynamic) {
      offset = 0;
    }

    GL11.glBegin(GL11.GL_QUADS);
    GL11.glNormal3f(0, 0, -1);
    GL11.glTexCoord2f(-x + offset, -x + offset);
    GL11.glVertex3f(-ssize + view_xyz[0], -ssize + view_xyz[1], z);
    GL11.glTexCoord2f(-x + offset, x + offset);
    GL11.glVertex3f(-ssize + view_xyz[0], ssize + view_xyz[1], z);
    GL11.glTexCoord2f(x + offset, x + offset);
    GL11.glVertex3f(ssize + view_xyz[0], ssize + view_xyz[1], z);
    GL11.glTexCoord2f(x + offset, -x + offset);
    GL11.glVertex3f(ssize + view_xyz[0], -ssize + view_xyz[1], z);
    GL11.glEnd();
    if (realTime) {
      offset = offset + 0.002f
          * (System.currentTimeMillis() - skyLastTime) / 100;

      skyLastTime = System.currentTimeMillis();
      if (offset > 1)
        offset -= 1;
    }
    GL11.glDepthFunc(GL11.GL_LESS);
    GL11.glDepthRange(0, 1);
  }

  private static final float ground_scale = 1.0f / 10.0f; // ground texture
                              // scale (1/size)
  private static final float ground_ofsx = 0.5f; // offset of ground texture
  private static final float ground_ofsy = 0.5f;
  // ground color for when there's no texture
  private static final float GROUND_R = 0.5f;
  private static final float GROUND_G = 0.5f;
  private static final float GROUND_B = 0.3f;

  private static void drawGround() {
    if (!enableGround) {
      return;
    }

    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LESS);
    GL11.glDepthRange(0, 1);

    if (use_textures) {
      GL11.glEnable(GL11.GL_TEXTURE_2D);
      ground_texture.bind(false);
    } else {
      GL11.glDisable(GL11.GL_TEXTURE_2D);
      GL11.glColor3f(GROUND_R, GROUND_G, GROUND_B);
    }

    final float gsize = 10000.0f;
    final float offset = 0; // -0.001f; ... polygon offsetting doesn't work
                // well

    GL11.glBegin(GL11.GL_QUADS);
    GL11.glNormal3f(0, 0, 1);
    GL11.glTexCoord2f(-gsize * ground_scale + ground_ofsx, -gsize
        * ground_scale + ground_ofsy);
    GL11.glVertex3f(-gsize, -gsize, offset);
    GL11.glTexCoord2f(gsize * ground_scale + ground_ofsx, -gsize
        * ground_scale + ground_ofsy);
    GL11.glVertex3f(gsize, -gsize, offset);
    GL11.glTexCoord2f(gsize * ground_scale + ground_ofsx, gsize
        * ground_scale + ground_ofsy);
    GL11.glVertex3f(gsize, gsize, offset);
    GL11.glTexCoord2f(-gsize * ground_scale + ground_ofsx, gsize
        * ground_scale + ground_ofsy);
    GL11.glVertex3f(-gsize, gsize, offset);
    GL11.glEnd();

    GL11.glDisable(GL11.GL_FOG);
  }

  private static void drawPyramidGrid() {
    if (!enableCoordinatePyramids) {
      return;
    }

    // setup stuff
    GL11.glEnable(GL11.GL_LIGHTING);
    GL11.glDisable(GL11.GL_TEXTURE_2D);
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDepthFunc(GL11.GL_LESS);

    // draw the pyramid grid
    for (int i = -1; i <= 1; i++) {
      for (int j = -1; j <= 1; j++) {
        GL11.glPushMatrix();
        GL11.glTranslatef(i, j, 0);
        if (i == 1 && j == 0)
          setColor(1, 0, 0, 1);
        else if (i == 0 && j == 1)
          setColor(0, 0, 1, 1);
        else
          setColor(1, 1, 0, 1);
        final float k = 0.05f;
        GL11.glBegin(GL11.GL_TRIANGLE_FAN);
        GL11.glNormal3f(0, -1, 1);
        GL11.glVertex3f(0, 0, k);
        GL11.glVertex3f(-k, -k, 0);
        GL11.glVertex3f(k, -k, 0);
        GL11.glNormal3f(1, 0, 1);
        GL11.glVertex3f(k, k, 0);
        GL11.glNormal3f(0, 1, 1);
        GL11.glVertex3f(-k, k, 0);
        GL11.glNormal3f(-1, 0, 1);
        GL11.glVertex3f(-k, -k, 0);
        GL11.glEnd();
        GL11.glPopMatrix();
      }
    }
  }

  private static FloatBuffer light_ambient2 = BufferUtils
      .createFloatBuffer(4);
  private static FloatBuffer light_diffuse2 = BufferUtils
      .createFloatBuffer(4);
  private static FloatBuffer light_specular2 = BufferUtils
      .createFloatBuffer(4);

  private static void setColor(float r, float g, float b, float alpha) {
    // GLfloat light_ambient[4],light_diffuse[4],light_specular[4];
    light_ambient2.put(new float[] { r * 0.3f, g * 0.3f, b * 0.3f, alpha })
        .flip();
    light_diffuse2.put(new float[] { r * 0.7f, g * 0.7f, b * 0.7f, alpha })
        .flip();
    light_specular2
        .put(new float[] { r * 0.2f, g * 0.2f, b * 0.2f, alpha })
        .flip();
    GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, light_ambient2);
    GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_DIFFUSE, light_diffuse2);
    GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR,
        light_specular2);
    GL11.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 5.0f);
  }

  // ***************************************************************************
  // Texture object.

  public static class Texture {
    private Image image;
    private int name; // GLuint TZ

    // public:
    // Texture (String filename);
    // ~Texture();
    // void bind (int modulate);

    public Texture(String filename) {
      image = new ImageIO(filename);
      // GL11.glGenTextures (1,name);
      IntBuffer ib = BufferUtils.createIntBuffer(1);
      ib.put(name).flip(); // Necessary ? TZ
      GL11.glGenTextures(ib);
      name = ib.get(0);
      GL11.glBindTexture(GL11.GL_TEXTURE_2D, name);

      // set pixel unpacking mode
      GL11.glPixelStorei(GL11.GL_UNPACK_SWAP_BYTES, 0);
      GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0);
      GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
      GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0);
      GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0);

      // glTexImage2D (GL11.GL_TEXTURE_2D, 0, 3, image->width(),
      // image->height(), 0,
      // GL_RGB, GL_UNSIGNED_BYTE, image->data());
      // GL11.glTexImage2D (GL11.GL_TEXTURE_2D, 0, 3, image.width(),
      // image.height(), 0,
      // GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE,
      // ByteBuffer.wrap(image.data()));
      // TZ this is the original:
      // GLX.gluBuild2DMipmaps (GL11.GL_TEXTURE_2D, 3, image.width(),
      // image.height(),
      // GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image.data());
      GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, 3, image.width(),
          image.height(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE,
          image.data());

      // set texture parameters - will these also be bound to the
      // texture???
      GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
          GL11.GL_REPEAT);
      GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
          GL11.GL_REPEAT);

      GL11.glTexParameterf(GL11.GL_TEXTURE_2D,
          GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
      GL11.glTexParameterf(GL11.GL_TEXTURE_2D,
          GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);

      GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE,
          GL11.GL_DECAL);
    }

    // Texture::~Texture()
    // {
    // delete image;
    // glDeleteTextures (1,&name);
    // }
    @Override
    protected void finalize() throws Throwable {
      image = null;
      // GL11.glDeleteTextures (1, name));
      GL11.glDeleteTextures(IntBuffer.wrap(new int[] { name }));
      super.finalize();
    }

    public void bind(boolean modulate) {
      GL11.glBindTexture(GL11.GL_TEXTURE_2D, name);
      GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE,
          modulate ? GL11.GL_MODULATE : GL11.GL_DECAL);
    }
  }

  // ***************************************************************************
  // PPM image object

  private interface Image {

    int height();

    int width();

    ByteBuffer data();

  }

  private static class ImageIO implements Image {
    private int image_width, image_height;
    private ByteBuffer image_data;

    // public:
    // Image (String filename);
    // load from PPM file
    // ~Image();
    @Override
    public int width() {
      return image_width;
    }

    @Override
    public int height() {
      return image_height;
    }

    @Override
    public ByteBuffer data() {
      return image_data;
    }

    // skip over whitespace and comments in a stream.

    private void skipWhiteSpace(String filename, PushbackInputStream f)
        throws IOException {
      int c, d;
      while (true) {// for(;;) {
        c = f.read();// fgetc(f);
        if (c == -1)
          System.err.println("unexpected end of file in " + filename);

        // skip comments
        if (c == '#') {
          do {
            d = f.read();// fgetc(f);
            if (d == -1)
              System.err.println("unexpected end of file in "
                  + filename);
          } while (d != '\n');
          continue;
        }

        if (c > ' ') {
          f.unread(c);// ungetc (c,f);
          return;
        }
      }
    }

    // read a number from a stream, this return 0 if there is none (that's
    // okay
    // because 0 is a bad value for all PPM numbers anyway).

    private int readNumber(String filename, PushbackInputStream f)
        throws IOException {
      int c, n = 0;
      for (;;) {
        c = f.read();// fgetc(f);
        if (c == -1)
          System.err.println("unexpected end of file in " + filename);
        if (c >= '0' && c <= '9')
          n = n * 10 + (c - '0');
        else {
          f.unread(c);// ungetc (c,f);
          return n;
        }
      }
    }

    ImageIO(String filename) {
      PushbackInputStream f = null;
      try {

        // InputStream is = getClass().getResourceAsStream(filename);
        FileInputStream is = new FileInputStream(filename);
        // if (is == null) throw new
        // IllegalArgumentException("File not found: " + filename);
        f = new PushbackInputStream(new BufferedInputStream(is));
        // if (f == null) System.err.println
        // ("Can't open image file "+filename);

        // read in header
        // if (fgetcC(f) != 'P' || fgetcC(f) != '6')
        if (f.read() != 'P' || f.read() != '6')
          System.err
              .println("image file is not a binary PPM (no P6 header): "
                  + filename);
        skipWhiteSpace(filename, f);

        // read in image parameters
        image_width = readNumber(filename, f);
        skipWhiteSpace(filename, f);
        image_height = readNumber(filename, f);
        skipWhiteSpace(filename, f);
        int max_value = readNumber(filename, f);

        // check values
        if (image_width < 1 || image_height < 1)
          System.err.println("bad image file " + filename);
        if (max_value != 255)
          System.err
              .println("image file must have color range of 255"
                  + filename);

        // read either nothing, LF (10), or CR,LF (13,10)
        int c = f.read();// fgetc(f);
        if (c == 10) {
          // LF
        } else if (c == 13) {
          // CR
          c = f.read();// fgetc(f);
          if (c != 10)
            f.unread(c);// ungetc (c,f);
        } else
          f.unread(c);// ungetc (c,f);

        // read in rest of data
        // image_data = new byte [image_width*image_height*3];
        image_data = BufferUtils.createByteBuffer(image_width
            * image_height * 3);
        // To Do TZ read directly into direct buffer!
        byte[] buf = new byte[image_width * image_height * 3];
        if (f.read(buf, 0, image_width * image_height * 3 * 1) != image_width
            * image_height * 3)
          System.err.println("Can not read data from image file "
              + filename);
        image_data.put(buf);
        image_data.flip();
      } catch (IOException e) {
        System.err.println("Error reading file: \"" + filename + "\"");
        e.printStackTrace();
        if (f != null) {
          try {
            f.close();
          } catch (IOException e2) {
            e2.printStackTrace();
          }
        }
        throw new RuntimeException(e);
      }
    }
  }

  public static void dsSetViewpoint(float[] xyz, float[] hpr) {
    if (xyz != null) {
      cam_pos[0] = xyz[0];
      cam_pos[1] = xyz[1];
      cam_pos[2] = xyz[2];
    }
    if (hpr != null) {
      view_hpr[0] = hpr[0];
      view_hpr[1] = hpr[1];
      view_hpr[2] = hpr[2];
      wrapCameraAngles();
    }
  }

  private static void dsDrawBox(final float[] pos, final float[] R,
      final float[] sides) {
    // if (current_state != 2) dsError
    // ("drawing function called outside simulation loop");
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawBox(sides);
    GL11.glPopMatrix();

    if (use_shadows) {
      setShadowDrawingMode();
      setShadowTransform();
      setTransform(pos, R);
      drawBox(sides);
      GL11.glPopMatrix();
      GL11.glPopMatrix();
      GL11.glDepthRange(0, 1);
    }
  }

  // static void drawBox (final float sides[3])
  private static void drawBox(final float[] sides) {
    float lx = sides[0] * 0.5f;
    float ly = sides[1] * 0.5f;
    float lz = sides[2] * 0.5f;

    // sides
    GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    GL11.glNormal3f(-1, 0, 0);
    GL11.glVertex3f(-lx, -ly, -lz);
    GL11.glVertex3f(-lx, -ly, lz);
    GL11.glVertex3f(-lx, ly, -lz);
    GL11.glVertex3f(-lx, ly, lz);
    GL11.glNormal3f(0, 1, 0);
    GL11.glVertex3f(lx, ly, -lz);
    GL11.glVertex3f(lx, ly, lz);
    GL11.glNormal3f(1, 0, 0);
    GL11.glVertex3f(lx, -ly, -lz);
    GL11.glVertex3f(lx, -ly, lz);
    GL11.glNormal3f(0, -1, 0);
    GL11.glVertex3f(-lx, -ly, -lz);
    GL11.glVertex3f(-lx, -ly, lz);
    GL11.glEnd();

    // top face
    GL11.glBegin(GL11.GL_TRIANGLE_FAN);
    GL11.glNormal3f(0, 0, 1);
    GL11.glVertex3f(-lx, -ly, lz);
    GL11.glVertex3f(lx, -ly, lz);
    GL11.glVertex3f(lx, ly, lz);
    GL11.glVertex3f(-lx, ly, lz);
    GL11.glEnd();

    // bottom face
    GL11.glBegin(GL11.GL_TRIANGLE_FAN);
    GL11.glNormal3f(0, 0, -1);
    GL11.glVertex3f(-lx, -ly, -lz);
    GL11.glVertex3f(-lx, ly, -lz);
    GL11.glVertex3f(lx, ly, -lz);
    GL11.glVertex3f(lx, -ly, -lz);
    GL11.glEnd();
  }

  // sets lighting and texture modes, sets current color
  private static final FloatBuffer s_params_SDM = BufferUtils
      .createFloatBuffer(4);
  private static final FloatBuffer t_params_SDM = BufferUtils
      .createFloatBuffer(4);
  static {
    s_params_SDM.put(new float[] { 1.0f, 1.0f, 0.0f, 1 }).flip();
    t_params_SDM.put(new float[] { 0.817f, -0.817f, 0.817f, 1 }).flip();
  }

  private static void setupDrawingMode() {
    GL11.glEnable(GL11.GL_LIGHTING);
    if (tnum != DS_TEXTURE_NUMBER.DS_NONE) {
      if (use_textures) {
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        texture[tnum.ordinal()].bind(true);
        GL11.glEnable(GL11.GL_TEXTURE_GEN_S);
        GL11.glEnable(GL11.GL_TEXTURE_GEN_T);
        GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE,
            GL11.GL_OBJECT_LINEAR);
        GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE,
            GL11.GL_OBJECT_LINEAR);
        // static GLfloat s_params[4] = {1.0f,1.0f,0.0f,1};
        // static GLfloat t_params[4] = {0.817f,-0.817f,0.817f,1};
        GL11.glTexGen(GL11.GL_S, GL11.GL_OBJECT_PLANE, s_params_SDM);
        GL11.glTexGen(GL11.GL_T, GL11.GL_OBJECT_PLANE, t_params_SDM);
      } else {
        GL11.glDisable(GL11.GL_TEXTURE_2D);
      }
    } else {
      GL11.glDisable(GL11.GL_TEXTURE_2D);
    }
    setColor(color[0], color[1], color[2], color[3]);

    if (color[3] < 1) {
      GL11.glEnable(GL11.GL_BLEND);
      GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
    } else {
      GL11.glDisable(GL11.GL_BLEND);
    }
  }

  private static FloatBuffer matrixF = BufferUtils.createFloatBuffer(16);

  // static void setTransform (final float pos[3], final float R[12])
  private static void setTransform(final float[] pos, final float[] R) {
    // GLfloat
    float[] matrix = new float[16];
    matrix[0] = R[0];
    matrix[1] = R[4];
    matrix[2] = R[8];
    matrix[3] = 0;
    matrix[4] = R[1];
    matrix[5] = R[5];
    matrix[6] = R[9];
    matrix[7] = 0;
    matrix[8] = R[2];
    matrix[9] = R[6];
    matrix[10] = R[10];
    matrix[11] = 0;
    matrix[12] = pos[0];
    matrix[13] = pos[1];
    matrix[14] = pos[2];
    matrix[15] = 1;
    matrixF.put(matrix);
    matrixF.flip();
    GL11.glPushMatrix();
    GL11.glMultMatrix(matrixF);
  }

  private static final FloatBuffer s_params_SSDM = BufferUtils
      .createFloatBuffer(4);
  private static final FloatBuffer t_params_SSDM = BufferUtils
      .createFloatBuffer(4);
  static {
    s_params_SSDM.put(new float[] { ground_scale, 0, 0, ground_ofsx })
        .flip();
    t_params_SSDM.put(new float[] { 0, ground_scale, 0, ground_ofsy })
        .flip();
  }

  private static final float SHADOW_INTENSITY = 0.65f;

  private static void setShadowDrawingMode() {
    GL11.glDisable(GL11.GL_LIGHTING);
    if (use_textures) {
      GL11.glEnable(GL11.GL_TEXTURE_2D);
      ground_texture.bind(true);
      GL11.glColor3f(SHADOW_INTENSITY, SHADOW_INTENSITY, SHADOW_INTENSITY);
      GL11.glEnable(GL11.GL_TEXTURE_2D);
      GL11.glEnable(GL11.GL_TEXTURE_GEN_S);
      GL11.glEnable(GL11.GL_TEXTURE_GEN_T);
      GL11.glTexGeni(GL11.GL_S, GL11.GL_TEXTURE_GEN_MODE,
          GL11.GL_EYE_LINEAR);
      GL11.glTexGeni(GL11.GL_T, GL11.GL_TEXTURE_GEN_MODE,
          GL11.GL_EYE_LINEAR);
      // static GLfloat s_params[4] = {ground_scale,0,0,ground_ofsx};
      // static GLfloat t_params[4] = {0,ground_scale,0,ground_ofsy};
      GL11.glTexGen(GL11.GL_S, GL11.GL_EYE_PLANE, s_params_SSDM);
      GL11.glTexGen(GL11.GL_T, GL11.GL_EYE_PLANE, t_params_SSDM);
    } else {
      GL11.glDisable(GL11.GL_TEXTURE_2D);
      GL11.glColor3f(GROUND_R * SHADOW_INTENSITY, GROUND_G
          * SHADOW_INTENSITY, GROUND_B * SHADOW_INTENSITY);
    }
    GL11.glDepthRange(0, 0.9999);
  }

  // set shadow projection transform

  private static FloatBuffer matrixSST = BufferUtils.createFloatBuffer(16);

  private static void setShadowTransform() {
    // GLfloat
    float[] matrix = new float[16];
    for (int i = 0; i < 16; i++)
      matrix[i] = 0;
    matrix[0] = 1;
    matrix[5] = 1;
    matrix[8] = -LIGHTX;
    matrix[9] = -LIGHTY;
    matrix[15] = 1;
    matrixSST.put(matrix);
    // for (int i=0; i < 16; i++) matrixSST.put(i, 0);
    // matrixSST.put(0, 1);
    // matrixSST.put(5, 1);
    // matrixSST.put(8, -LIGHTX);
    // matrixSST.put(9, -LIGHTY);
    // matrixSST.put(15, 1);
    matrixSST.flip();
    GL11.glPushMatrix();
    GL11.glMultMatrix(matrixSST);
  }

  public static void dsSetColor(float red, float green, float blue) {
    // if (current_state != 2) dsError
    // ("drawing function called outside simulation loop");
    color[0] = red;
    color[1] = green;
    color[2] = blue;
    color[3] = 1;
  }

  // extern "C"
  public static void dsSetColorAlpha(float red, float green, float blue,
      float alpha) {
    // if (current_state != 2) dsError
    // ("drawing function called outside simulation loop");
    color[0] = red;
    color[1] = green;
    color[2] = blue;
    color[3] = alpha;
  }

  @SuppressWarnings("unused")
  private static void dsDrawConvex(final float[] pos, final float[] R,
      float[] _planes, int _planecount, float[] _points, int _pointcount,
      int[] _polygons) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawConvex(_planes, _planecount, _points, _pointcount, _polygons);
    GL11.glPopMatrix();
    if (use_shadows) {
      setShadowDrawingMode();
      setShadowTransform();
      setTransform(pos, R);
      drawConvex(_planes, _planecount, _points, _pointcount, _polygons);
      GL11.glPopMatrix();
      GL11.glPopMatrix();
      GL11.glDepthRange(0, 1);
    }
  }

  private static void drawConvex(float[] _planes, int _planecount,
      float[] _points, @SuppressWarnings("unused") int _pointcount,
      int[] _polygons) {
    // unsigned
    int polyindex = 0;
    for (int i = 0; i < _planecount; ++i) {
      // unsigned
      int pointcount = _polygons[polyindex];
      polyindex++;
      GL11.glBegin(GL11.GL_POLYGON);
      GL11.glNormal3f(_planes[(i * 4) + 0], _planes[(i * 4) + 1],
          _planes[(i * 4) + 2]);
      for (int j = 0; j < pointcount; ++j) {
        GL11.glVertex3f(_points[_polygons[polyindex] * 3],
            _points[(_polygons[polyindex] * 3) + 1],
            _points[(_polygons[polyindex] * 3) + 2]);
        polyindex++;
      }
      GL11.glEnd();
    }
  }

  // static void drawConvexD (double *_planes,unsigned int _planecount,
  // double *_points,
  // unsigned int _pointcount,
  // unsigned int *_polygons)
  private static void drawConvexD(double[] _planes, int _planecount,
      double[] _points, @SuppressWarnings("unused") int _pointcount,
      int[] _polygons) {
    // unsigned
    int polyindex = 0;
    for (int i = 0; i < _planecount; ++i) {
      // unsigned
      int pointcount = _polygons[polyindex];
      polyindex++;
      GL11.glBegin(GL11.GL_POLYGON);
      GL11.glNormal3d(_planes[(i * 4) + 0], _planes[(i * 4) + 1],
          _planes[(i * 4) + 2]);
      for (int j = 0; j < pointcount; ++j) {
        GL11.glVertex3d(_points[_polygons[polyindex] * 3],
            _points[(_polygons[polyindex] * 3) + 1],
            _points[(_polygons[polyindex] * 3) + 2]);
        polyindex++;
      }
      GL11.glEnd();
    }
  }

  // extern "C"
  // void dsDrawSphere (final float pos[3], final float R[12],
  // float radius)
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawSphere(float[],
   * float[], float)
   */
  private static void dsDrawSphere(final float[] pos, final float[] R,
      float radius) {
    setupDrawingMode();
    GL11.glEnable(GL11.GL_NORMALIZE);
    GL11.glShadeModel(GL11.GL_SMOOTH);
    setTransform(pos, R);
    GL11.glScaled(radius, radius, radius);
    drawSphere();
    GL11.glPopMatrix();
    GL11.glDisable(GL11.GL_NORMALIZE);
    // draw shadows
    if (use_shadows) {
      GL11.glDisable(GL11.GL_LIGHTING);
      if (use_textures) {
        ground_texture.bind(true);
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        GL11.glDisable(GL11.GL_TEXTURE_GEN_S);
        GL11.glDisable(GL11.GL_TEXTURE_GEN_T);
        GL11.glColor3f(SHADOW_INTENSITY, SHADOW_INTENSITY,
            SHADOW_INTENSITY);
      } else {
        GL11.glDisable(GL11.GL_TEXTURE_2D);
        GL11.glColor3f(GROUND_R * SHADOW_INTENSITY, GROUND_G
            * SHADOW_INTENSITY, GROUND_B * SHADOW_INTENSITY);
      }
      GL11.glShadeModel(GL11.GL_FLAT);
      GL11.glDepthRange(0, 0.9999);
      drawSphereShadow(pos[0], pos[1], pos[2], radius);
      GL11.glDepthRange(0, 1);
    }
  }

  private static int sphere_quality = 1;

  private static final float ICX = 0.525731112119133606f;
  private static final float ICZ = 0.850650808352039932f;
  private static final float[][] idata = { // GLfloat [12][3] = {
  { -ICX, 0, ICZ }, { ICX, 0, ICZ }, { -ICX, 0, -ICZ }, { ICX, 0, -ICZ },
      { 0, ICZ, ICX }, { 0, ICZ, -ICX }, { 0, -ICZ, ICX },
      { 0, -ICZ, -ICX }, { ICZ, ICX, 0 }, { -ICZ, ICX, 0 },
      { ICZ, -ICX, 0 }, { -ICZ, -ICX, 0 } };
  private static final int[][] index = {// [20][3] = {
  { 0, 4, 1 }, { 0, 9, 4 }, { 9, 5, 4 }, { 4, 5, 8 }, { 4, 8, 1 },
      { 8, 10, 1 }, { 8, 3, 10 }, { 5, 3, 8 }, { 5, 2, 3 }, { 2, 7, 3 },
      { 7, 10, 3 }, { 7, 6, 10 }, { 7, 11, 6 }, { 11, 0, 6 },
      { 0, 1, 6 }, { 6, 1, 10 }, { 9, 0, 11 }, { 9, 11, 2 }, { 9, 2, 5 },
      { 7, 2, 11 }, };
  private static int listnum = 0; // GLunint TZ

  private static void drawSphere() {
    // icosahedron data for an icosahedron of radius 1.0
    // # define ICX 0.525731112119133606f
    // # define ICZ 0.850650808352039932f
   
    // Beim Zeichnen mit einem PBuffer (Off-Screen-Rendering)
    // muss die Liste zum Zeichnen der Kugel immer wieder erzeugt werden
    // da der Kontext gelöscht wird und damit auch die Liste!

    boolean listExists = GL11.glIsList(listnum);
    if (!listExists || listnum == 0) { //(listnum == 0) {
      listnum = GL11.glGenLists(1);
      GL11.glNewList(listnum, GL11.GL_COMPILE);
      GL11.glBegin(GL11.GL_TRIANGLES);
      for (int i = 0; i < 20; i++) {
        // drawPatch (&idata[index[i][2]][0],&idata[index[i][1]][0],
        // &idata[index[i][0]][0],sphere_quality);
        drawPatch(idata[index[i][2]], idata[index[i][1]],
            idata[index[i][0]], sphere_quality);
      }
      GL11.glEnd();
      GL11.glEndList();
    }
    GL11.glCallList(listnum);
  }

  private static boolean init = false;
  private static float len2, len1, scale;

  @SuppressWarnings("all")
  private static void drawSphereShadow(float px, float py, float pz,
      float radius) {
    // calculate shadow constants based on light vector
    if (!init) {
      len2 = LIGHTX * LIGHTX + LIGHTY * LIGHTY;
      len1 = 1.0f / (float) Math.sqrt(len2);
      scale = (float) Math.sqrt(len2 + 1);
      init = true;
    }

    // map sphere center to ground plane based on light vector
    px -= LIGHTX * pz;
    py -= LIGHTY * pz;

    final float kx = 0.96592582628907f;
    final float ky = 0.25881904510252f;
    float x = radius, y = 0;

    GL11.glBegin(GL11.GL_TRIANGLE_FAN);
    for (int i = 0; i < 24; i++) {
      // for all points on circle, scale to elongated rotated shadow and
      // draw
      float x2 = (LIGHTX * x * scale - LIGHTY * y) * len1 + px;
      float y2 = (LIGHTY * x * scale + LIGHTX * y) * len1 + py;
      GL11.glTexCoord2f(x2 * ground_scale + ground_ofsx, y2
          * ground_scale + ground_ofsy);
      GL11.glVertex3f(x2, y2, 0);

      // rotate [x,y] vector
      float xtmp = kx * x - ky * y;
      y = ky * x + kx * y;
      x = xtmp;
    }
    GL11.glEnd();
  }

  private static void drawPatch(float[] p1, float[] p2, float[] p3, int level) {
    int i;
    if (level > 0) {
      float[] q1 = new float[3], q2 = new float[3], q3 = new float[3]; // sub-vertices
      for (i = 0; i < 3; i++) {
        q1[i] = 0.5f * (p1[i] + p2[i]);
        q2[i] = 0.5f * (p2[i] + p3[i]);
        q3[i] = 0.5f * (p3[i] + p1[i]);
      }
      float length1 = (float) (1.0 / Math.sqrt(q1[0] * q1[0] + q1[1]
          * q1[1] + q1[2] * q1[2]));
      float length2 = (float) (1.0 / Math.sqrt(q2[0] * q2[0] + q2[1]
          * q2[1] + q2[2] * q2[2]));
      float length3 = (float) (1.0 / Math.sqrt(q3[0] * q3[0] + q3[1]
          * q3[1] + q3[2] * q3[2]));
      for (i = 0; i < 3; i++) {
        q1[i] *= length1;
        q2[i] *= length2;
        q3[i] *= length3;
      }
      drawPatch(p1, q1, q3, level - 1);
      drawPatch(q1, p2, q2, level - 1);
      drawPatch(q1, q2, q3, level - 1);
      drawPatch(q3, q2, p3, level - 1);
    } else {
      GL11.glNormal3f(p1[0], p1[1], p1[2]);
      GL11.glVertex3f(p1[0], p1[1], p1[2]);
      GL11.glNormal3f(p2[0], p2[1], p2[2]);
      GL11.glVertex3f(p2[0], p2[1], p2[2]);
      GL11.glNormal3f(p3[0], p3[1], p3[2]);
      GL11.glVertex3f(p3[0], p3[1], p3[2]);
    }
  }

  // extern "C"
  // void dsDrawTriangle (final float pos[3], final float R[12],
  // final float *v0, final float *v1,
  // final float *v2, int solid)
  void dsDrawTriangle(final float[] pos, final float[] R, final float[] v0,
      final float[] v1, final float[] v2, boolean solid) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawTriangle(v0, v1, v2, solid);
    GL11.glPopMatrix();
  }

  private static void normalizeVector3(float[] v)// [3])
  {
    float len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
    if (len <= 0.0f) {
      v[0] = 1;
      v[1] = 0;
      v[2] = 0;
    } else {
      len = 1.0f / (float) Math.sqrt(len);
      v[0] *= len;
      v[1] *= len;
      v[2] *= len;
    }
  }

  @SuppressWarnings("unused")
  private static void dsDrawTriangle(final float[] pos, final float[] R,
      final float[] vAll, final int v0, final int v1, final int v2,
      boolean solid) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawTriangle(vAll, v0, v1, v2, solid);
    GL11.glPopMatrix();
  }

  @SuppressWarnings("unused")
  private void dsDrawTriangle(final DVector3C pos, final DMatrix3C R,
      final float[] vAll, final int v0, final int v1, final int v2,
      boolean solid) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawTriangle(vAll, v0, v1, v2, solid);
    GL11.glPopMatrix();
  }

  private static DoubleBuffer matrixD = BufferUtils.createDoubleBuffer(16);

  private static void setTransform(final DVector3C pos, final DMatrix3C R) {
    // GLdouble
    double[] matrix = new double[16];
    matrix[0] = R.get00();
    matrix[1] = R.get10();
    matrix[2] = R.get20();
    matrix[3] = 0;
    matrix[4] = R.get01();
    matrix[5] = R.get11();
    matrix[6] = R.get21();
    matrix[7] = 0;
    matrix[8] = R.get02();
    matrix[9] = R.get12();
    matrix[10] = R.get22();
    matrix[11] = 0;
    matrix[12] = pos.get0();
    matrix[13] = pos.get1();
    matrix[14] = pos.get2();
    matrix[15] = 1;
    matrixD.put(matrix);
    matrixD.flip();
    GL11.glPushMatrix();
    GL11.glMultMatrix(matrixD);
  }

  public static void setUseTextures(boolean a) {
    use_textures = a;
  }

  public static void setTexture(DS_TEXTURE_NUMBER texture_number) {
    tnum = texture_number;
  }

  @SuppressWarnings("unused")
  private static void dsDrawTriangle(final DVector3C pos, final DMatrix3C R,
      final float[] v0, final float[] v1, final float[] v2, boolean solid) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawTriangle(v0, v1, v2, solid);
    GL11.glPopMatrix();
  }

  // extern "C"
  // void dsDrawCylinder (final float pos[3], final float R[12],
  // float length, float radius)
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawCylinder(float[],
   * float[], float, float)
   */
  private static void dsDrawCylinder(final float[] pos, final float[] R,
      float length, float radius) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_SMOOTH);
    setTransform(pos, R);
    drawCylinder(length, radius, 0);
    GL11.glPopMatrix();

    if (use_shadows) {
      setShadowDrawingMode();
      setShadowTransform();
      setTransform(pos, R);
      drawCylinder(length, radius, 0);
      GL11.glPopMatrix();
      GL11.glPopMatrix();
      GL11.glDepthRange(0, 1);
    }
  }

  @SuppressWarnings("all")
  private static void drawCylinder(float l, float r, float zoffset) {
    int i;
    float tmp, ny, nz, a, ca, sa;
    final int n = 24; // number of sides to the cylinder (divisible by 4)

    l *= 0.5;
    a = (float) (M_PI * 2.0 / n);
    sa = (float) Math.sin(a);
    ca = (float) Math.cos(a);

    // draw cylinder body
    ny = 1;
    nz = 0; // normal vector = (0,ny,nz)
    GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    for (i = 0; i <= n; i++) {
      GL11.glNormal3d(ny, nz, 0);
      GL11.glVertex3d(ny * r, nz * r, l + zoffset);
      GL11.glNormal3d(ny, nz, 0);
      GL11.glVertex3d(ny * r, nz * r, -l + zoffset);
      // rotate ny,nz
      tmp = ca * ny - sa * nz;
      nz = sa * ny + ca * nz;
      ny = tmp;
    }
    GL11.glEnd();

    // draw top cap
    GL11.glShadeModel(GL11.GL_FLAT);
    ny = 1;
    nz = 0; // normal vector = (0,ny,nz)
    GL11.glBegin(GL11.GL_TRIANGLE_FAN);
    GL11.glNormal3d(0, 0, 1);
    GL11.glVertex3d(0, 0, l + zoffset);
    for (i = 0; i <= n; i++) {
      if (i == 1 || i == n / 2 + 1)
        setColor(color[0] * 0.75f, color[1] * 0.75f, color[2] * 0.75f,
            color[3]);
      GL11.glNormal3d(0, 0, 1);
      GL11.glVertex3d(ny * r, nz * r, l + zoffset);
      if (i == 1 || i == n / 2 + 1)
        setColor(color[0], color[1], color[2], color[3]);

      // rotate ny,nz
      tmp = ca * ny - sa * nz;
      nz = sa * ny + ca * nz;
      ny = tmp;
    }
    GL11.glEnd();

    // draw bottom cap
    ny = 1;
    nz = 0; // normal vector = (0,ny,nz)
    GL11.glBegin(GL11.GL_TRIANGLE_FAN);
    GL11.glNormal3d(0, 0, -1);
    GL11.glVertex3d(0, 0, -l + zoffset);
    for (i = 0; i <= n; i++) {
      if (i == 1 || i == n / 2 + 1)
        setColor(color[0] * 0.75f, color[1] * 0.75f, color[2] * 0.75f,
            color[3]);
      GL11.glNormal3d(0, 0, -1);
      GL11.glVertex3d(ny * r, nz * r, -l + zoffset);
      if (i == 1 || i == n / 2 + 1)
        setColor(color[0], color[1], color[2], color[3]);

      // rotate ny,nz
      tmp = ca * ny + sa * nz;
      nz = -sa * ny + ca * nz;
      ny = tmp;
    }
    GL11.glEnd();
  }

  // extern "C"
  // void dsDrawCapsule (final float pos[3], final float R[12],
  // float length, float radius)
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawCapsule(float[],
   * float[], float, float)
   */
  private static void dsDrawCapsule(final float[] pos, final float[] R,
      float length, float radius) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_SMOOTH);
    setTransform(pos, R);
    drawCapsule(length, radius);
    GL11.glPopMatrix();

    if (use_shadows) {
      setShadowDrawingMode();
      setShadowTransform();
      setTransform(pos, R);
      drawCapsule(length, radius);
      GL11.glPopMatrix();
      GL11.glPopMatrix();
      GL11.glDepthRange(0, 1);
    }
  }

  private static int capped_cylinder_quality = 3;

  @SuppressWarnings("all")
  private static void drawCapsule(float l, float r) {
    int i, j;
    float tmp, nx, ny, nz, start_nx, start_ny, a, ca, sa;
    // number of sides to the cylinder (divisible by 4):
    final int n = capped_cylinder_quality * 4;

    l *= 0.5;
    a = (float) ((M_PI * 2.0) / n);
    sa = (float) Math.sin(a);
    ca = (float) Math.cos(a);

    // draw cylinder body
    ny = 1;
    nz = 0; // normal vector = (0,ny,nz)
    GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
    for (i = 0; i <= n; i++) {
      GL11.glNormal3d(ny, nz, 0);
      GL11.glVertex3d(ny * r, nz * r, l);
      GL11.glNormal3d(ny, nz, 0);
      GL11.glVertex3d(ny * r, nz * r, -l);
      // rotate ny,nz
      tmp = ca * ny - sa * nz;
      nz = sa * ny + ca * nz;
      ny = tmp;
    }
    GL11.glEnd();

    // draw first cylinder cap
    start_nx = 0;
    start_ny = 1;
    for (j = 0; j < (n / 4); j++) {
      // get start_n2 = rotated start_n
      float start_nx2 = ca * start_nx + sa * start_ny;
      float start_ny2 = -sa * start_nx + ca * start_ny;
      // get n=start_n and n2=start_n2
      nx = start_nx;
      ny = start_ny;
      nz = 0;
      float nx2 = start_nx2, ny2 = start_ny2, nz2 = 0;
      GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
      for (i = 0; i <= n; i++) {
        GL11.glNormal3d(ny2, nz2, nx2);
        GL11.glVertex3d(ny2 * r, nz2 * r, l + nx2 * r);
        GL11.glNormal3d(ny, nz, nx);
        GL11.glVertex3d(ny * r, nz * r, l + nx * r);
        // rotate n,n2
        tmp = ca * ny - sa * nz;
        nz = sa * ny + ca * nz;
        ny = tmp;
        tmp = ca * ny2 - sa * nz2;
        nz2 = sa * ny2 + ca * nz2;
        ny2 = tmp;
      }
      GL11.glEnd();
      start_nx = start_nx2;
      start_ny = start_ny2;
    }
  }

  // void dsDrawLine (final float pos1[3], final float pos2[3])
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawLine(float[], float[])
   */
  private static void dsDrawLine(final float[] pos1, final float[] pos2) {
    setupDrawingMode();
    GL11.glColor3f(color[0], color[1], color[2]);
    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glLineWidth(2);
    GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glBegin(GL11.GL_LINES);
    GL11.glVertex3f(pos1[0], pos1[1], pos1[2]);
    GL11.glVertex3f(pos2[0], pos2[1], pos2[2]);
    GL11.glEnd();
  }

  // void dsDrawBoxD (final double pos[3], final double R[12],
  // final double sides[3])
  /**
   * @see eas.simulation.spatial.sim3D.physicalSimulation.org.ode4j.drawstuff.internal.DrawStuffApi#dsDrawBox(float[],
   *      float[], float[])
   */
  private static void dsDrawBox(DVector3C pos, DMatrix3C R, DVector3C sides) {
    float[] pos2 = pos.toFloatArray4();
    float[] R2 = R.toFloatArray12();
    float[] fsides = sides.toFloatArray4();
    dsDrawBox(pos2, R2, fsides);
  }

  // extern "C"
  // void dsDrawConvexD (final double pos[3], final double R[12],
  // double *_planes,unsigned int _planecount,
  // double *_points,
  // unsigned int _pointcount,
  // unsigned int *_polygons)
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawConvexD(double[],
   * double[], double[], int, double[], int, int[])
   */
  @SuppressWarnings("unused")
  private static void dsDrawConvex(DVector3C pos, DMatrix3C R,
      double[] _planes, int _planecount, double[] _points,
      int _pointcount, int[] _polygons) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawConvexD(_planes, _planecount, _points, _pointcount, _polygons);
    GL11.glPopMatrix();
    if (use_shadows) {
      setShadowDrawingMode();
      setShadowTransform();
      setTransform(pos, R);
      drawConvexD(_planes, _planecount, _points, _pointcount, _polygons);
      GL11.glPopMatrix();
      GL11.glPopMatrix();
      GL11.glDepthRange(0, 1);
    }
  }

  // void dsDrawSphereD (final double pos[3], final double R[12], float
  // radius)
  /**
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawSphereD(double[],
   *      double[], float)
   */
  private static void dsDrawSphere(final DVector3C pos, final DMatrix3C R,
      float radius) {
    float[] pos2 = pos.toFloatArray4();
    float[] R2 = R.toFloatArray12();
    dsDrawSphere(pos2, R2, radius);
  }

  @SuppressWarnings("unused")
  private void dsDrawTriangle(final DVector3C pos, final DMatrix3C R,
      final DVector3C v0, final DVector3C v1, final DVector3C v2,
      boolean solid) {
    setupDrawingMode();
    GL11.glShadeModel(GL11.GL_FLAT);
    setTransform(pos, R);
    drawTriangle(v0, v1, v2, solid);
    GL11.glPopMatrix();
  }

  private void drawTriangle(final DVector3C v0, final DVector3C v1,
      final DVector3C v2, boolean solid) {
    float[] u = new float[3], v = new float[3], normal = new float[3];
    u[0] = (float) (v1.get0() - v0.get0());
    u[1] = (float) (v1.get1() - v0.get1());
    u[2] = (float) (v1.get2() - v0.get2());
    v[0] = (float) (v2.get0() - v0.get0());
    v[1] = (float) (v2.get1() - v0.get1());
    v[2] = (float) (v2.get2() - v0.get2());
    OdeMath.dCROSS(normal, OP.EQ, u, v);
    normalizeVector3(normal);

    GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
    GL11.glNormal3f(normal[0], normal[1], normal[2]);
    GL11.glVertex3d(v0.get0(), v0.get1(), v0.get2());
    GL11.glVertex3d(v1.get0(), v1.get1(), v1.get2());
    GL11.glVertex3d(v2.get0(), v2.get1(), v2.get2());
    GL11.glEnd();
  }

  private static void drawTriangle(final float[] v0, final float[] v1,
      final float[] v2, boolean solid) {
    float[] u = new float[3], v = new float[3], normal = new float[3];
    u[0] = v1[0] - v0[0];
    u[1] = v1[1] - v0[1];
    u[2] = v1[2] - v0[2];
    v[0] = v2[0] - v0[0];
    v[1] = v2[1] - v0[1];
    v[2] = v2[2] - v0[2];
    OdeMath.dCROSS(normal, OP.EQ, u, v);
    normalizeVector3(normal);

    GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
    GL11.glNormal3f(normal[0], normal[1], normal[2]);
    GL11.glVertex3f(v0[0], v0[1], v0[2]);
    GL11.glVertex3f(v1[0], v1[1], v1[2]);
    GL11.glVertex3f(v2[0], v2[1], v2[2]);
    GL11.glEnd();
  }

  private static void drawTriangle(final float[] vAll, final int v0,
      final int v1, final int v2, boolean solid) {
    float[] u = new float[3], v = new float[3], normal = new float[3];
    u[0] = vAll[v1] - vAll[v0];// v1[0] - v0[0];
    u[1] = vAll[v1 + 1] - vAll[v0 + 1];// v1[1] - v0[1];
    u[2] = vAll[v1 + 2] - vAll[v0 + 2];// v1[2] - v0[2];
    v[0] = vAll[v2] - vAll[v0];// v2[0] - v0[0];
    v[1] = vAll[v2 + 1] - vAll[v0 + 1];// v2[1] - v0[1];
    v[2] = vAll[v2 + 2] - vAll[v0 + 2];// v2[2] - v0[2];
    OdeMath.dCROSS(normal, OP.EQ, u, v);
    normalizeVector3(normal);

    GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
    GL11.glNormal3f(normal[0], normal[1], normal[2]);
    GL11.glVertex3f(vAll[v0], vAll[v0 + 1], vAll[v0 + 2]);// , v0[0], v0[1],
                                // v0[2]);
    GL11.glVertex3f(vAll[v1], vAll[v1 + 1], vAll[v1 + 2]);// v1[0], v1[1],
                                // v1[2]);
    GL11.glVertex3f(vAll[v2], vAll[v2 + 1], vAll[v2 + 2]);// v2[0], v2[1],
                                // v2[2]);
    GL11.glEnd();
  }

  // void dsDrawCapsuleD (final double pos[3], final double R[12],
  // float length, float radius)
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawCapsuleD(double[],
   * double[], float, float)
   */
  private static void dsDrawCapsule(final DVector3C pos, final DMatrix3C R,
      float length, float radius) {
    float[] pos2 = pos.toFloatArray4();
    float[] R2 = R.toFloatArray12();
    dsDrawCapsule(pos2, R2, length, radius);
  }

  // void dsDrawLineD (final double _pos1[3], final double _pos2[3])
  /*
   * (non-Javadoc)
   *
   * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawLineD(double[],
   * double[])
   */
  @SuppressWarnings("unused")
  private void dsDrawLine(final DVector3C _pos1, final DVector3C _pos2) {
    float[] pos1 = _pos1.toFloatArray4();
    float[] pos2 = _pos2.toFloatArray4();
    dsDrawLine(pos1, pos2);
  }

  @SuppressWarnings("unused")
  private void dsSetSphereQuality(int n) {
    sphere_quality = n;
  }

  @SuppressWarnings("unused")
  private void dsSetCapsuleQuality(int n) {
    capped_cylinder_quality = n;
  }

  public static final int DS_POLYFILL = 0;
  // #define DS_WIREFRAME 1
  public static final int DS_WIREFRAME = 1;

  /**
   * Sets draw mode. Choose between DS_POLYFILL (filled polygons) and
   * DS_WIREFRAME (wireframe models);
   *
   * @param mode
   *            Drawing mode.
   */
  public static void setDrawMode(int mode) {
    switch (mode) {
    case DS_POLYFILL:
      GL11.glPolygonMode(GL11.GL_FRONT, GL11.GL_FILL);
      break;
    case DS_WIREFRAME:
      GL11.glPolygonMode(GL11.GL_FRONT, GL11.GL_LINE);
      break;
    }
  }

  private static void dsDrawCylinder(final DVector3C pos, final DMatrix3C R,
      float length, float radius) {
    float[] pos2 = pos.toFloatArray4();
    float[] R2 = R.toFloatArray12();
    dsDrawCylinder(pos2, R2, length, radius);
  }

  /**
   * Call this method to draw a geometry.
   *
   * @param g
   *            Geometry to be drawn.
   */
  public static void drawGeom(DGeom g) {
    DVector3C pos = g.getPosition();
    DMatrix3C R = g.getRotation();

    if (g instanceof DBox) {
      DVector3C sides = ((DBox) g).getLengths();
      dsDrawBox(pos, R, sides);
    } else if (g instanceof DCylinder) {
      double r = ((DCylinder) g).getRadius();
      double l = ((DCylinder) g).getLength();
      dsDrawCylinder(pos, R, (float) l, (float) r);
    } else if (g instanceof DSphere) {
      dsDrawSphere(pos, R, (float) ((DSphere) g).getRadius());
    } else if (g instanceof DCapsule) {
      DCapsule cap = (DCapsule) g;
      dsDrawCapsule(pos, R, (float) cap.getLength(),
          (float) cap.getRadius());
    } else {
      System.err
          .println("!!!!!!!!!!!!!!!!!!!!!!! GEOM NOT SUPPORTED. Add a method to public static void drawGeom !!!!!!!!!!!!!");
    }
  }

  /**
   * True enables drawing of ground, false disables it.
   *
   * @param drawGround
   *            Whether to draw the ground or not.
   */
  public static void enableGroundDrawing(boolean drawGround) {
    enableGround = drawGround;
  }

  /**
   * Moves camera by specified values.
   *
   * @param x
   *            Moves camera specified value in x-direction.
   * @param y
   *            Moves camera specified value in y-direction.
   * @param z
   *            Moves camera specified value in z-direction.
   */
  public static void moveCameraPosition(double x, double y, double z) {
    cam_pos[0] = cam_pos[0] + (float) x;
    cam_pos[1] = cam_pos[1] + (float) y;
    cam_pos[2] = cam_pos[2] + (float) z;
  }

  /**
   * Rotates camera by specified values (in degrees).
   *
   * @param h
   *            Rotate heading.
   * @param p
   *            Rotate pitch.
   * @param r
   *            Rotate roll.
   */
  public static void rotateCamera(double h, double p, double r) {
    view_hpr[0] = view_hpr[0] + (float) h;
    view_hpr[1] = view_hpr[1] + (float) p;
    view_hpr[2] = view_hpr[2] + (float) r;
    wrapCameraAngles();
  }

  /**
   * Look from the specified cam position at the specified target position.
   *
   * @param camPosX
   *            Cam pos x.
   * @param camPosY
   *            Cam pos y.
   * @param camPosZ
   *            Cam pos z.
   * @param observePosX
   *            Target pos x.
   * @param observePosY
   *            Target pos y.
   * @param observePosZ
   *            Target pos z.
   */
  public static void setCameraToLookAtObjectFromSpecifiedPosition(
      double camPosX, double camPosY, double camPosZ, double observePosX,
      double observePosY, double observePosZ) {
    // cam_pos[0] = (float) camPosX;
    // cam_pos[1] = (float) camPosY;
    // cam_pos[2] = (float) camPosZ;

    cam_pos[0] = (float) camPosX;
    cam_pos[1] = (float) camPosY;
    cam_pos[2] = (float) camPosZ;

    look_at_pos[0] = (float) observePosX;
    look_at_pos[1] = (float) observePosY;
    look_at_pos[2] = (float) observePosZ;

    setCamMode(CAMERA_MODE.LOOK_AT_CAMERA);
    // enableGluLookAt = true;
  }

  /**
   * Looks at the specified position from a certain distance.
   *
   * @param observePosX
   *            Target pos x.
   * @param observePosY
   *            Target pos y.
   * @param observePosZ
   *            Target pos z.
   * @param distance
   *            Distance.
   */
  public static void setCameraToLookAtPositionFromSpecifiedDistance(
      double observePosX, double observePosY, double observePosZ,
      double distance) {
    look_at_pos[0] = (float) observePosX;
    look_at_pos[1] = (float) observePosY;
    look_at_pos[2] = (float) observePosZ;

    //
    // cam_pos[0] = (float) (observePosX + distance);
    // cam_pos[1] = (float) (observePosY);
    // cam_pos[2] = (float) (observePosZ + distance/2);

    cam_pos[0] = (float) (observePosX + distance);
    cam_pos[1] = (float) (observePosY);
    cam_pos[2] = (float) (observePosZ + distance / 2);

    /*
     * cam_pos[0] = (float) (observePosX + distance); cam_pos[1] = (float)
     * (observePosY + distance); cam_pos[2] = (float) (observePosZ +
     * distance/2);
     */

    setCamMode(CAMERA_MODE.LOOK_AT_CAMERA);
  }

  /**
   * Looks at the specified position from a certain distance while rotating
   * around it.
   *
   * @param observePosX
   *            Target pos x.
   * @param observePosY
   *            Target pos y.
   * @param observePosZ
   *            Target pos z.
   * @param distance
   *            Distance.
   * @param numberOfSteps
   *            Number of method calls until one rotation is fulfilled.
   */
  public static void setRotatingCameraToLookAtObjectFromSpecifiedDistance(
      double observePosX, double observePosY, double observePosZ,
      double distance, int numberOfSteps) {
    look_at_pos[0] = (float) observePosX;
    look_at_pos[1] = (float) observePosY;
    look_at_pos[2] = (float) observePosZ;

    double xDistance = Math.sin(2 * M_PI * rotationCounter / numberOfSteps)
        * distance;
    double yDistance = Math.cos(2 * M_PI * rotationCounter / numberOfSteps)
        * distance;

    // cam_pos[0] = (float) (observePosX + xDistance);
    // cam_pos[1] = (float) (observePosY + yDistance);
    // cam_pos[2] = (float) (observePosZ + distance/2);

    cam_pos[0] = (float) (observePosX + xDistance);
    cam_pos[1] = (float) (observePosY + yDistance);
    cam_pos[2] = (float) (observePosZ + distance / 2);

    rotationCounter++;
    setCamMode(CAMERA_MODE.LOOK_AT_CAMERA);
  }

  public static void setEnvironment(AbstractEnvironmentLWJGLdrawable<?> env) {
    environment = env;
  }

  /**
   * Returns a BufferedImage of the current scene. Use with care (slows down
   * the simulation!). Does not open an OpenGL-window!
   *
   * @return BufferedImage of the current scene.
   */
  public static BufferedImage getBufferedImage() {
    if (!openGLWindowOpen) {
      // if (!pbufferInitialized || pbuffer.isBufferLost()) {
      // // Use Pbuffer to render in background.
      // // No OpenGL-window is produced.
      // // Pbuffer pbuffer = null;
      width = 800;
      height = 600;
      try {

        pbuffer = new Pbuffer(width, height, new PixelFormat(), null,
            null);
        if (pbuffer.isBufferLost()) {
          System.err.println("PBuffer is lost!");
        }
        pbuffer.makeCurrent();

      } catch (LWJGLException e1) {
        System.err
            .println("An error occured when creating the PBuffer... ");
        System.err.println(e1.getMessage());
      }

      VideoPluginLWJGL.loadTextures();
      // pbufferInitialized = true;
      // }

      GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
      enableGround = true;
      enableSky = true;

      dsDrawFrame();
      for (int i = 0; i < environment.getAgents().size(); i++) {
        AbstractAgentLWJGLdrawable<?> agent = environment.getAgents()
            .get(i);
        if (agent.isDrawableInLWJGL()) {
          agent.drawInLWJGL();
        }
      }
      try {
        pbuffer.swapBuffers();
      } catch (LWJGLException e) {
        System.err
            .println("An error occured when swapping the PBuffer... ");
        System.err.println(e.getMessage());
      }
    }

    // //////////////////////////////////////
    // Create BufferedImage from Pbuffer. //
    // //////////////////////////////////////
    // Size of BufferedImage.
    int frameWidth = width;
    int frameHeight = height;
    // Create new BufferedImage.
    BufferedImage bufferedImage = new BufferedImage(frameWidth,
        frameHeight, BufferedImage.TYPE_INT_RGB);
    // 3 bytes per pixel: red, green, blue (no alpha!)
    int bytesPerPixel = 3;
    // Allocate necessary buffer space.
    ByteBuffer buffer = BufferUtils.createByteBuffer(width * height
        * bytesPerPixel);
    // Read the buffer.
    GL11.glReadPixels(0, 0, frameWidth, frameHeight, GL11.GL_RGB,
        GL11.GL_UNSIGNED_BYTE, buffer);
    // Convert the buffer into a BufferedImage.
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        int i = (x + (width * y)) * bytesPerPixel;
        int r = buffer.get(i) & 0xFF;
        int g = buffer.get(i + 1) & 0xFF;
        int b = buffer.get(i + 2) & 0xFF;
        // Flip image vertically and shift the bytes.
        bufferedImage.setRGB(x, height - (y + 1), (0xFF << 24)
            | (r << 16) | (g << 8) | b);
      }
    }
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
    if (!openGLWindowOpen) {
      pbuffer.destroy();
    }
    // pbufferInitialized = false;
    return bufferedImage;
  }

  public static void drawClothAsPatches(DBody[][] bodymatrix) {
    GL11.glEnable(GL11.GL_LIGHTING);

    FloatBuffer fb1 = BufferUtils.createFloatBuffer(4);
    fb1.put(new float[] { .8f, .0f, 1.f, 1.f }).flip();
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, fb1);
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, fb1);

    FloatBuffer fb2 = BufferUtils.createFloatBuffer(4);
    fb2.put(new float[] { .8f, .8f, 0f, 1.f }).flip();
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_AMBIENT, fb2);
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_DIFFUSE, fb2);

    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
    GL11.glFrontFace(GL11.GL_CW);
    GL11.glDisable(GL11.GL_CULL_FACE);
    GL11.glEnable(GL11.GL_MAP2_VERTEX_3);
    GL11.glEnable(GL11.GL_AUTO_NORMAL);

    // Extract control points

    int size = bodymatrix.length;
    FloatBuffer pos = BufferUtils.createFloatBuffer(bodymatrix.length
        * bodymatrix.length * 3);

    for (int i = 0; i < bodymatrix.length; i++) {
      for (int j = 0; j < bodymatrix.length; j++) {
        DVector3C vec0 = bodymatrix[i][j].getPosition();
        float[] f0 = vec0.toFloatArray();
        pos.put(f0);
      }
    }
    pos.rewind();

    GL11.glMap2f(GL11.GL_MAP2_VERTEX_3, 0, 1, size * 3, size, 0, 1, 3,
        size, pos);
    GL11.glMapGrid2f(40, 0, 1, 40, 0, 1);
    GL11.glEvalMesh2(GL11.GL_FILL, 0, 40, 0, 40);

    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glDisable(GL11.GL_MAP2_VERTEX_3);
    // GL11.glDisable(GL11.GL_MAP2_NORMAL);
    GL11.glFrontFace(GL11.GL_CCW);
  }

  public static void drawClothAsPatchesBAK(DBody[][] bodymatrix) {
    GL11.glEnable(GL11.GL_LIGHTING);

    FloatBuffer fb1 = BufferUtils.createFloatBuffer(4);
    fb1.put(new float[] { .8f, .0f, 1.f, 1.f }).flip();
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, fb1);
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, fb1);

    FloatBuffer fb2 = BufferUtils.createFloatBuffer(4);
    fb2.put(new float[] { .8f, .8f, 0f, 1.f }).flip();
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_AMBIENT, fb2);
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_DIFFUSE, fb2);

    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
    GL11.glFrontFace(GL11.GL_CW);
    GL11.glDisable(GL11.GL_CULL_FACE);
    GL11.glEnable(GL11.GL_MAP2_VERTEX_3);
    GL11.glEnable(GL11.GL_AUTO_NORMAL);
    // GL11.glEnable(GL11.GL_MAP2_NORMAL);

    int size = bodymatrix.length;
    FloatBuffer pos = BufferUtils.createFloatBuffer(bodymatrix.length
        * bodymatrix.length * 3);

    for (int i = 0; i < bodymatrix.length; i++) {
      for (int j = 0; j < bodymatrix.length; j++) {
        DVector3C vec0 = bodymatrix[i][j].getPosition();
        float[] f0 = vec0.toFloatArray();
        pos.put(f0);
      }
    }
    pos.rewind();

    GL11.glMap2f(GL11.GL_MAP2_VERTEX_3, 0, 1, size * 3, size, 0, 1, 3,
        size, pos);
    GL11.glMapGrid2f(40, 0, 1, 40, 0, 1);
    GL11.glEvalMesh2(GL11.GL_FILL, 0, 40, 0, 40);

    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glDisable(GL11.GL_MAP2_VERTEX_3);
    // GL11.glDisable(GL11.GL_MAP2_NORMAL);
    GL11.glFrontFace(GL11.GL_CCW);
  }

  public static void drawCloth(DBody[][] bodies) {

    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
    GL11.glDisable(GL11.GL_CULL_FACE);
    GL11.glEnable(GL11.GL_LIGHTING);
    GL11.glShadeModel(GL11.GL_SMOOTH);

    FloatBuffer color = BufferUtils.createFloatBuffer(4);
    color.put(new float[] { .8f, .0f, .8f, 1 }).flip();
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, color); // set material
    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, color);

    FloatBuffer color2 = BufferUtils.createFloatBuffer(4);
    color2.put(new float[] { 1f, .8f, 0, 1 }).flip();
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_AMBIENT, color2);
    GL11.glMaterial(GL11.GL_BACK, GL11.GL_DIFFUSE, color2);

    GL11.glEnable(GL11.GL_NORMALIZE);
    GL11.glBegin(GL11.GL_TRIANGLES);
    for (int i = 0; i < bodies.length - 1; ++i) {
      for (int j = 0; j < bodies[i].length - 1; ++j) {

        DVector3C vec0 = bodies[i][j].getPosition();
        DVector3C vec1 = bodies[i + 1][j].getPosition();
        DVector3C vec2 = bodies[i][j + 1].getPosition();
        DVector3C vec3 = bodies[i + 1][j + 1].getPosition();

        DVector3C temp1 = new DVector3(vec1.get0() - vec0.get0(),
            vec1.get1() - vec0.get1(), vec1.get2() - vec0.get2());
        DVector3C temp2 = new DVector3(vec2.get0() - vec0.get0(),
            vec2.get1() - vec0.get1(), vec2.get2() - vec0.get2());

        DVector3C temp3 = new DVector3(vec1.get0() - vec2.get0(),
            vec1.get1() - vec2.get1(), vec1.get2() - vec2.get2());
        DVector3C temp4 = new DVector3(vec3.get0() - vec2.get0(),
            vec3.get1() - vec2.get1(), vec3.get2() - vec2.get2());

        DVector3C normal = new DVector3(temp1.get1() * temp2.get2()
            - temp1.get2() * temp2.get1(), temp1.get2()
            * temp2.get0() - temp1.get0() * temp2.get2(),
            temp1.get0() * temp2.get1() - temp1.get1()
                * temp2.get0());
        DVector3C normal2 = new DVector3(temp3.get1() * temp4.get2()
            - temp3.get2() * temp4.get1(), temp3.get2()
            * temp4.get0() - temp3.get0() * temp4.get2(),
            temp3.get0() * temp4.get1() - temp3.get1()
                * temp4.get0());

        DVector3C norm0 = normal.clone();
        DVector3C norm1 = normal.clone();
        DVector3C norm2 = normal.clone();
        DVector3C norm3 = normal.clone();

        norm1 = new DVector3(norm1.get0() + normal2.get0(),
            norm1.get1() + normal2.get1(), norm1.get2()
                + normal2.get2());
        norm2 = new DVector3(norm2.get0() + normal2.get0(),
            norm2.get1() + normal2.get1(), norm2.get2()
                + normal2.get2());
        norm3 = new DVector3(norm3.get0() + normal2.get0(),
            norm3.get1() + normal2.get1(), norm3.get2()
                + normal2.get2());

        GL11.glNormal3d(norm0.get0(), norm0.get1(), norm0.get2());
        GL11.glVertex3d(vec0.get0(), vec0.get1(), vec0.get2());
        GL11.glNormal3d(norm1.get0(), norm1.get1(), norm1.get2());
        GL11.glVertex3d(vec1.get0(), vec1.get1(), vec1.get2());
        GL11.glNormal3d(norm2.get0(), norm2.get1(), norm2.get2());
        GL11.glVertex3d(vec2.get0(), vec2.get1(), vec2.get2());

        GL11.glNormal3d(norm2.get0(), norm2.get1(), norm2.get2());
        GL11.glVertex3d(vec2.get0(), vec2.get1(), vec2.get2());
        GL11.glNormal3d(norm1.get0(), norm1.get1(), norm1.get2());
        GL11.glVertex3d(vec1.get0(), vec1.get1(), vec1.get2());
        GL11.glNormal3d(norm3.get0(), norm3.get1(), norm3.get2());
        GL11.glVertex3d(vec3.get0(), vec3.get1(), vec3.get2());
      }
    }
    GL11.glEnd();
    GL11.glDisable(GL11.GL_NORMALIZE);

    GL11.glDisable(GL11.GL_LIGHTING);
    // GL11.glEnable(GL11.GL_CULL_FACE);
    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_FALSE);

    // / GRID
    GL11.glDisable(GL11.GL_LIGHTING);
    GL11.glLineWidth(2);
    GL11.glEnable(GL11.GL_LINE_STIPPLE);
    // GL11.glShadeModel(GL11.GL_FLAT);
    GL11.glColor3f(.085f, .085f, .085f);
    GL11.glBegin(GL11.GL_LINES);

    for (int i = 0; i < bodies.length - 1; ++i) {
      for (int j = 0; j < bodies[i].length - 1; ++j) {
        DVector3C vec0 = bodies[i][j].getPosition();
        DVector3C vec1 = bodies[i][j + 1].getPosition();
        DVector3C vec2 = bodies[i + 1][j].getPosition();
        GL11.glVertex3d(vec0.get0(), vec0.get1(), vec0.get2());
        GL11.glVertex3d(vec1.get0(), vec1.get1(), vec1.get2());

        GL11.glVertex3d(vec0.get0(), vec0.get1(), vec0.get2());
        GL11.glVertex3d(vec2.get0(), vec2.get1(), vec2.get2());
      }
    }
    for (int i = 0; i < bodies.length - 1; i++) {
      DVector3C vec0 = bodies[i][bodies[i].length - 1].getPosition();
      DVector3C vec1 = bodies[i + 1][bodies[i + 1].length - 1]
          .getPosition();
      GL11.glVertex3d(vec0.get0(), vec0.get1(), vec0.get2());
      GL11.glVertex3d(vec1.get0(), vec1.get1(), vec1.get2());
    }
    for (int i = 0; i < bodies.length - 1; i++) {
      DVector3C vec0 = bodies[bodies[i].length - 1][i].getPosition();
      DVector3C vec1 = bodies[bodies[i + 1].length - 1][i + 1]
          .getPosition();
      GL11.glVertex3d(vec0.get0(), vec0.get1(), vec0.get2());
      GL11.glVertex3d(vec1.get0(), vec1.get1(), vec1.get2());
    }
    GL11.glEnd();
  }

  @Override
  public List<String> getSupportedPlugins() {
    return null;
  }

    @Override
    public void onSimulationResumed(AbstractEnvironmentLWJGLdrawable<AbstractAgentLWJGLdrawable<?>> env,
            Wink resumeTime, ParCollection params) {
       
    }
}
TOP

Related Classes of eas.plugins.standard.visualization.visualization3D.VideoPluginLWJGL

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.