org.ode4j.drawstuff.internal.LwJGL.java Source code

Java tutorial

Introduction

Here is the source code for org.ode4j.drawstuff.internal.LwJGL.java

Source

/*************************************************************************
 *                                                                       *
 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.       *
 * All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
 * Open Dynamics Engine 4J, Copyright (C) 2009-2014 Tilmann Zaeschke     *
 * All rights reserved.  Email: ode4j@gmx.de   Web: www.ode4j.org        *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of EITHER:                                  *
 *   (1) The GNU Lesser General Public License as published by the Free  *
 *       Software Foundation; either version 2.1 of the License, or (at  *
 *       your option) any later version. The text of the GNU Lesser      *
 *       General Public License is included with this library in the     *
 *       file LICENSE.TXT.                                               *
 *   (2) The BSD-style license that is included with this library in     *
 *       the file ODE-LICENSE-BSD.TXT and ODE4J-LICENSE-BSD.TXT.         *
 *                                                                       *
 * This library is distributed in the hope that it will be useful,       *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
 * LICENSE.TXT, ODE-LICENSE-BSD.TXT and ODE4J-LICENSE-BSD.TXT for more   *
 * details.                                                              *
 *                                                                       *
 *************************************************************************/
package org.ode4j.drawstuff.internal;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
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.GLContext;
import org.ode4j.drawstuff.DrawStuff;
import org.ode4j.drawstuff.DrawStuff.dsFunctions;
import org.ode4j.ode.OdeHelper;
import org.ode4j.ode.internal.Common;

import static org.ode4j.ode.internal.cpp4j.Cstdio.*;

/**
 * Main window and event handling for LWJGL.
 * Ported from x11.cpp.
 */
abstract class LwJGL extends Internal implements DrawStuffApi {

    //Ensure that Display.destroy() is called (TZ)
    //Not sure this works, but it's an attempt at least.
    //-> This should avoid the Problem that a process keeps running with 99%CPU, 
    //   even if the window is closed (clicking on the 'x'). The supposed 
    //   problem is that when clicking 'x', Display.destroy() never gets called
    //   by dsPlatformSimLoop(). 
    static {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                //            Display.destroy();
            }
        });
    }

    //***************************************************************************
    // error handling for unix

    //static void printMessage (const char *msg1, const char *msg2, va_list ap)
    private static void printMessage(String msg1, String fmt, Object... ap) {
        fflush(stderr);
        fflush(stdout);
        fprintf(stderr, "\n%s: ", msg1);
        vfprintf(stderr, fmt, ap);
        fprintf(stderr, "\n");
        fflush(stderr);
    }

    //extern "C" void dsError (const char *msg, ...)
    static void dsError(String msg, Object... ap) {
        //  va_list ap;
        //  va_start (ap,msg);
        printMessage("Error", msg, ap);
        //TZ exit (1);
        throw new RuntimeException();
    }

    //extern "C" void dsDebug (const char *msg, ...)
    static void dsDebug(String msg, Object... ap) {
        //  va_list ap;
        //  va_start (ap,msg);
        printMessage("INTERNAL ERROR", msg, ap);
        // *((char *)0) = 0;    ... commit SEGVicide ?
        //TZ abort();
        throw new RuntimeException();
    }

    //extern "C" void dsPrint (const char *msg, ...)
    static void dsPrint(String msg, Object... ap) {
        //  va_list ap;
        //  va_start (ap,msg);
        vprintf(msg, ap);
    }

    //***************************************************************************
    // openGL window

    // X11 display info
    //static Display display;//*display=0;
    //private static int screen=0;
    //static XVisualInfo visual;//*visual=0;      // best visual for openGL
    //static Colormap colormap=null;      // window's colormap
    //static Atom wm_protocols_atom = null;
    //static Atom wm_delete_window_atom = null;

    // window and openGL
    //static Window win=null;         // X11 window, 0 if not initialized
    private static int width = 0, height = 0; // window size
    //static GLXContext glx_context=null;   // openGL rendering context
    private static int last_key_pressed = 0; // last key pressed in the window
    private static boolean run = true; // 1 if simulation running
    private static boolean pause = false; // 1 if in `pause' mode
    private static boolean singlestep = false; // 1 if single step key pressed
    private static boolean writeframes = false; // 1 if frame files to be written

    private static void createMainWindow(int _width, int _height) {
        // create Window of size 300x300
        try {
            Display.setLocation((Display.getDisplayMode().getWidth() - _width) / 2,
                    (Display.getDisplayMode().getHeight() - _height) / 2);
        } catch (UnsatisfiedLinkError e) {
            System.err.println("Missing lwjgl native libraries.");
            System.err.println("If you are using maven, make sure to use "
                    + "'-Djava.library.path=target/natives' as VM argument of your application.");
            System.err.println("For plain Eclipse, add the native library path to the included "
                    + "lwjgl.jar in the definition of the Referenced Libraries.");
            throw e;
        }
        try {
            Display.setDisplayMode(new DisplayMode(_width, _height));
            Display.setTitle("Simulation");
            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);
        }

        if (firsttime) {
            System.err.println("GL_VENDOR:     " + GL11.glGetString(GL11.GL_VENDOR));
            System.err.println("GL_RENDERER:   " + GL11.glGetString(GL11.GL_RENDERER));
            System.err.println("GL_VERSION:    " + GL11.glGetString(GL11.GL_VERSION));
            System.err.println("LWJGL_VERSION: " + Sys.getVersion());
            System.err.println();
            System.err.println("glLoadTransposeMatrixfARB() supported: "
                    + GLContext.getCapabilities().GL_ARB_transpose_matrix);
        }

        //   // create X11 display connection
        //  display = XOpenDisplay (null);
        //  if (!display) dsError ("can not open X11 display");
        //  screen = DefaultScreen(display);
        //
        //  // get GL visual
        //  static int attribList[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE,16,
        //              GLX_RED_SIZE,4, GLX_GREEN_SIZE,4,
        //              GLX_BLUE_SIZE,4, None};
        //  visual = glXChooseVisual (display,screen,attribList);
        //  if (!visual) dsError ("no good X11 visual found for OpenGL");

        // create colormap
        //  colormap = XCreateColormap (display,RootWindow(display,screen),
        //               visual.visual,AllocNone);

        // initialize variables
        //  win = 0;
        width = _width;
        height = _height;
        //  glx_context = 0;
        last_key_pressed = 0;

        if (width < 1 || height < 1)
            dsDebug("", "bad window width or height");

        // create the window
        //  XSetWindowAttributes attributes;
        //  attributes.background_pixel = BlackPixel(display,screen);
        //  attributes.colormap = colormap;
        //  attributes.event_mask = ButtonPressMask | ButtonReleaseMask |
        //    KeyPressMask | KeyReleaseMask | ButtonMotionMask | PointerMotionHintMask |
        //    StructureNotifyMask;
        //  win = XCreateWindow (display,RootWindow(display,screen),50,50,width,height,
        //             0,visual.depth, InputOutput,visual.visual,
        //             CWBackPixel | CWColormap | CWEventMask, attributes);

        // associate a GLX context with the window
        //  glx_context = glXCreateContext (display,visual,0,GL_TRUE);
        //  if (!glx_context) dsError ("can't make an OpenGL context");

        // set the window title
        //  XTextProperty window_name;
        //  window_name.value = "Simulation";//(unsigned char *) "Simulation";
        //  window_name.encoding = XA_STRING;
        //  window_name.format = 8;
        //  window_name.nitems = window_name.value.length;//strlen((char *) window_name.value);
        //  XSetWMName (display,win,window_name);

        // participate in the window manager 'delete yourself' protocol
        //  wm_protocols_atom = XInternAtom (display,"WM_PROTOCOLS",False);
        //  wm_delete_window_atom = XInternAtom (display,"WM_DELETE_WINDOW",False);
        //  if (XSetWMProtocols (display,win,wm_delete_window_atom,1)==0)
        //    dsError ("XSetWMProtocols() call failed");

        // pop up the window
        //  XMapWindow (display,win);
        //  XSync (display,win);
    }

    private static void destroyMainWindow() {
        //  glXDestroyContext (display,glx_context);
        //  XDestroyWindow (display,win);
        //  XSync (display,0);
        //  XCloseDisplay(display);
        //  display = 0;
        //  win = 0;
        //  glx_context = 0;
        Keyboard.destroy();
        Mouse.destroy();
        Display.destroy();
    }

    //   private static int mx=0,my=0;    // mouse position
    //   private static int mode = 0;      // mouse button bits
    //static void handleEvent (XEvent &event, dsFunctions *fn)
    //   static void handleEvent (XEvent event, dsFunctions fn)
    //   {
    //      //TZ  static int mx=0,my=0;    // mouse position
    //      //TZ  static int mode = 0;      // mouse button bits

    //      switch (event.type) {
    //
    //      case ButtonPress: {
    //         if (event.xbutton.button == Button1) mode |= 1;
    //         if (event.xbutton.button == Button2) mode |= 2;
    //         if (event.xbutton.button == Button3) mode |= 4;
    //         mx = event.xbutton.x;
    //         my = event.xbutton.y;
    //      }
    //      return;
    //
    //      case ButtonRelease: {
    //         if (event.xbutton.button == Button1) mode &= (~1);
    //         if (event.xbutton.button == Button2) mode &= (~2);
    //         if (event.xbutton.button == Button3) mode &= (~4);
    //         mx = event.xbutton.x;
    //         my = event.xbutton.x;
    //      }
    //      return;
    //
    //      case MotionNotify: {
    //         if (event.xmotion.is_hint) {
    //            Window root,child;
    //            //unsigned 
    //            int mask;
    //            XQueryPointer (display,win,root,child,event.xbutton.x_root,
    //                  event.xbutton.y_root,event.xbutton.x,event.xbutton.y,
    //                  mask);
    //         }
    //         dsMotion (mode, event.xmotion.x - mx, event.xmotion.y - my);
    //         mx = event.xmotion.x;
    //         my = event.xmotion.y;
    //      }
    //      return;

    //Moved to handleKeyboard() TZ
    //      case KeyPress: {
    //         KeySym key;
    //         XLookupString (event.xkey,NULL,0,key,0);
    //         if ((event.xkey.state & ControlMask) == 0) {
    //            if (key >= ' ' && key <= 126 && fn.command) fn.command (key);
    //         }
    //         else if (event.xkey.state & ControlMask) {
    //            switch (key) {
    //            case 't': case 'T':
    //               dsSetTextures (dsGetTextures() ^ 1);
    //               break;
    //            case 's': case 'S':
    //               dsSetShadows (dsGetShadows() ^ 1);
    //               break;
    //            case 'x': case 'X':
    //               run = 0;
    //               break;
    //            case 'p': case 'P':
    //               pause ^= 1;
    //               singlestep = 0;
    //               break;
    //            case 'o': case 'O':
    //               if (pause) singlestep = 1;
    //               break;
    //            case 'v': case 'V': {
    //               float[] xyz=new float [3], hpr = new float [3];
    //               dsGetViewpoint (xyz,hpr);
    //               printf ("Viewpoint = (%.4f,%.4f,%.4f,%.4f,%.4f,%.4f)\n",
    //                     xyz[0],xyz[1],xyz[2],hpr[0],hpr[1],hpr[2]);
    //               break;
    //            }
    //            case 'w': case 'W':
    //               writeframes ^= 1;
    //               if (writeframes) printf ("Now writing frames to PPM files\n");
    //               break;
    //            }
    //         }
    //         last_key_pressed = key;      // a kludgy place to put this...
    //      }
    //      return;
    //
    //      case KeyRelease: {
    //         // hmmmm...
    //      }
    //      return;

    //      case ClientMessage:
    //         if (event.xclient.message_type == wm_protocols_atom &&
    //               event.xclient.format == 32 &&
    //               Atom(event.xclient.data.l[0]) == wm_delete_window_atom) {
    //            run = 0;
    //            return;
    //         }
    //         return;
    //
    //      case ConfigureNotify:
    //         width = event.xconfigure.width;
    //         height = event.xconfigure.height;
    //         return;
    //      }
    //   }

    //   // return the index of the highest bit
    //   //static int getHighBitIndex (unsigned int x)
    //   private static int getHighBitIndex (int x)
    //   {
    //      int i = 0;
    //      while (x!=0) {
    //         i++;
    //         x >>= 1;
    //      }
    //      return i-1;
    //   }
    //
    //
    //   // shift x left by i, where i can be positive or negative
    //   //#define SHIFTL(x,i) (((i) >= 0) ? ((x) << (i)) : ((x) >> (-i)))
    //   //int? double?
    //   private final int SHIFTL(long x, int i) { 
    //      return (int) ((i >= 0) ? (x << (i)) : ((x) >> (-i))); 
    //   }

    private static void captureFrame(int num) {
        throw new UnsupportedOperationException();
        //  fprintf (stderr,"capturing frame %04d\n",num);
        //
        //  char s[100];
        //  sprintf (s,"frame/frame%04d.ppm",num);
        //  FILE *f = fopen (s,"wb");
        //  if (!f) dsError ("can't open \"%s\" for writing",s);
        //  fprintf (f,"P6\n%d %d\n255\n",width,height);
        //  XImage *image = XGetImage (display,win,0,0,width,height,~0,ZPixmap);
        //
        //  int rshift = 7 - getHighBitIndex (image.red_mask);
        //  int gshift = 7 - getHighBitIndex (image.green_mask);
        //  int bshift = 7 - getHighBitIndex (image.blue_mask);
        //
        //  for (int y=0; y<height; y++) {
        //    for (int x=0; x<width; x++) {
        //      unsigned long pixel = XGetPixel (image,x,y);
        //      unsigned char b[3];
        //      b[0] = SHIFTL(pixel & image.red_mask,rshift);
        //      b[1] = SHIFTL(pixel & image.green_mask,gshift);
        //      b[2] = SHIFTL(pixel & image.blue_mask,bshift);
        //      fwrite (b,3,1,f);
        //    }
        //  }
        //  fclose (f);
        //  XDestroyImage (image);
    }

    /**
     * Handles the keyboard
     * @param fn 
     */
    private void handleKeyboard(dsFunctions fn) {
        Keyboard.poll();
        while (Keyboard.next()) {
            char key = (char) Keyboard.getEventKey();
            if (key == Keyboard.KEY_ESCAPE) {
                run = false;
            }

            if (!(Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL))) {
                //            if ((event.xkey.state & ControlMask) == 0) {
                //if (key >= ' ' && key <= 126 && fn.command) fn.command (key);
                char keyChar = Keyboard.getEventCharacter();
                if (keyChar >= ' ' && keyChar <= 126)
                    fn.command(keyChar);
                //            System.out.println("cmd-c " +Keyboard.getEventCharacter());
                //              System.out.println("cmd- " + (char)(key+65));

            } else { //if (event.xkey.state & ControlMask) {
                if (key == last_key_pressed) {
                    continue;
                }
                switch (key) {
                case Keyboard.KEY_T:
                    dsSetTextures(!dsGetTextures());
                    break;
                case Keyboard.KEY_S:
                    dsSetShadows(!dsGetShadows());
                    break;
                case Keyboard.KEY_X:
                    run = false;
                    break;
                case Keyboard.KEY_P:
                    pause = !pause;
                    singlestep = false;
                    break;
                case Keyboard.KEY_O:
                    if (pause)
                        singlestep = true;
                    break;
                case Keyboard.KEY_V: {
                    float[] xyz = new float[3], hpr = new float[3];
                    dsGetViewpoint(xyz, hpr);
                    printf("Viewpoint = (%.4f,%.4f,%.4f,%.4f,%.4f,%.4f)\n", xyz[0], xyz[1], xyz[2], hpr[0], hpr[1],
                            hpr[2]);
                    break;
                }
                case Keyboard.KEY_W:
                    writeframes = !writeframes;
                    if (writeframes)
                        printf("Now writing frames to PPM files\n");
                    break;
                }
            }
            last_key_pressed = key; // a kludgy place to put this...
        }
    }

    /**
     * handles the mouse
     */
    private void handleMouse() {
        readBufferedMouse();
    }

    /**
     * reads a mouse in buffered mode
     */
    private void readBufferedMouse() {
        // iterate all events, use the last button down
        while (Mouse.next()) {
            if (Mouse.getEventButton() != -1) {
                if (Mouse.getEventButtonState()) {
                }
                //lastButton = Mouse.getEventButton();
            }
        }

        updateState();
    }

    /**
     * Updates our "model"
     *
     */
    private 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;
        }

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

    }

    //void dsPlatformSimLoop (int window_width, int window_height, dsFunctions *fn,
    //         int initial_pause)
    private static boolean firsttime = true;

    @Override
    void dsPlatformSimLoop(int window_width, int window_height, dsFunctions fn, boolean initial_pause) {
        pause = initial_pause;
        createMainWindow(window_width, window_height);
        //glXMakeCurrent (display,win,glx_context);
        //TODO ?
        //GLContext.useContext(context);
        try {
            //Sets the context / by TZ
            Display.makeCurrent();
        } catch (LWJGLException e) {
            throw new RuntimeException(e);
        }

        dsStartGraphics(window_width, window_height, fn);

        //TZ static bool firsttime=true;
        if (firsttime) {
            System.err.println();
            System.err.print("Using ode4j version: " + OdeHelper.getVersion());
            System.err.println("  [" + OdeHelper.getConfiguration() + "]");
            System.err.println();
            fprintf(stderr,
                    "\n" + "Simulation test environment v%d.%02d\n"
                            + "   Ctrl-P : pause / unpause (or say `-pause' on command line).\n"
                            + "   Ctrl-O : single step when paused.\n"
                            + "   Ctrl-T : toggle textures (or say `-notex' on command line).\n"
                            + "   Ctrl-S : toggle shadows (or say `-noshadow' on command line).\n"
                            + "   Ctrl-V : print current viewpoint coordinates (x,y,z,h,p,r).\n"
                            + "   Ctrl-W : write frames to ppm files: frame/frameNNN.ppm\n" + "   Ctrl-X : exit.\n"
                            + "\n" + "Change the camera position by clicking + dragging in the window.\n"
                            + "   Left button - pan and tilt.\n" + "   Right button - forward and sideways.\n"
                            + "   Left + Right button (or middle button) - sideways and up.\n" + "\n",
                    DrawStuff.DS_VERSION >> 8, DrawStuff.DS_VERSION & 0xff);
            firsttime = false;
        }

        //if (fn.start) 
        fn.start();

        int frame = 1;
        run = true;
        long startTime = System.currentTimeMillis() + 5000;
        long fps = 0;
        while (run && !Display.isCloseRequested()) {
            //  while (run) {
            // read in and process all pending events for the main window
            //    XEvent event;
            //    while (run && XPending (display)) {
            //      XNextEvent (display,event);
            //      handleEvent (event,fn);
            //    }
            handleKeyboard(fn);
            handleMouse();

            //processDrawFrame: This was not move into separate method for convenience

            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

            dsDrawFrame(width, height, fn, pause && !singlestep);
            singlestep = false;

            Display.update();
            if (startTime > System.currentTimeMillis()) {
                fps++;
            } else {
                long timeUsed = 5000 + (startTime - System.currentTimeMillis());
                startTime = System.currentTimeMillis() + 5000;
                System.out.println(
                        fps + " frames in " + (timeUsed / 1000f) + " seconds = " + (fps / (timeUsed / 1000f)));
                fps = 0;
            }
            //    glFlush();
            //    glXSwapBuffers (display,win);
            //    XSync (display,0);

            // capture frames if necessary
            if (pause == false && writeframes) {
                captureFrame(frame);
                frame++;
            }
        }

        //if (fn.stop) 
        fn.stop();
        dsStopGraphics();

        destroyMainWindow();
    }

    //extern "C" void dsStop()
    @Override
    public void dsStop() {
        run = false;
    }

    private static double prev = System.nanoTime() / 1000000000.0;

    //extern "C" double dsElapsedTime()
    @Override
    public double dsElapsedTime() {
        //      if (true) {//(HAVE_GETTIMEOFDAY) { //#if HAVE_GETTIMEOFDAY
        //TZ static double prev=0.0;
        //      timeval tv ;
        //
        //      gettimeofday(tv, 0);
        //      double curr = tv.tv_sec + (double) tv.tv_usec / 1000000.0 ;
        double curr = System.nanoTime() / 1000000000.0;
        //      if (prev==-1)
        //         prev=curr;
        double retval = curr - prev;
        prev = curr;
        if (retval > 1.0)
            retval = 1.0;
        if (retval < Common.dEpsilon)
            retval = Common.dEpsilon;
        return retval;
        //      } else { //#else
        //         return 0.01666; // Assume 60 fps
        //         //#endif
        //      }

    }
}