Jelajahi Sumber

Merge pull request #1 from jMonkeyEngine/master

update to jMonkeyEngine master
Yan 8 tahun lalu
induk
melakukan
e67cb0b0a9
27 mengubah file dengan 1302 tambahan dan 848 penghapusan
  1. 1 0
      .gitignore
  2. 23 18
      jme3-core/src/main/java/com/jme3/material/Material.java
  3. 1 0
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  4. 3 4
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  5. 13 7
      jme3-core/src/main/java/com/jme3/scene/control/LodControl.java
  6. 12 2
      jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java
  7. 196 170
      jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java
  8. 2 1
      jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
  9. 40 2
      jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java
  10. 52 34
      jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java
  11. 6 0
      jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
  12. 78 0
      jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java
  13. 10 0
      jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java
  14. 1 1
      jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java
  15. 3 4
      jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java
  16. 6 6
      jme3-vr/src/main/java/com/jme3/app/VRAppState.java
  17. 6 6
      jme3-vr/src/main/java/com/jme3/app/VRApplication.java
  18. 19 3
      jme3-vr/src/main/java/com/jme3/app/VREnvironment.java
  19. 525 524
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java
  20. 89 30
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java
  21. 93 0
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java
  22. 5 5
      jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java
  23. 40 0
      jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java
  24. 26 15
      jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java
  25. 12 4
      jme3-vr/src/main/java/com/jme3/util/VRViewManager.java
  26. 4 4
      jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java
  27. 36 8
      jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java

+ 1 - 0
.gitignore

@@ -43,3 +43,4 @@
 !/jme3-vr/src/main/resources/**/*.so.dbg
 !/jme3-vr/src/main/resources/**/*.dll
 !/jme3-vr/src/main/resources/**/*.pdb
+/buildMaven.bat

+ 23 - 18
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -529,24 +529,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         checkSetParam(type, name);
         MatParamTexture val = getTextureParam(name);
         if (val == null) {
-            MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name);
-            if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) {
-                value.getImage().setColorSpace(paramDef.getColorSpace());
-                logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, "
-                        + "texture {2} was switched to {3} color space.",
-                        new Object[]{name, paramDef.getColorSpace().toString(),
-                            value.getName(),
-                            value.getImage().getColorSpace().name()});
-            } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) {
-                logger.log(Level.WARNING,
-                        "The texture {0} has linear color space, but the material "
-                        + "parameter {2} specifies no color space requirement, this may "
-                        + "lead to unexpected behavior.\nCheck if the image "
-                        + "was not set to another material parameter with a linear "
-                        + "color space, or that you did not set the ColorSpace to "
-                        + "Linear using texture.getImage.setColorSpace().",
-                        new Object[]{value.getName(), value.getImage().getColorSpace().name(), name});
-            }
+            checkTextureParamColorSpace(name, value);
             paramValues.put(name, new MatParamTexture(type, name, value, null));
         } else {
             val.setTextureValue(value);
@@ -560,6 +543,27 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         sortingId = -1;
     }
 
+    private void checkTextureParamColorSpace(String name, Texture value) {
+        MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name);
+        if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) {
+            value.getImage().setColorSpace(paramDef.getColorSpace());
+            logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, "
+                            + "texture {2} was switched to {3} color space.",
+                    new Object[]{name, paramDef.getColorSpace().toString(),
+                            value.getName(),
+                            value.getImage().getColorSpace().name()});
+        } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) {
+            logger.log(Level.WARNING,
+                    "The texture {0} has linear color space, but the material "
+                            + "parameter {2} specifies no color space requirement, this may "
+                            + "lead to unexpected behavior.\nCheck if the image "
+                            + "was not set to another material parameter with a linear "
+                            + "color space, or that you did not set the ColorSpace to "
+                            + "Linear using texture.getImage.setColorSpace().",
+                    new Object[]{value.getName(), value.getImage().getColorSpace().name(), name});
+        }
+    }
+
     /**
      * Pass a texture to the material shader.
      *
@@ -1062,6 +1066,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
                 if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
                     continue;
                 }
+                checkTextureParamColorSpace(texVal.getName(), texVal.getTextureValue());
             }
 
             if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) {

+ 1 - 0
jme3-core/src/main/java/com/jme3/scene/Geometry.java

@@ -506,6 +506,7 @@ public class Geometry extends Spatial {
     /**
      *  The old clone() method that did not use the new Cloner utility.
      */
+    @Override
     public Geometry oldClone(boolean cloneMaterial) {
         Geometry geomClone = (Geometry) super.clone(cloneMaterial);
 

+ 3 - 4
jme3-core/src/main/java/com/jme3/scene/Mesh.java

@@ -63,8 +63,7 @@ import java.util.ArrayList;
  * All visible elements in a scene are represented by meshes.
  * Meshes may contain three types of geometric primitives:
  * <ul>
- * <li>Points - Every vertex represents a single point in space,
- * the size of each point is specified via {@link Mesh#setPointSize(float) }.
+ * <li>Points - Every vertex represents a single point in space.
  * Points can also be used for {@link RenderState#setPointSprite(boolean) point
  * sprite} mode.</li>
  * <li>Lines - 2 vertices represent a line segment, with the width specified
@@ -82,8 +81,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
      */
     public enum Mode {
         /**
-         * A primitive is a single point in space. The size of the points
-         * can be specified with {@link Mesh#setPointSize(float) }.
+         * A primitive is a single point in space. The size of {@link Mode#Points points} are
+         * determined via the vertex shader's <code>gl_PointSize</code> output.
          */
         Points(true),
 

+ 13 - 7
jme3-core/src/main/java/com/jme3/scene/control/LodControl.java

@@ -121,17 +121,23 @@ public class LodControl extends AbstractControl implements Cloneable, JmeCloneab
 
     @Override
     public void setSpatial(Spatial spatial) {
-        if (!(spatial instanceof Geometry)) {
+        if (spatial != null && !(spatial instanceof Geometry)) {
             throw new IllegalArgumentException("LodControl can only be attached to Geometry!");
         }
 
         super.setSpatial(spatial);
-        Geometry geom = (Geometry) spatial;
-        Mesh mesh = geom.getMesh();
-        numLevels = mesh.getNumLodLevels();
-        numTris = new int[numLevels];
-        for (int i = numLevels - 1; i >= 0; i--) {
-            numTris[i] = mesh.getTriangleCount(i);
+        
+        if(spatial != null) {
+            Geometry geom = (Geometry) spatial;
+            Mesh mesh = geom.getMesh();
+            numLevels = mesh.getNumLodLevels();
+            numTris = new int[numLevels];
+            for (int i = numLevels - 1; i >= 0; i--) {
+                numTris[i] = mesh.getTriangleCount(i);
+            }
+        } else {
+            numLevels = 0;
+            numTris = null;
         }
     }
 

+ 12 - 2
jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java

@@ -31,12 +31,13 @@
  */
 package com.jme3.scene.debug;
 
-import java.util.Map;
-
 import com.jme3.animation.Skeleton;
 import com.jme3.renderer.queue.RenderQueue.Bucket;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Node;
+import com.jme3.util.clone.Cloner;
+
+import java.util.Map;
 
 /**
  * The class that creates a mesh to display how bones behave.
@@ -122,4 +123,13 @@ public class SkeletonDebugger extends Node {
     public SkeletonInterBoneWire getInterBoneWires() {
         return interBoneWires;
     }
+
+    @Override
+    public void cloneFields(Cloner cloner, Object original) {
+        super.cloneFields(cloner, original);
+
+        this.wires = cloner.clone(wires);
+        this.points = cloner.clone(points);
+        this.interBoneWires = cloner.clone(interBoneWires);
+    }
 }

+ 196 - 170
jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java

@@ -40,11 +40,8 @@ import com.jme3.math.FastMath;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer.Type;
-import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.util.BufferUtils;
-import static com.jme3.util.BufferUtils.*;
 import java.io.IOException;
-import java.nio.FloatBuffer;
 
 /**
  * A simple cylinder, defined by it's height and radius.
@@ -127,10 +124,10 @@ public class Cylinder extends Mesh {
      * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need
      * a suited distorted texture.
      *
-     * @param axisSamples
-     *            Number of triangle samples along the axis.
-     * @param radialSamples
-     *            Number of triangle samples along the radial.
+     * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so 
+     * that, for instance, 4 samples mean the cylinder will be made of 3 segments.
+     * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the
+     * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles.
      * @param radius
      *            The radius of the cylinder.
      * @param height
@@ -201,194 +198,225 @@ public class Cylinder extends Mesh {
     /**
      * Rebuilds the cylinder based on a new set of parameters.
      *
-     * @param axisSamples the number of samples along the axis.
-     * @param radialSamples the number of samples around the radial.
-     * @param radius the radius of the bottom of the cylinder.
-     * @param radius2 the radius of the top of the cylinder.
+     * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so 
+     * that, for instance, 4 samples mean the cylinder will be made of 3 segments.
+     * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the
+     * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles.
+     * @param topRadius the radius of the top of the cylinder.
+     * @param bottomRadius the radius of the bottom of the cylinder.
      * @param height the cylinder's height.
      * @param closed should the cylinder have top and bottom surfaces.
      * @param inverted is the cylinder is meant to be viewed from the inside.
      */
     public void updateGeometry(int axisSamples, int radialSamples,
-            float radius, float radius2, float height, boolean closed, boolean inverted) {
+            float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) {
+        // Ensure there's at least two axis samples and 3 radial samples, and positive dimensions.
+        if( axisSamples < 2
+            || radialSamples < 3
+            || topRadius <= 0
+            || bottomRadius <= 0
+            || height <= 0 ) {
+            throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions.");
+        }
+        
         this.axisSamples = axisSamples;
         this.radialSamples = radialSamples;
-        this.radius = radius;
-        this.radius2 = radius2;
+        this.radius = bottomRadius;
+        this.radius2 = topRadius;
         this.height = height;
         this.closed = closed;
         this.inverted = inverted;
 
-//        VertexBuffer pvb = getBuffer(Type.Position);
-//        VertexBuffer nvb = getBuffer(Type.Normal);
-//        VertexBuffer tvb = getBuffer(Type.TexCoord);
-        axisSamples += (closed ? 2 : 0);
-
-        // Vertices
-        int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0);
-
-        setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount));
-
-        // Normals
-        setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount));
-
-        // Texture co-ordinates
-        setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount));
+        // Vertices : One per radial sample plus one duplicate for texture closing around the sides.
+        int verticesCount = axisSamples * (radialSamples +1);
+        // Triangles: Two per side rectangle, which is the product of numbers of samples.
+        int trianglesCount = axisSamples * radialSamples * 2 ;
+        if( closed ) {
+            // If there are caps, add two additional rims and two summits.
+            verticesCount += 2 + 2 * (radialSamples +1);
+            // Add one triangle per radial sample, twice, to form the caps.
+            trianglesCount += 2 * radialSamples ;
+        }
 
-        int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples;
+        // Compute the points along a unit circle:
+        float[][] circlePoints = new float[radialSamples+1][2];
+        for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
+            float angle = FastMath.TWO_PI / radialSamples * circlePoint;
+            circlePoints[circlePoint][0] = FastMath.cos(angle);
+            circlePoints[circlePoint][1] = FastMath.sin(angle);
+        }
+        // Add an additional point for closing the texture around the side of the cylinder.
+        circlePoints[radialSamples][0] = circlePoints[0][0];
+        circlePoints[radialSamples][1] = circlePoints[0][1];
         
-        setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount));
-
-        // generate geometry
-        float inverseRadial = 1.0f / radialSamples;
-        float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1);
-        float inverseAxisLessTexture = 1.0f / (axisSamples - 1);
-        float halfHeight = 0.5f * height;
-
-        // Generate points on the unit circle to be used in computing the mesh
-        // points on a cylinder slice.
-        float[] sin = new float[radialSamples + 1];
-        float[] cos = new float[radialSamples + 1];
-
-        for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
-            float angle = FastMath.TWO_PI * inverseRadial * radialCount;
-            cos[radialCount] = FastMath.cos(angle);
-            sin[radialCount] = FastMath.sin(angle);
+        // Calculate normals.
+        // 
+        // A---------B
+        //  \        |
+        //   \       |
+        //    \      |
+        //     D-----C
+        //
+        // Let be B and C the top and bottom points of the axis, and A and D the top and bottom edges.
+        // The normal in A and D is simply orthogonal to AD, which means we can get it once per sample.
+        //
+        Vector3f[] circleNormals = new Vector3f[radialSamples+1];
+        for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) {
+            // The normal is the orthogonal to the side, which can be got without trigonometry.
+            // The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use
+            // those values in reverse order.
+            Vector3f normal = new Vector3f(height * circlePoints[circlePoint][0], height * circlePoints[circlePoint][1], bottomRadius - topRadius );
+            circleNormals[circlePoint] = normal.normalizeLocal();
         }
-        sin[radialSamples] = sin[0];
-        cos[radialSamples] = cos[0];
-
-        // calculate normals
-        Vector3f[] vNormals = null;
-        Vector3f vNormal = Vector3f.UNIT_Z;
-
-        if ((height != 0.0f) && (radius != radius2)) {
-            vNormals = new Vector3f[radialSamples];
-            Vector3f vHeight = Vector3f.UNIT_Z.mult(height);
-            Vector3f vRadial = new Vector3f();
-
-            for (int radialCount = 0; radialCount < radialSamples; radialCount++) {
-                vRadial.set(cos[radialCount], sin[radialCount], 0.0f);
-                Vector3f vRadius = vRadial.mult(radius);
-                Vector3f vRadius2 = vRadial.mult(radius2);
-                Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius));
-                Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z);
-                vNormals[radialCount] = vMantle.cross(vTangent).normalize();
+
+        float[] vertices = new float[verticesCount * 3];
+        float[] normals = new float[verticesCount * 3];
+        float[] textureCoords = new float[verticesCount * 2];
+        int currentIndex = 0;
+        
+        // Add a circle of points for each axis sample.
+        for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) {
+            float currentHeight = -height / 2 + height * axisSample / (axisSamples-1);
+            float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1);
+            
+            for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
+                // Position, by multipliying the position on a unit circle with the current radius.
+                vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius;
+                vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius;
+                vertices[currentIndex*3 +2] = currentHeight;
+                
+                // Normal
+                Vector3f currentNormal = circleNormals[circlePoint];
+                normals[currentIndex*3] = currentNormal.x;
+                normals[currentIndex*3+1] = currentNormal.y;
+                normals[currentIndex*3+2] = currentNormal.z;
+                        
+                // Texture
+                // The X is the angular position of the point.
+                textureCoords[currentIndex *2] = (float) circlePoint / radialSamples;
+                // Depending on whether there is a cap, the Y is either the height scaled to [0,1], or the radii of 
+                // the cap count as well.
+                if (closed)
+                    textureCoords[currentIndex *2 +1] = (bottomRadius + height / 2 + currentHeight) / (bottomRadius + height + topRadius);
+                else
+                    textureCoords[currentIndex *2 +1] = height / 2 + currentHeight;
+                
+                currentIndex++;
             }
         }
+        
+        // If closed, add duplicate rims on top and bottom, with normals facing up and down.
+        if (closed) {
+            // Bottom
+            for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
+                vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius;
+                vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius;
+                vertices[currentIndex*3 +2] = -height/2;
 
-        FloatBuffer nb = getFloatBuffer(Type.Normal);
-        FloatBuffer pb = getFloatBuffer(Type.Position);
-        FloatBuffer tb = getFloatBuffer(Type.TexCoord);
-
-        // generate the cylinder itself
-        Vector3f tempNormal = new Vector3f();
-        for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) {
-            float axisFraction;
-            float axisFractionTexture;
-            int topBottom = 0;
-            if (!closed) {
-                axisFraction = axisCount * inverseAxisLess; // in [0,1]
-                axisFractionTexture = axisFraction;
-            } else {
-                if (axisCount == 0) {
-                    topBottom = -1; // bottom
-                    axisFraction = 0;
-                    axisFractionTexture = inverseAxisLessTexture;
-                } else if (axisCount == axisSamples - 1) {
-                    topBottom = 1; // top
-                    axisFraction = 1;
-                    axisFractionTexture = 1 - inverseAxisLessTexture;
-                } else {
-                    axisFraction = (axisCount - 1) * inverseAxisLess;
-                    axisFractionTexture = axisCount * inverseAxisLessTexture;
-                }
-            }
+                normals[currentIndex*3] = 0;
+                normals[currentIndex*3+1] = 0;
+                normals[currentIndex*3+2] = -1;
 
-            // compute center of slice
-            float z = -halfHeight + height * axisFraction;
-            Vector3f sliceCenter = new Vector3f(0, 0, z);
-
-            // compute slice vertices with duplication at end point
-            int save = i;
-            for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) {
-                float radialFraction = radialCount * inverseRadial; // in [0,1)
-                tempNormal.set(cos[radialCount], sin[radialCount], 0.0f);
-
-                if (vNormals != null) {
-                    vNormal = vNormals[radialCount];
-                } else if (radius == radius2) {
-                    vNormal = tempNormal;
-                }
-
-                if (topBottom == 0) {
-                    if (!inverted)
-                        nb.put(vNormal.x).put(vNormal.y).put(vNormal.z);
-                    else
-                        nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z);
-                } else {
-                    nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1));
-                }
-
-                tempNormal.multLocal((radius - radius2) * axisFraction + radius2)
-                        .addLocal(sliceCenter);
-                pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z);
-
-                tb.put((inverted ? 1 - radialFraction : radialFraction))
-                        .put(axisFractionTexture);
+                textureCoords[currentIndex *2] = (float) circlePoint / radialSamples;
+                textureCoords[currentIndex *2 +1] = bottomRadius / (bottomRadius + height + topRadius);
+
+                currentIndex++;
             }
+            // Top
+            for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) {
+                vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius;
+                vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius;
+                vertices[currentIndex*3 +2] = height/2;
 
-            BufferUtils.copyInternalVector3(pb, save, i);
-            BufferUtils.copyInternalVector3(nb, save, i);
+                normals[currentIndex*3] = 0;
+                normals[currentIndex*3+1] = 0;
+                normals[currentIndex*3+2] = 1;
 
-            tb.put((inverted ? 0.0f : 1.0f))
-                    .put(axisFractionTexture);
-        }
+                textureCoords[currentIndex *2] = (float) circlePoint / radialSamples;
+                textureCoords[currentIndex *2 +1] = (bottomRadius + height) / (bottomRadius + height + topRadius);
 
-        if (closed) {
-            pb.put(0).put(0).put(-halfHeight); // bottom center
-            nb.put(0).put(0).put(-1 * (inverted ? -1 : 1));
-            tb.put(0.5f).put(0);
-            pb.put(0).put(0).put(halfHeight); // top center
-            nb.put(0).put(0).put(1 * (inverted ? -1 : 1));
-            tb.put(0.5f).put(1);
+                currentIndex++;
+            }
+            
+            // Add the centers of the caps.
+            vertices[currentIndex*3] = 0;
+            vertices[currentIndex*3 +1] = 0;
+            vertices[currentIndex*3 +2] = -height/2;
+            
+            normals[currentIndex*3] = 0;
+            normals[currentIndex*3+1] = 0;
+            normals[currentIndex*3+2] = -1;
+            
+            textureCoords[currentIndex *2] = 0.5f;
+            textureCoords[currentIndex *2+1] = 0f;
+            
+            currentIndex++;
+            
+            vertices[currentIndex*3] = 0;
+            vertices[currentIndex*3 +1] = 0;
+            vertices[currentIndex*3 +2] = height/2;
+            
+            normals[currentIndex*3] = 0;
+            normals[currentIndex*3+1] = 0;
+            normals[currentIndex*3+2] = 1;
+            
+            textureCoords[currentIndex *2] = 0.5f;
+            textureCoords[currentIndex *2+1] = 1f;
         }
 
-        IndexBuffer ib = getIndexBuffer();
-        int index = 0;
-        // Connectivity
-        for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) {
-            int i0 = axisStart;
-            int i1 = i0 + 1;
-            axisStart += radialSamples + 1;
-            int i2 = axisStart;
-            int i3 = i2 + 1;
-            for (int i = 0; i < radialSamples; i++) {
-                if (closed && axisCount == 0) {
-                    if (!inverted) {
-                        ib.put(index++, i0++);
-                        ib.put(index++, vertCount - 2);
-                        ib.put(index++, i1++);
-                    } else {
-                        ib.put(index++, i0++);
-                        ib.put(index++, i1++);
-                        ib.put(index++, vertCount - 2);
-                    }
-                } else if (closed && axisCount == axisSamples - 2) {
-                    ib.put(index++, i2++);
-                    ib.put(index++, inverted ? vertCount - 1 : i3++);
-                    ib.put(index++, inverted ? i3++ : vertCount - 1);
-                } else {
-                    ib.put(index++, i0++);
-                    ib.put(index++, inverted ? i2 : i1);
-                    ib.put(index++, inverted ? i1 : i2);
-                    ib.put(index++, i1++);
-                    ib.put(index++, inverted ? i2++ : i3++);
-                    ib.put(index++, inverted ? i3++ : i2++);
-                }
+        // Add the triangles indexes.
+        short[] indices = new short[trianglesCount * 3];
+        currentIndex = 0;
+        for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) {
+            for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
+                indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint);
+                indices[currentIndex++] =  (short) (axisSample * (radialSamples + 1) + circlePoint + 1);
+                indices[currentIndex++] =  (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint);
+
+                indices[currentIndex++] =  (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint);
+                indices[currentIndex++] =  (short) (axisSample * (radialSamples + 1) + circlePoint + 1);
+                indices[currentIndex++] =  (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint + 1);
+            }
+        }
+        // Add caps if needed.
+        if(closed) {
+            short bottomCapIndex = (short) (verticesCount - 2);
+            short topCapIndex = (short) (verticesCount - 1);
+            
+            int bottomRowOffset = (axisSamples) * (radialSamples +1 );
+            int topRowOffset = (axisSamples+1) * (radialSamples +1 );
+            
+            for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) {
+                indices[currentIndex++] =  (short) (bottomRowOffset + circlePoint +1);
+                indices[currentIndex++] = (short) (bottomRowOffset + circlePoint);
+                indices[currentIndex++] =  bottomCapIndex;
+
+                
+                indices[currentIndex++] = (short) (topRowOffset + circlePoint);
+                indices[currentIndex++] =  (short) (topRowOffset + circlePoint +1);
+                indices[currentIndex++] =  topCapIndex;
             }
         }
 
+        // If inverted, the triangles and normals are all reverted.
+        if (inverted) {
+            for (int i = 0; i < indices.length / 2; i++) {
+                short temp = indices[i];
+                indices[i] = indices[indices.length - 1 - i];
+                indices[indices.length - 1 - i] = temp;
+            }
+            
+            for(int i = 0; i< normals.length; i++) {
+                normals[i] = -normals[i];
+            }
+        }
+        
+        // Fill in the buffers.
+        setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
+        setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
+        setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoords));
+        setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(indices));
+        
         updateBound();
         setStatic();
     }
@@ -418,6 +446,4 @@ public class Cylinder extends Mesh {
         capsule.write(closed, "closed", false);
         capsule.write(inverted, "inverted", false);
     }
-
-
 }

+ 2 - 1
jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java

@@ -243,7 +243,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
             //all variables fed with a matparam or world param are replaced but the matparam itself
             //it avoids issue with samplers that have to be uniforms, and it optimize a but the shader code.
             if (isWorldOrMaterialParam(mapping.getRightVariable())) {
-                nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getName());
+                nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getPrefix() + mapping.getRightVariable().getName());
             } else {
                 if (mapping.getLeftVariable().getType().startsWith("sampler")) {
                     throw new IllegalArgumentException("a Sampler must be a uniform");
@@ -338,6 +338,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
             source.append(var.getNameSpace());
             source.append("_");
         }
+        source.append(var.getPrefix());
         source.append(var.getName());
         if (var.getMultiplicity() != null) {
             source.append("[");

+ 40 - 2
jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java

@@ -45,6 +45,7 @@ import java.io.IOException;
  */
 public class ShaderNodeVariable implements Savable, Cloneable {
 
+    private String prefix = "";
     private String name;
     private String type;
     private String nameSpace;
@@ -62,8 +63,7 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         this.name = name;
         this.type = type;
     }
-    
-    
+
     /**
      * creates a ShaderNodeVariable
      *
@@ -80,6 +80,22 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         this.multiplicity = multiplicity;
     }
 
+
+    /**
+     * creates a ShaderNodeVariable
+     *
+     * @param type         the glsl type of the variable
+     * @param nameSpace    the nameSpace (can be the name of the shaderNode or
+     *                     Global,Attr,MatParam,WorldParam)
+     * @param name         the name of the variable
+     * @param multiplicity the number of element if this variable is an array. Can be an Int of a declared material parameter
+     * @param prefix       the variable prefix to append at generation times. This is mostly to add the g_ and m_ for uniforms
+     */
+    public ShaderNodeVariable(String type, String nameSpace, String name, String multiplicity, String prefix) {
+        this(type, nameSpace, name, multiplicity);
+        this.prefix = prefix;
+    }
+
     /**
      * creates a ShaderNodeVariable
      *
@@ -138,6 +154,22 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         return nameSpace;
     }
 
+    /**
+     * @return the variable prefix
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Sets the variable prefix (m_ or g_)
+     *
+     * @param prefix
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
     /**
      * sets the nameSpace (can be the name of the shaderNode or
      * Global,Attr,MatParam,WorldParam)
@@ -153,6 +185,7 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         int hash = 7;
         hash = 29 * hash + (name != null?name.hashCode():0);
         hash = 29 * hash + (type != null?type.hashCode():0);
+        hash = 29 * hash + (prefix != null ? prefix.hashCode() : 0);
         hash = 29 * hash + (nameSpace != null?nameSpace.hashCode():0);
         hash = 29 * hash + (condition != null?condition.hashCode():0);
         hash = 29 * hash + (multiplicity != null?multiplicity.hashCode():0);
@@ -174,6 +207,9 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) {
             return false;
         }
+        if ((this.prefix == null) ? (other.prefix != null) : !this.prefix.equals(other.prefix)) {
+            return false;
+        }
         if ((this.nameSpace == null) ? (other.nameSpace != null) : !this.nameSpace.equals(other.nameSpace)) {
             return false;
         }
@@ -197,6 +233,7 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         OutputCapsule oc = (OutputCapsule) ex.getCapsule(this);
         oc.write(name, "name", "");
         oc.write(type, "type", "");
+        oc.write(prefix, "prefix", "");
         oc.write(nameSpace, "nameSpace", "");
         oc.write(condition, "condition", null);
         oc.write(shaderOutput, "shaderOutput", false);
@@ -215,6 +252,7 @@ public class ShaderNodeVariable implements Savable, Cloneable {
         InputCapsule ic = (InputCapsule) im.getCapsule(this);
         name = ic.readString("name", "");
         type = ic.readString("type", "");
+        prefix = ic.readString("pefix", "");
         nameSpace = ic.readString("nameSpace", "");
         condition = ic.readString("condition", null);        
         shaderOutput = ic.readBoolean("shaderOutput", false);

+ 52 - 34
jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java

@@ -193,6 +193,7 @@ public class ShaderNodeLoaderDelegate {
                     shaderNodeDefinition = new ShaderNodeDefinition();
                     getNodeDefinitions().put(name, shaderNodeDefinition);
                     shaderNodeDefinition.setName(name);
+                    shaderNodeDefinition.setPath(key.getName());
                     readShaderNodeDefinition(statement.getContents(), key);
 
                 }
@@ -231,40 +232,52 @@ public class ShaderNodeLoaderDelegate {
     protected void readShaderNodeDefinition(List<Statement> statements, ShaderNodeDefinitionKey key) throws IOException {
         boolean isLoadDoc = key instanceof ShaderNodeDefinitionKey && ((ShaderNodeDefinitionKey) key).isLoadDocumentation();
         for (Statement statement : statements) {
-            String[] split = statement.getLine().split("[ \\{]");
-            String line = statement.getLine();
-
-            if (line.startsWith("Type")) {
-                String type = line.substring(line.lastIndexOf(':') + 1).trim();
-                shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type));
-            } else if (line.startsWith("Shader ")) {
-                readShaderStatement(statement);
-                shaderNodeDefinition.getShadersLanguage().add(shaderLanguage);
-                shaderNodeDefinition.getShadersPath().add(shaderName);
-            } else if (line.startsWith("Documentation")) {
-                if (isLoadDoc) {
-                    String doc = "";
+            try {
+                String[] split = statement.getLine().split("[ \\{]");
+                String line = statement.getLine();
+
+                if (line.startsWith("Type")) {
+                    String type = line.substring(line.lastIndexOf(':') + 1).trim();
+                    shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type));
+                } else if (line.startsWith("Shader ")) {
+                    readShaderStatement(statement);
+                    shaderNodeDefinition.getShadersLanguage().add(shaderLanguage);
+                    shaderNodeDefinition.getShadersPath().add(shaderName);
+                } else if (line.startsWith("Documentation")) {
+                    if (isLoadDoc) {
+                        String doc = "";
+                        for (Statement statement1 : statement.getContents()) {
+                            doc += "\n" + statement1.getLine();
+                        }
+                        shaderNodeDefinition.setDocumentation(doc);
+                    }
+                } else if (line.startsWith("Input")) {
+                    varNames = "";
                     for (Statement statement1 : statement.getContents()) {
-                        doc += "\n" + statement1.getLine();
+                        try {
+                            shaderNodeDefinition.getInputs().add(readVariable(statement1));
+                        } catch (RuntimeException e) {
+                            throw new MatParseException(e.getMessage(), statement1, e);
+                        }
                     }
-                    shaderNodeDefinition.setDocumentation(doc);
-                }
-            } else if (line.startsWith("Input")) {
-                varNames = "";
-                for (Statement statement1 : statement.getContents()) {
-                    shaderNodeDefinition.getInputs().add(readVariable(statement1));
-                }
-            } else if (line.startsWith("Output")) {
-                varNames = "";
-                for (Statement statement1 : statement.getContents()) {
-                    if(statement1.getLine().trim().equals("None")){
-                        shaderNodeDefinition.setNoOutput(true);
-                    }else{
-                        shaderNodeDefinition.getOutputs().add(readVariable(statement1));
+                } else if (line.startsWith("Output")) {
+                    varNames = "";
+                    for (Statement statement1 : statement.getContents()) {
+                        try {
+                            if (statement1.getLine().trim().equals("None")) {
+                                shaderNodeDefinition.setNoOutput(true);
+                            } else {
+                                shaderNodeDefinition.getOutputs().add(readVariable(statement1));
+                            }
+                        } catch (RuntimeException e) {
+                            throw new MatParseException(e.getMessage(), statement1, e);
+                        }
                     }
+                } else {
+                    throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement);
                 }
-            } else {
-                throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement);
+            } catch (RuntimeException e) {
+                throw new MatParseException(e.getMessage(), statement, e);
             }
         }
     }
@@ -279,6 +292,9 @@ public class ShaderNodeLoaderDelegate {
     protected ShaderNodeVariable readVariable(Statement statement) throws IOException {
         String line = statement.getLine().trim().replaceAll("\\s*\\[", "[");
         String[] splitVar = line.split("\\s");
+        if (splitVar.length != 2) {
+            throw new MatParseException("2 arguments", splitVar.length + "", statement);
+        }
         String varName = splitVar[1];
         String varType = splitVar[0];
         String multiplicity = null;
@@ -541,11 +557,13 @@ public class ShaderNodeLoaderDelegate {
      */
     protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map<String, DeclaredVariable> map) {
         ShaderNodeVariable right = mapping.getRightVariable();
-        String name = "g_" + param.toString();
+        String name = param.toString();
+
         DeclaredVariable dv = map.get(name);
         if (dv == null) {
             right.setType(param.getGlslType());
             right.setName(name);
+            right.setPrefix("g_");
             dv = new DeclaredVariable(right);
             map.put(right.getName(), dv);
             dv.addNode(shaderNode);
@@ -569,10 +587,11 @@ public class ShaderNodeLoaderDelegate {
      */
     public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map<String, DeclaredVariable> map, Statement statement) throws MatParseException {
         ShaderNodeVariable right = mapping.getRightVariable();
-        DeclaredVariable dv = map.get(param.getPrefixedName());
+        DeclaredVariable dv = map.get(param.getName());
         if (dv == null) {
             right.setType(param.getVarType().getGlslType());
-            right.setName(param.getPrefixedName());     
+            right.setName(param.getName());
+            right.setPrefix("m_");
             if(mapping.getLeftVariable().getMultiplicity() != null){
                 if(!param.getVarType().name().endsWith("Array")){
                     throw new MatParseException(param.getName() + " is not of Array type", statement);
@@ -942,7 +961,6 @@ public class ShaderNodeLoaderDelegate {
                 }
 
                 for (ShaderNodeDefinition definition : defs) {
-                    definition.setPath(defLine[2].trim());
                     if (defName.equals(definition.getName())) {
                         def = definition;
                     }

+ 6 - 0
jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java

@@ -302,6 +302,12 @@ public class DDSLoader implements AssetLoader {
                     bpp = 64;
                     pixelFormat = Image.Format.RGBA16F;
                     break;
+                case 111:
+                    compressed = false;
+                    bpp = 16;
+                    pixelFormat = Format.Luminance16F;
+                    grayscaleOrAlpha = true;
+                    break;
                 default:
                     throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
             }

+ 78 - 0
jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2017 jMonkeyEngine
+ * 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 'jMonkeyEngine' 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.
+ */
+package com.jme3.scene;
+
+import com.jme3.collision.CollisionResults;
+import com.jme3.math.FastMath;
+import com.jme3.math.Ray;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.shape.Cylinder;
+import java.util.Random;
+import org.junit.Test;
+
+/**
+ * Ensures that geometries behave correctly, by casting rays and ensure they don't break.
+ *
+ * @author Christophe Carpentier
+ */
+public class ShapeGeometryTest {
+
+    protected static final int NUMBER_OF_TRIES = 1000;
+    
+    @Test
+    public void testCylinders() {
+        Random random = new Random();
+        
+        // Create a cylinder, cast a random ray, and ensure everything goes well.
+        Node scene = new Node("Scene Node");
+
+        for (int i = 0; i < NUMBER_OF_TRIES; i++) {
+            scene.detachAllChildren();
+
+            Cylinder cylinder = new Cylinder(2, 8, 1, 1, true);
+            Geometry geometry = new Geometry("cylinder", cylinder);
+            geometry.rotate(FastMath.HALF_PI, 0, 0);
+            scene.attachChild(geometry);
+
+            // Cast a random ray, and count successes and IndexOutOfBoundsExceptions.
+            Vector3f randomPoint = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat());
+            Vector3f randomDirection = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat());
+            randomDirection.normalizeLocal();
+
+            Ray ray = new Ray(randomPoint, randomDirection);
+            CollisionResults collisionResults = new CollisionResults();
+
+            // If the geometry is invalid, this should throw various exceptions.
+            scene.collideWith(ray, collisionResults);
+        }
+    }
+}

+ 10 - 0
jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java

@@ -254,6 +254,16 @@ public class LodGenerator {
         }
     };
 
+    /**
+     * Construct a LodGenerator for the given mesh
+     *
+     * @param mesh the mesh to consider to generate de Lods.
+     */
+    public LodGenerator(Mesh mesh) {
+        this.mesh = mesh;
+        build();
+    }
+
     /**
      * Construct a LodGenerator for the given geometry
      *

+ 1 - 1
jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java

@@ -52,7 +52,7 @@ import java.util.ArrayList;
  * There are 2 mode : Scene and Objects.<br>
  * Scene mode extracts the bright parts of the scene to make them glow<br>
  * Object mode make objects glow according to their material's glowMap or their GlowColor<br>
- * @see <a href="http://jmonkeyengine.org/wiki/doku.php/jme3:advanced:bloom_and_glow">advanced:bloom_and_glow</a> for more details
+ * @see <a href="http://jmonkeyengine.github.io/wiki/jme3/advanced/bloom_and_glow.html">advanced:bloom_and_glow</a> for more details
  * 
  * @author Rémy Bouquet aka Nehon
  */

+ 3 - 4
jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java

@@ -31,10 +31,9 @@ public class TestShaderNodes extends SimpleApplication {
         mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
         Technique t = mat.getActiveTechnique();
 
-//        for (Shader.ShaderSource shaderSource : t.getShader().getSources()) {
-//            System.out.println(shaderSource.getSource());
-//        }
-
+        for (Shader.ShaderSource shaderSource : t.getDef().getShader(assetManager, renderer.getCaps(), t.getDynamicDefines()).getSources()) {
+            System.out.println(shaderSource.getSource());
+        }
         
         mat.setColor("Color", ColorRGBA.Yellow);
         mat.setTexture("ColorMap", tex);

+ 6 - 6
jme3-vr/src/main/java/com/jme3/app/VRAppState.java

@@ -241,7 +241,7 @@ public class VRAppState extends AbstractAppState {
         	return application.getViewPort();
         }
         
-        return environment.getVRViewManager().getLeftViewport();
+        return environment.getVRViewManager().getLeftViewPort();
     }
     
     /**
@@ -253,7 +253,7 @@ public class VRAppState extends AbstractAppState {
         if( environment.getVRViewManager() == null ){
         	return application.getViewPort();
         }
-        return environment.getVRViewManager().getRightViewport();
+        return environment.getVRViewManager().getRightViewPort();
     }
     
     /**
@@ -263,12 +263,12 @@ public class VRAppState extends AbstractAppState {
     public void setBackgroundColors(ColorRGBA clr) {
         if( environment.getVRViewManager() == null ) {
             application.getViewPort().setBackgroundColor(clr);
-        } else if( environment.getVRViewManager().getLeftViewport() != null ) {
+        } else if( environment.getVRViewManager().getLeftViewPort() != null ) {
         	
-        	environment.getVRViewManager().getLeftViewport().setBackgroundColor(clr);
+        	environment.getVRViewManager().getLeftViewPort().setBackgroundColor(clr);
             
-        	if( environment.getVRViewManager().getRightViewport() != null ){
-            	environment.getVRViewManager().getRightViewport().setBackgroundColor(clr);
+        	if( environment.getVRViewManager().getRightViewPort() != null ){
+            	environment.getVRViewManager().getRightViewPort().setBackgroundColor(clr);
             }
         }
     }

+ 6 - 6
jme3-vr/src/main/java/com/jme3/app/VRApplication.java

@@ -254,7 +254,7 @@ public abstract class VRApplication implements Application, SystemListener {
         dummyCam = new Camera();
         
         initStateManager();
-        
+
         // Create the GUI manager.
         guiManager = new VRGuiManager(null);
         
@@ -1085,7 +1085,7 @@ public abstract class VRApplication implements Application, SystemListener {
      */
     public ViewPort getLeftViewPort() {
         if( viewmanager == null ) return getViewPort();
-        return viewmanager.getLeftViewport();
+        return viewmanager.getLeftViewPort();
     }
     
     /**
@@ -1095,7 +1095,7 @@ public abstract class VRApplication implements Application, SystemListener {
      */
     public ViewPort getRightViewPort() {
         if( viewmanager == null ) return getViewPort();
-        return viewmanager.getRightViewport();
+        return viewmanager.getRightViewPort();
     }
     
     
@@ -1106,9 +1106,9 @@ public abstract class VRApplication implements Application, SystemListener {
     public void setBackgroundColors(ColorRGBA clr) {
         if( viewmanager == null ) {
             getViewPort().setBackgroundColor(clr);
-        } else if( viewmanager.getLeftViewport() != null ) {
-        	viewmanager.getLeftViewport().setBackgroundColor(clr);
-            if( viewmanager.getRightViewport() != null ) viewmanager.getRightViewport().setBackgroundColor(clr);
+        } else if( viewmanager.getLeftViewPort() != null ) {
+        	viewmanager.getLeftViewPort().setBackgroundColor(clr);
+            if( viewmanager.getRightViewPort() != null ) viewmanager.getRightViewPort().setBackgroundColor(clr);
         }
     }
     

+ 19 - 3
jme3-vr/src/main/java/com/jme3/app/VREnvironment.java

@@ -8,6 +8,7 @@ import com.jme3.app.state.AppState;
 import com.jme3.input.vr.OSVR;
 import com.jme3.input.vr.OpenVR;
 import com.jme3.input.vr.VRAPI;
+import com.jme3.input.vr.VRBounds;
 import com.jme3.input.vr.VRInputAPI;
 import com.jme3.renderer.Camera;
 import com.jme3.scene.Spatial;
@@ -28,6 +29,8 @@ public class VREnvironment {
     private VRMouseManager mouseManager = null;
     private VRViewManager viewmanager   = null;
     
+    private VRBounds bounds             = null;
+    
     /**
      * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}.
      */
@@ -65,7 +68,6 @@ public class VREnvironment {
     
     private boolean initialized = false;
     
-    private boolean attached    = false;
     
     public VREnvironment(AppSettings settings){
     	
@@ -73,7 +75,8 @@ public class VREnvironment {
     	
         guiManager   = new VRGuiManager(this);
         mouseManager = new VRMouseManager(this);
-//        dummyCam = new Camera(settings.getWidth(), settings.getHeight());
+        
+        bounds = new VRBounds();
         
         processSettings();
     }
@@ -86,6 +89,14 @@ public class VREnvironment {
 	    return hardware;
 	}
 	
+	/**
+	 * Get the VR bounds.
+	 * @return the VR bounds.
+	 */
+	public VRBounds getVRBounds(){
+		return bounds;
+	}
+	
 	/**
 	 * Get the VR dedicated input.
 	 * @return the VR dedicated input.
@@ -347,7 +358,12 @@ public class VREnvironment {
     			if (application.getCamera() != null){
     				dummyCam = application.getCamera().clone();
     			} else {
-    				return new Camera(settings.getWidth(), settings.getHeight());
+    				
+    				if ((settings != null) && (settings.getWidth() != 0) && (settings.getHeight() != 0)){
+    		        	dummyCam = new Camera(settings.getWidth(), settings.getHeight());
+    		        } else {
+    		        	dummyCam = new Camera();
+    		        }
     			}
     		} else {
     			throw new IllegalStateException("VR environment is not attached to any application.");

+ 525 - 524
jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java

@@ -1,524 +1,525 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package com.jme3.input.vr;
-
-import com.jme3.app.VREnvironment;
-import com.jme3.math.Matrix4f;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
-import com.jme3.system.jopenvr.HmdMatrix34_t;
-import com.jme3.system.jopenvr.HmdMatrix44_t;
-import com.jme3.system.jopenvr.JOpenVRLibrary;
-import com.jme3.system.jopenvr.OpenVRUtil;
-import com.jme3.system.jopenvr.TrackedDevicePose_t;
-import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable;
-import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
-import com.jme3.util.VRUtil;
-import com.sun.jna.Memory;
-import com.sun.jna.Pointer;
-import com.sun.jna.ptr.FloatByReference;
-import com.sun.jna.ptr.IntByReference;
-import com.sun.jna.ptr.LongByReference;
-
-import java.nio.IntBuffer;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system. 
- * @author reden - phr00t - https://github.com/phr00t
- * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
- */
-public class OpenVR implements VRAPI {
-    
-	private static final Logger logger = Logger.getLogger(OpenVR.class.getName());
-	
-    private static VR_IVRCompositor_FnTable compositorFunctions;
-    private static VR_IVRSystem_FnTable vrsystemFunctions;
-    
-    private static boolean initSuccess = false;
-    private static boolean flipEyes    = false;
-    
-    private static IntBuffer hmdDisplayFrequency;
-    private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference;
-    protected static TrackedDevicePose_t[] hmdTrackedDevicePoses;
-    
-    protected static IntByReference hmdErrorStore;
-    
-    private static final Quaternion rotStore = new Quaternion();
-    private static final Vector3f posStore = new Vector3f();
-    
-    private static FloatByReference tlastVsync;
-    
-    /**
-     * The actual frame count.
-     */
-    public static LongByReference _tframeCount;
-    
-    // for debugging latency
-    private int frames = 0;    
-    
-    protected static Matrix4f[] poseMatrices;
-    
-    private static final Matrix4f hmdPose = Matrix4f.IDENTITY.clone();
-    private static Matrix4f hmdProjectionLeftEye;
-    private static Matrix4f hmdProjectionRightEye;
-    private static Matrix4f hmdPoseLeftEye;
-    private static Matrix4f hmdPoseRightEye;
-    
-    private static Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand;
-    
-    private static float vsyncToPhotons;
-    private static double timePerFrame, frameCountRun;
-    private static long frameCount;
-    private static OpenVRInput VRinput;
-    
-    private VREnvironment environment = null;
-    
-    /**
-     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system 
-     * attached to the given {@link VREnvironment VR environment}.
-     * @param environment the VR environment to which this API is attached.
-     */
-    public OpenVR(VREnvironment environment){
-      this.environment = environment;
-    }
-    
-    @Override
-    public OpenVRInput getVRinput() {
-        return VRinput;
-    }
-    
-    @Override
-    public VR_IVRSystem_FnTable getVRSystem() {
-        return vrsystemFunctions;
-    }
-    
-    @Override
-    public VR_IVRCompositor_FnTable getCompositor() {
-        return compositorFunctions;
-    }
-    
-    @Override
-    public String getName() {
-        return "OpenVR";
-    }
-    
-    private static long latencyWaitTime = 0;
-    
-    @Override
-    public void setFlipEyes(boolean set) {
-        flipEyes = set;
-    }
-    
-    private boolean enableDebugLatency = false;
-    
-    @Override
-    public void printLatencyInfoToConsole(boolean set) {
-        enableDebugLatency = set;
-    }
-
-    @Override
-    public int getDisplayFrequency() {
-        if( hmdDisplayFrequency == null ) return 0;
-        return hmdDisplayFrequency.get(0);
-    }
-    
-    @Override
-    public boolean initialize() {
-    	
-    	logger.config("Initializing OpenVR system...");
-    	
-        hmdErrorStore = new IntByReference();
-        vrsystemFunctions = null;
-        JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene);
-        if( hmdErrorStore.getValue() == 0 ) {
-            vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer());
-        }
-        
-        if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) {
-            logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0));
-            logger.severe("Initializing OpenVR system [FAILED]");
-            return false;
-        } else {
-            logger.config("OpenVR initialized & VR connected.");
-            
-            vrsystemFunctions.setAutoSynch(false);
-            vrsystemFunctions.read();
-            
-            tlastVsync = new FloatByReference();
-            _tframeCount = new LongByReference();
-            
-            hmdDisplayFrequency = IntBuffer.allocate(1);
-            hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float);
-            hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference();
-            hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount);
-            poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount];
-            for(int i=0;i<poseMatrices.length;i++) poseMatrices[i] = new Matrix4f();
-
-            timePerFrame = 1.0 / hmdDisplayFrequency.get(0);
-            
-            // disable all this stuff which kills performance
-            hmdTrackedDevicePoseReference.setAutoRead(false);
-            hmdTrackedDevicePoseReference.setAutoWrite(false);
-            hmdTrackedDevicePoseReference.setAutoSynch(false);
-            for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
-                hmdTrackedDevicePoses[i].setAutoRead(false);
-                hmdTrackedDevicePoses[i].setAutoWrite(false);
-                hmdTrackedDevicePoses[i].setAutoSynch(false);
-            }
-            
-            // init controllers for the first time
-            VRinput = new OpenVRInput(environment);
-            VRinput.init();
-            VRinput.updateConnectedControllers();
-            
-            // init bounds & chaperone info
-            VRBounds.init();
-            
-            logger.config("Initializing OpenVR system [SUCCESS]");
-            initSuccess = true;
-            return true;
-        }
-    }
-    
-    @Override
-    public boolean initVRCompositor(boolean allowed) {
-        hmdErrorStore.setValue(0); // clear the error store
-        if( allowed && vrsystemFunctions != null ) {
-        	
-        	IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRCompositor_Version, hmdErrorStore);
-        	if (intptr != null){
-        	
-        		if (intptr.getPointer() != null){
-            		compositorFunctions = new VR_IVRCompositor_FnTable(intptr.getPointer());
-                    if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){          
-                        compositorFunctions.setAutoSynch(false);
-                        compositorFunctions.read();
-                        if( environment.isSeatedExperience() ) {                    
-                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
-                        } else {
-                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);                
-                        }
-                        logger.config("OpenVR Compositor initialized");
-                    } else {
-                        logger.severe("OpenVR Compositor error: " + hmdErrorStore.getValue());
-                        compositorFunctions = null;
-                    }
-        		} else {
-        			logger.log(Level.SEVERE, "Cannot get valid pointer for generic interface \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
-        			compositorFunctions = null;
-        		}
-
-        	} else {
-        		logger.log(Level.SEVERE, "Cannot get generic interface for \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
-        		compositorFunctions = null;
-        	}
-        	
-            
-        }
-        if( compositorFunctions == null ) {
-            logger.severe("Skipping VR Compositor...");
-            if( vrsystemFunctions != null ) {
-                vsyncToPhotons = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_SecondsFromVsyncToPhotons_Float, hmdErrorStore);
-            } else {
-                vsyncToPhotons = 0f;
-            }
-        }
-        return compositorFunctions != null;
-    }
-
-    @Override
-    public void destroy() {
-        JOpenVRLibrary.VR_ShutdownInternal();
-    }
-
-    @Override
-    public boolean isInitialized() {
-        return initSuccess;
-    }
-
-    @Override
-    public void reset() {
-        if( vrsystemFunctions == null ) return;
-        vrsystemFunctions.ResetSeatedZeroPose.apply();
-        hmdSeatToStand = null;
-    }
-
-    @Override
-    public void getRenderSize(Vector2f store) {
-        if( vrsystemFunctions == null ) {
-            // 1344x1512
-            store.x = 1344f;
-            store.y = 1512f;
-        } else {
-            IntByReference x = new IntByReference();
-            IntByReference y = new IntByReference();
-            vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y);
-            store.x = x.getValue();
-            store.y = y.getValue();
-        }
-    }
-    /*
-    @Override
-    public float getFOV(int dir) {
-        float val = 0f;
-        if( vrsystemFunctions != null ) {      
-            val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore);
-        }
-        // verification of number
-        if( val == 0f ) {
-            return 55f;
-        } else if( val <= 10f ) {
-            // most likely a radian number
-            return val * 57.2957795f;
-        }
-        return val;
-    }
-    */
-    
-    @Override
-    public float getInterpupillaryDistance() {
-        if( vrsystemFunctions == null ) return 0.065f;
-        return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore);
-    }
-    
-    @Override
-    public Quaternion getOrientation() {
-        VRUtil.convertMatrix4toQuat(hmdPose, rotStore);
-        return rotStore;
-    }
-
-    @Override
-    public Vector3f getPosition() {
-        // the hmdPose comes in rotated funny, fix that here
-        hmdPose.toTranslationVector(posStore);
-        posStore.x = -posStore.x;
-        posStore.z = -posStore.z;
-        return posStore;
-    }
-    
-    @Override
-    public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) {
-        hmdPose.toTranslationVector(storePos);
-        storePos.x = -storePos.x;
-        storePos.z = -storePos.z;
-        storeRot.set(getOrientation());
-    }    
-    
-    @Override
-    public void updatePose(){
-        if(vrsystemFunctions == null) return;
-        if(compositorFunctions != null) {
-           compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0);
-        } else {
-            // wait
-            if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime);
-                        
-            vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount);
-            float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons;
-            
-            if( enableDebugLatency ) {
-                if( frames == 10 ) {
-                    System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime));
-                    System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons));
-                }
-                frames = (frames + 1) % 60;            
-            }            
-            
-            // handle skipping frame stuff
-            long nowCount = _tframeCount.getValue();
-            if( nowCount - frameCount > 1 ) {
-                // skipped a frame!
-                if( enableDebugLatency ) System.out.println("Frame skipped!");
-                frameCountRun = 0;
-                if( latencyWaitTime > 0 ) {
-                    latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1);
-                    if( latencyWaitTime < 0 ) latencyWaitTime = 0;
-                }
-            } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) {
-                // didn't skip a frame, lets try waiting longer to improve latency
-                frameCountRun++;
-                latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0));
-            }
-
-            frameCount = nowCount;
-            
-            vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
-                    environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
-                                                       JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
-                    fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);   
-        }
-        
-        // deal with controllers being plugged in and out
-        // causing an invalid memory crash... skipping for now
-        /*boolean hasEvent = false;
-        while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) {
-            // wait until the events are clear..
-            hasEvent = true;
-        }
-        if( hasEvent ) {
-            // an event probably changed controller state
-            VRInput._updateConnectedControllers();
-        }*/
-        //update controllers pose information
-        environment.getVRinput().updateControllerStates();
-                
-        // read pose data from native
-        for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
-            hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid");
-            if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){
-                hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking");
-                VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]);
-            }            
-        }
-        
-        if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){
-            hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]);
-        } else {
-            hmdPose.set(Matrix4f.IDENTITY);
-        }
-    }
-
-    @Override
-    public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){
-        if( hmdProjectionLeftEye != null ) {
-            return hmdProjectionLeftEye;
-        } else if(vrsystemFunctions == null){
-            return cam.getProjectionMatrix();
-        } else {
-            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar());
-            hmdProjectionLeftEye = new Matrix4f();
-            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye);
-            return hmdProjectionLeftEye;
-        }
-    }
-        
-    @Override
-    public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){
-        if( hmdProjectionRightEye != null ) {
-            return hmdProjectionRightEye;
-        } else if(vrsystemFunctions == null){
-            return cam.getProjectionMatrix();
-        } else {
-            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar());
-            hmdProjectionRightEye = new Matrix4f();
-            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye);
-            return hmdProjectionRightEye;
-        }
-    }
-    
-    @Override
-    public Vector3f getHMDVectorPoseLeftEye() {
-        if( hmdPoseLeftEyeVec == null ) {
-            hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector();
-            // set default IPD if none or broken
-            if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) {
-                hmdPoseLeftEyeVec.x = 0.065f * -0.5f;
-            }
-            if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping
-        }
-        return hmdPoseLeftEyeVec;
-    }
-    
-    @Override
-    public Vector3f getHMDVectorPoseRightEye() {
-        if( hmdPoseRightEyeVec == null ) {
-            hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector();
-            // set default IPD if none or broken
-            if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) {
-                hmdPoseRightEyeVec.x = 0.065f * 0.5f;
-            }
-            if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping
-        }
-        return hmdPoseRightEyeVec;
-    }
-    
-    @Override
-    public Vector3f getSeatedToAbsolutePosition() {
-        if( environment.isSeatedExperience() == false ) return Vector3f.ZERO;
-        if( hmdSeatToStand == null ) {
-            hmdSeatToStand = new Vector3f();
-            HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
-            Matrix4f tempmat = new Matrix4f();
-            VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat);
-            tempmat.toTranslationVector(hmdSeatToStand);
-        }
-        return hmdSeatToStand;
-    }
-    
-    @Override
-    public Matrix4f getHMDMatrixPoseLeftEye(){
-        if( hmdPoseLeftEye != null ) {
-            return hmdPoseLeftEye;
-        } else if(vrsystemFunctions == null) {
-            return Matrix4f.IDENTITY;
-        } else {
-            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left);
-            hmdPoseLeftEye = new Matrix4f();
-            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye);
-        }
-    }
-    
-    @Override
-    public HmdType getType() {
-        if( vrsystemFunctions != null ) {      
-            Pointer str1 = new Memory(128);
-            Pointer str2 = new Memory(128);
-            String completeName = "";
-            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
-                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String,
-                                                                   str1, 128, hmdErrorStore);
-            if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0);
-            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
-                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String,
-                                                                   str2, 128, hmdErrorStore);
-            if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0);
-            if( completeName.length() > 0 ) {
-                completeName = completeName.toLowerCase(Locale.ENGLISH).trim();
-                if( completeName.contains("htc") || completeName.contains("vive") ) {
-                    return HmdType.HTC_VIVE;
-                } else if( completeName.contains("osvr") ) {
-                    return HmdType.OSVR;
-                } else if( completeName.contains("oculus") || completeName.contains("rift") ||
-                           completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) {
-                    return HmdType.OCULUS_RIFT;
-                } else if( completeName.contains("fove") ) {
-                    return HmdType.FOVE;
-                } else if( completeName.contains("game") && completeName.contains("face") ) {
-                    return HmdType.GAMEFACE;
-                } else if( completeName.contains("morpheus") ) {
-                    return HmdType.MORPHEUS;
-                } else if( completeName.contains("gear") ) {
-                    return HmdType.GEARVR;
-                } else if( completeName.contains("star") ) {
-                    return HmdType.STARVR;
-                } else if( completeName.contains("null") ) {
-                    return HmdType.NULL;
-                }
-            }
-        } else return HmdType.NONE;
-        return HmdType.OTHER;
-    }
-    
-    @Override
-    public Matrix4f getHMDMatrixPoseRightEye(){
-        if( hmdPoseRightEye != null ) {
-            return hmdPoseRightEye;
-        } else if(vrsystemFunctions == null) {
-            return Matrix4f.IDENTITY;
-        } else {
-            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right);
-            hmdPoseRightEye = new Matrix4f();
-            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye);
-        }
-    }
-  
-}
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.input.vr;
+
+import com.jme3.app.VREnvironment;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.system.jopenvr.HmdMatrix34_t;
+import com.jme3.system.jopenvr.HmdMatrix44_t;
+import com.jme3.system.jopenvr.JOpenVRLibrary;
+import com.jme3.system.jopenvr.OpenVRUtil;
+import com.jme3.system.jopenvr.TrackedDevicePose_t;
+import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable;
+import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
+import com.jme3.util.VRUtil;
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.FloatByReference;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+
+import java.nio.IntBuffer;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system. 
+ * @author reden - phr00t - https://github.com/phr00t
+ * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
+ */
+public class OpenVR implements VRAPI {
+    
+	private static final Logger logger = Logger.getLogger(OpenVR.class.getName());
+	
+    private static VR_IVRCompositor_FnTable compositorFunctions;
+    private static VR_IVRSystem_FnTable vrsystemFunctions;
+    
+    private static boolean initSuccess = false;
+    private static boolean flipEyes    = false;
+    
+    private IntBuffer hmdDisplayFrequency;
+    private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference;
+    protected TrackedDevicePose_t[] hmdTrackedDevicePoses;
+    
+    protected IntByReference hmdErrorStore;
+    
+    private final Quaternion rotStore = new Quaternion();
+    private final Vector3f posStore = new Vector3f();
+    
+    private static FloatByReference tlastVsync;
+    
+    /**
+     * The actual frame count.
+     */
+    public static LongByReference _tframeCount;
+    
+    // for debugging latency
+    private int frames = 0;    
+    
+    protected Matrix4f[] poseMatrices;
+    
+    private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone();
+    private Matrix4f hmdProjectionLeftEye;
+    private Matrix4f hmdProjectionRightEye;
+    private Matrix4f hmdPoseLeftEye;
+    private Matrix4f hmdPoseRightEye;
+    
+    private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand;
+    
+    private float vsyncToPhotons;
+    private double timePerFrame, frameCountRun;
+    private long frameCount;
+    private OpenVRInput VRinput;
+    
+    
+    private VREnvironment environment = null;
+    
+    /**
+     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system 
+     * attached to the given {@link VREnvironment VR environment}.
+     * @param environment the VR environment to which this API is attached.
+     */
+    public OpenVR(VREnvironment environment){
+      this.environment = environment;
+    }
+    
+    @Override
+    public OpenVRInput getVRinput() {
+        return VRinput;
+    }
+    
+    @Override
+    public VR_IVRSystem_FnTable getVRSystem() {
+        return vrsystemFunctions;
+    }
+    
+    @Override
+    public VR_IVRCompositor_FnTable getCompositor() {
+        return compositorFunctions;
+    }
+    
+    @Override
+    public String getName() {
+        return "OpenVR";
+    }
+    
+    private static long latencyWaitTime = 0;
+    
+    @Override
+    public void setFlipEyes(boolean set) {
+        flipEyes = set;
+    }
+    
+    private boolean enableDebugLatency = false;
+    
+    @Override
+    public void printLatencyInfoToConsole(boolean set) {
+        enableDebugLatency = set;
+    }
+
+    @Override
+    public int getDisplayFrequency() {
+        if( hmdDisplayFrequency == null ) return 0;
+        return hmdDisplayFrequency.get(0);
+    }
+    
+    @Override
+    public boolean initialize() {
+    	
+    	logger.config("Initializing OpenVR system...");
+    	
+        hmdErrorStore = new IntByReference();
+        vrsystemFunctions = null;
+        JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene);
+        if( hmdErrorStore.getValue() == 0 ) {
+            vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer());
+        }
+        
+        if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) {
+            logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0));
+            logger.severe("Initializing OpenVR system [FAILED]");
+            return false;
+        } else {
+            logger.config("OpenVR initialized & VR connected.");
+            
+            vrsystemFunctions.setAutoSynch(false);
+            vrsystemFunctions.read();
+            
+            tlastVsync = new FloatByReference();
+            _tframeCount = new LongByReference();
+            
+            hmdDisplayFrequency = IntBuffer.allocate(1);
+            hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float);
+            hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference();
+            hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount);
+            poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount];
+            for(int i=0;i<poseMatrices.length;i++) poseMatrices[i] = new Matrix4f();
+
+            timePerFrame = 1.0 / hmdDisplayFrequency.get(0);
+            
+            // disable all this stuff which kills performance
+            hmdTrackedDevicePoseReference.setAutoRead(false);
+            hmdTrackedDevicePoseReference.setAutoWrite(false);
+            hmdTrackedDevicePoseReference.setAutoSynch(false);
+            for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
+                hmdTrackedDevicePoses[i].setAutoRead(false);
+                hmdTrackedDevicePoses[i].setAutoWrite(false);
+                hmdTrackedDevicePoses[i].setAutoSynch(false);
+            }
+            
+            // init controllers for the first time
+            VRinput = new OpenVRInput(environment);
+            VRinput.init();
+            VRinput.updateConnectedControllers();
+            
+            // init bounds & chaperone info
+            environment.getVRBounds().init(this);
+            
+            logger.config("Initializing OpenVR system [SUCCESS]");
+            initSuccess = true;
+            return true;
+        }
+    }
+    
+    @Override
+    public boolean initVRCompositor(boolean allowed) {
+        hmdErrorStore.setValue(0); // clear the error store
+        if( allowed && vrsystemFunctions != null ) {
+        	
+        	IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRCompositor_Version, hmdErrorStore);
+        	if (intptr != null){
+        	
+        		if (intptr.getPointer() != null){
+            		compositorFunctions = new VR_IVRCompositor_FnTable(intptr.getPointer());
+                    if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){          
+                        compositorFunctions.setAutoSynch(false);
+                        compositorFunctions.read();
+                        if( environment.isSeatedExperience() ) {                    
+                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
+                        } else {
+                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);                
+                        }
+                        logger.config("OpenVR Compositor initialized");
+                    } else {
+                        logger.severe("OpenVR Compositor error: " + hmdErrorStore.getValue());
+                        compositorFunctions = null;
+                    }
+        		} else {
+        			logger.log(Level.SEVERE, "Cannot get valid pointer for generic interface \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
+        			compositorFunctions = null;
+        		}
+
+        	} else {
+        		logger.log(Level.SEVERE, "Cannot get generic interface for \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
+        		compositorFunctions = null;
+        	}
+        	
+            
+        }
+        if( compositorFunctions == null ) {
+            logger.severe("Skipping VR Compositor...");
+            if( vrsystemFunctions != null ) {
+                vsyncToPhotons = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_SecondsFromVsyncToPhotons_Float, hmdErrorStore);
+            } else {
+                vsyncToPhotons = 0f;
+            }
+        }
+        return compositorFunctions != null;
+    }
+
+    @Override
+    public void destroy() {
+        JOpenVRLibrary.VR_ShutdownInternal();
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return initSuccess;
+    }
+
+    @Override
+    public void reset() {
+        if( vrsystemFunctions == null ) return;
+        vrsystemFunctions.ResetSeatedZeroPose.apply();
+        hmdSeatToStand = null;
+    }
+
+    @Override
+    public void getRenderSize(Vector2f store) {
+        if( vrsystemFunctions == null ) {
+            // 1344x1512
+            store.x = 1344f;
+            store.y = 1512f;
+        } else {
+            IntByReference x = new IntByReference();
+            IntByReference y = new IntByReference();
+            vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y);
+            store.x = x.getValue();
+            store.y = y.getValue();
+        }
+    }
+    /*
+    @Override
+    public float getFOV(int dir) {
+        float val = 0f;
+        if( vrsystemFunctions != null ) {      
+            val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore);
+        }
+        // verification of number
+        if( val == 0f ) {
+            return 55f;
+        } else if( val <= 10f ) {
+            // most likely a radian number
+            return val * 57.2957795f;
+        }
+        return val;
+    }
+    */
+    
+    @Override
+    public float getInterpupillaryDistance() {
+        if( vrsystemFunctions == null ) return 0.065f;
+        return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore);
+    }
+    
+    @Override
+    public Quaternion getOrientation() {
+        VRUtil.convertMatrix4toQuat(hmdPose, rotStore);
+        return rotStore;
+    }
+
+    @Override
+    public Vector3f getPosition() {
+        // the hmdPose comes in rotated funny, fix that here
+        hmdPose.toTranslationVector(posStore);
+        posStore.x = -posStore.x;
+        posStore.z = -posStore.z;
+        return posStore;
+    }
+    
+    @Override
+    public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) {
+        hmdPose.toTranslationVector(storePos);
+        storePos.x = -storePos.x;
+        storePos.z = -storePos.z;
+        storeRot.set(getOrientation());
+    }    
+    
+    @Override
+    public void updatePose(){
+        if(vrsystemFunctions == null) return;
+        if(compositorFunctions != null) {
+           compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0);
+        } else {
+            // wait
+            if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime);
+                        
+            vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount);
+            float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons;
+            
+            if( enableDebugLatency ) {
+                if( frames == 10 ) {
+                    System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime));
+                    System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons));
+                }
+                frames = (frames + 1) % 60;            
+            }            
+            
+            // handle skipping frame stuff
+            long nowCount = _tframeCount.getValue();
+            if( nowCount - frameCount > 1 ) {
+                // skipped a frame!
+                if( enableDebugLatency ) System.out.println("Frame skipped!");
+                frameCountRun = 0;
+                if( latencyWaitTime > 0 ) {
+                    latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1);
+                    if( latencyWaitTime < 0 ) latencyWaitTime = 0;
+                }
+            } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) {
+                // didn't skip a frame, lets try waiting longer to improve latency
+                frameCountRun++;
+                latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0));
+            }
+
+            frameCount = nowCount;
+            
+            vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
+                    environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
+                                                       JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
+                    fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);   
+        }
+        
+        // deal with controllers being plugged in and out
+        // causing an invalid memory crash... skipping for now
+        /*boolean hasEvent = false;
+        while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) {
+            // wait until the events are clear..
+            hasEvent = true;
+        }
+        if( hasEvent ) {
+            // an event probably changed controller state
+            VRInput._updateConnectedControllers();
+        }*/
+        //update controllers pose information
+        environment.getVRinput().updateControllerStates();
+                
+        // read pose data from native
+        for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
+            hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid");
+            if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){
+                hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking");
+                VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]);
+            }            
+        }
+        
+        if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){
+            hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]);
+        } else {
+            hmdPose.set(Matrix4f.IDENTITY);
+        }
+    }
+
+    @Override
+    public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){
+        if( hmdProjectionLeftEye != null ) {
+            return hmdProjectionLeftEye;
+        } else if(vrsystemFunctions == null){
+            return cam.getProjectionMatrix();
+        } else {
+            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar());
+            hmdProjectionLeftEye = new Matrix4f();
+            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye);
+            return hmdProjectionLeftEye;
+        }
+    }
+        
+    @Override
+    public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){
+        if( hmdProjectionRightEye != null ) {
+            return hmdProjectionRightEye;
+        } else if(vrsystemFunctions == null){
+            return cam.getProjectionMatrix();
+        } else {
+            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar());
+            hmdProjectionRightEye = new Matrix4f();
+            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye);
+            return hmdProjectionRightEye;
+        }
+    }
+    
+    @Override
+    public Vector3f getHMDVectorPoseLeftEye() {
+        if( hmdPoseLeftEyeVec == null ) {
+            hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector();
+            // set default IPD if none or broken
+            if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) {
+                hmdPoseLeftEyeVec.x = 0.065f * -0.5f;
+            }
+            if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping
+        }
+        return hmdPoseLeftEyeVec;
+    }
+    
+    @Override
+    public Vector3f getHMDVectorPoseRightEye() {
+        if( hmdPoseRightEyeVec == null ) {
+            hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector();
+            // set default IPD if none or broken
+            if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) {
+                hmdPoseRightEyeVec.x = 0.065f * 0.5f;
+            }
+            if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping
+        }
+        return hmdPoseRightEyeVec;
+    }
+    
+    @Override
+    public Vector3f getSeatedToAbsolutePosition() {
+        if( environment.isSeatedExperience() == false ) return Vector3f.ZERO;
+        if( hmdSeatToStand == null ) {
+            hmdSeatToStand = new Vector3f();
+            HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
+            Matrix4f tempmat = new Matrix4f();
+            VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat);
+            tempmat.toTranslationVector(hmdSeatToStand);
+        }
+        return hmdSeatToStand;
+    }
+    
+    @Override
+    public Matrix4f getHMDMatrixPoseLeftEye(){
+        if( hmdPoseLeftEye != null ) {
+            return hmdPoseLeftEye;
+        } else if(vrsystemFunctions == null) {
+            return Matrix4f.IDENTITY;
+        } else {
+            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left);
+            hmdPoseLeftEye = new Matrix4f();
+            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye);
+        }
+    }
+    
+    @Override
+    public HmdType getType() {
+        if( vrsystemFunctions != null ) {      
+            Pointer str1 = new Memory(128);
+            Pointer str2 = new Memory(128);
+            String completeName = "";
+            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
+                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String,
+                                                                   str1, 128, hmdErrorStore);
+            if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0);
+            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
+                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String,
+                                                                   str2, 128, hmdErrorStore);
+            if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0);
+            if( completeName.length() > 0 ) {
+                completeName = completeName.toLowerCase(Locale.ENGLISH).trim();
+                if( completeName.contains("htc") || completeName.contains("vive") ) {
+                    return HmdType.HTC_VIVE;
+                } else if( completeName.contains("osvr") ) {
+                    return HmdType.OSVR;
+                } else if( completeName.contains("oculus") || completeName.contains("rift") ||
+                           completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) {
+                    return HmdType.OCULUS_RIFT;
+                } else if( completeName.contains("fove") ) {
+                    return HmdType.FOVE;
+                } else if( completeName.contains("game") && completeName.contains("face") ) {
+                    return HmdType.GAMEFACE;
+                } else if( completeName.contains("morpheus") ) {
+                    return HmdType.MORPHEUS;
+                } else if( completeName.contains("gear") ) {
+                    return HmdType.GEARVR;
+                } else if( completeName.contains("star") ) {
+                    return HmdType.STARVR;
+                } else if( completeName.contains("null") ) {
+                    return HmdType.NULL;
+                }
+            }
+        } else return HmdType.NONE;
+        return HmdType.OTHER;
+    }
+    
+    @Override
+    public Matrix4f getHMDMatrixPoseRightEye(){
+        if( hmdPoseRightEye != null ) {
+            return hmdPoseRightEye;
+        } else if(vrsystemFunctions == null) {
+            return Matrix4f.IDENTITY;
+        } else {
+            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right);
+            hmdPoseRightEye = new Matrix4f();
+            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye);
+        }
+    }
+  
+}

+ 89 - 30
jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java

@@ -5,6 +5,7 @@
  */
 package com.jme3.input.vr;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -183,28 +184,50 @@ public class OpenVRInput implements VRInputAPI {
     
     @Override
     public Vector3f getVelocity(int controllerIndex) {
-        int index = OpenVRInput.controllerIndex[controllerIndex];
-        if( needsNewVelocity[index] ) {
-            OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity");
-            needsNewVelocity[index] = false;
-        }
-        tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0];
-        tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1];
-        tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2];
-        return tempVel;
+    	
+    	if (environment != null){
+    		
+    		if (environment.getVRHardware() instanceof OpenVR){
+                int index = OpenVRInput.controllerIndex[controllerIndex];
+                if( needsNewVelocity[index] ) {
+                    ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vVelocity");
+                    needsNewVelocity[index] = false;
+                }
+                tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[0];
+                tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[1];
+                tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[2];
+                return tempVel;
+    		} else {
+        		throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName());
+        	}
+    	} else {
+    		throw new IllegalStateException("VR input is not attached to a VR environment.");
+    	}
     }
     
     @Override
     public Vector3f getAngularVelocity(int controllerIndex) {
-        int index = OpenVRInput.controllerIndex[controllerIndex];
-        if( needsNewAngVelocity[index] ) {
-            OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity");
-            needsNewAngVelocity[index] = false;
-        }
-        tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0];
-        tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1];
-        tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2];
-        return tempVel;
+    	
+    	if (environment != null){
+    		
+    		if (environment.getVRHardware() instanceof OpenVR){
+    	    	
+    	        int index = OpenVRInput.controllerIndex[controllerIndex];
+    	        if( needsNewAngVelocity[index] ) {
+    	        	((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vAngularVelocity");
+    	            needsNewAngVelocity[index] = false;
+    	        }
+    	        tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[0];
+    	        tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[1];
+    	        tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[2];
+    	        return tempVel;
+    		} else {
+        		throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName());
+        	}
+    	} else {
+    		throw new IllegalStateException("VR input is not attached to a VR environment.");
+    	}
+
     }
     
     @Override
@@ -309,7 +332,16 @@ public class OpenVRInput implements VRInputAPI {
         	return false;
         }
         
-        return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0;
+    	if (environment != null){
+    		
+    		if (environment.getVRHardware() instanceof OpenVR){
+    			 return ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0;
+    		} else {
+        		throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName());
+        	}
+    	} else {
+    		throw new IllegalStateException("VR input is not attached to a VR environment.");
+    	}
     }
     
     @Override
@@ -317,9 +349,19 @@ public class OpenVRInput implements VRInputAPI {
         if( isInputDeviceTracking(index) == false ){
         	return null;
         }
-        index = controllerIndex[index];
-        VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]);
-        return rotStore[index];
+        
+        if (environment != null){
+    		
+    		if (environment.getVRHardware() instanceof OpenVR){
+    	        index = controllerIndex[index];
+    	        VRUtil.convertMatrix4toQuat(((OpenVR)environment.getVRHardware()).poseMatrices[index], rotStore[index]);
+    	        return rotStore[index];
+    		} else {
+        		throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName());
+        	}
+    	} else {
+    		throw new IllegalStateException("VR input is not attached to a VR environment.");
+    	}
     }
 
     @Override
@@ -328,12 +370,23 @@ public class OpenVRInput implements VRInputAPI {
         	return null;
         }
         
-        // the hmdPose comes in rotated funny, fix that here
-        index = controllerIndex[index];
-        OpenVR.poseMatrices[index].toTranslationVector(posStore[index]);
-        posStore[index].x = -posStore[index].x;
-        posStore[index].z = -posStore[index].z;
-        return posStore[index];
+        if (environment != null){
+    		
+    		if (environment.getVRHardware() instanceof OpenVR){
+    	        // the hmdPose comes in rotated funny, fix that here
+    	        index = controllerIndex[index];
+    	        ((OpenVR)environment.getVRHardware()).poseMatrices[index].toTranslationVector(posStore[index]);
+    	        posStore[index].x = -posStore[index].x;
+    	        posStore[index].z = -posStore[index].z;
+    	        return posStore[index];
+    		} else {
+        		throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName());
+        	}
+    	} else {
+    		throw new IllegalStateException("VR input is not attached to a VR environment.");
+    	}
+        
+
     }
     
     @Override
@@ -411,9 +464,9 @@ public class OpenVRInput implements VRInputAPI {
     	if (environment != null){
     		controllerCount = 0;
         	for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
-                    int classCallback = ((OpenVR)environment.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i);
+        		int classCallback = ((OpenVR)environment.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i);
         		if( classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller || classCallback == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_GenericTracker) {
-        			
+	
         			String controllerName   = "Unknown";
     				String manufacturerName = "Unknown";
     				try {
@@ -425,6 +478,12 @@ public class OpenVRInput implements VRInputAPI {
         			
         			controllerIndex[controllerCount] = i;
         			
+        			// Adding tracked controller to control.
+        			if (trackedControllers == null){
+        				trackedControllers = new ArrayList<VRTrackedController>(JOpenVRLibrary.k_unMaxTrackedDeviceCount);
+        			}
+        			trackedControllers.add(new OpenVRTrackedController(i, this, controllerName, manufacturerName, environment));
+        			
         			// Send an Haptic pulse to the controller
         			triggerHapticPulse(controllerCount, 1.0f);
         			

+ 93 - 0
jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java

@@ -0,0 +1,93 @@
+package com.jme3.input.vr;
+
+import com.jme3.app.VREnvironment;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
+public class OpenVRTrackedController implements VRTrackedController{
+
+	/**
+	 * The index of the controller within the unserlying VR API.
+	 */
+	private int controllerIndex = -1;
+	
+	/**
+	 * The underlying VRAPI.
+	 */
+	private OpenVRInput hardware     = null;
+	
+	/**
+	 * The name of the controller.
+	 */
+	private String name;
+	
+	private VREnvironment environment;
+	
+    /**
+	 * Wrap a new VR tracked controller on an OpenVR system.
+	 * @param controllerIndex the index of the controller within the underlying VR system.
+	 * @param hardware the underlying VR system.
+	 * @param name the name of the controller.
+	 * @param manufacturer the manufacturer of the controller.
+	 * @param environment the VR environment.
+	 */
+    public OpenVRTrackedController(int controllerIndex, OpenVRInput hardware, String name, String manufacturer, VREnvironment environment){
+    	this.controllerIndex = controllerIndex;
+    	this.hardware        = hardware;
+    	
+    	this.name            = name;
+    	this.manufacturer    = manufacturer;
+    	
+    	this.environment     = environment;
+    }
+	
+	/**
+	 * The manufacturer of the controller.
+	 */
+	private String manufacturer;
+	
+	@Override
+	public Vector3f getPosition() {
+		if (hardware != null){
+			return hardware.getPosition(controllerIndex);
+		} else {
+			throw new IllegalStateException("No underlying VR API.");
+		}
+	}
+
+	@Override
+	public Quaternion getOrientation() {
+        if (hardware != null){
+			return hardware.getOrientation(controllerIndex);
+		} else {
+			throw new IllegalStateException("No underlying VR API.");
+		}
+	}
+
+	@Override
+	public Matrix4f getPose(){
+		
+		if (environment != null){
+			 if (hardware != null){
+					return ((OpenVR)environment.getVRHardware()).poseMatrices[controllerIndex];
+				} else {
+					throw new IllegalStateException("No underlying VR API.");
+				}
+		} else {
+			throw new IllegalStateException("VR tracked device is not attached to any environment.");
+		}
+		
+		
+	}
+	
+	@Override
+	public String getControllerName() {
+		return name;
+	}
+
+	@Override
+	public String getControllerManufacturer() {
+		return manufacturer;
+	}
+}

+ 5 - 5
jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java

@@ -16,19 +16,19 @@ public class VRBounds {
 
 	private static Logger logger = Logger.getLogger(VRBounds.class.getName());
 	
-    private static VR_IVRChaperone_FnTable vrChaperone;
-    private static Vector2f playSize;
+    private VR_IVRChaperone_FnTable vrChaperone;
+    private Vector2f playSize;
     
     /**
      * Initialize the VR bounds.
      * @return <code>true</code> if the initialization is a success and <code>false</code> otherwise.
      */
-    public static boolean init() {
+    public boolean init(OpenVR api) {
     	
     	logger.config("Initialize VR bounds...");
     	
         if( vrChaperone == null ) {
-            vrChaperone = new VR_IVRChaperone_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRChaperone_Version, OpenVR.hmdErrorStore).getPointer());
+            vrChaperone = new VR_IVRChaperone_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRChaperone_Version, api.hmdErrorStore).getPointer());
             if( vrChaperone != null ) {
                 vrChaperone.setAutoSynch(false);
                 vrChaperone.read();
@@ -53,7 +53,7 @@ public class VRBounds {
      * Get the size of the VR world.
      * @return the size of the VR world.
      */
-    public static Vector2f getPlaySize() {
+    public Vector2f getPlaySize() {
         return playSize;
     }
     

+ 40 - 0
jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java

@@ -1,5 +1,9 @@
 package com.jme3.input.vr;
 
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+
 /**
  * TODO
  * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
@@ -7,4 +11,40 @@ package com.jme3.input.vr;
  */
 public interface VRTrackedController {
   
+	/**
+	 * Get the controller name.
+	 * @return the controller name.
+	 */
+	public String getControllerName();
+	
+	/**
+	 * Get the controller manufacturer.
+	 * @return the controller manufacturer.
+	 */
+	public String getControllerManufacturer();
+	
+	/**
+	 * Get the position of the tracked device. This value is the translation component of the device {@link #getPose() pose}.
+	 * @return the position of the tracked device.
+	 * @see #getOrientation()
+	 * @see #getPose()
+	 */
+	public Vector3f getPosition();
+	
+	/**
+	 * Get the orientation of the tracked device. This value is the rotation component of the device {@link #getPose() pose}.
+	 * @return the orientation of the tracked device.
+	 * @see #getPosition()
+	 * @see #getPose()
+	 */
+	public Quaternion getOrientation();
+	
+	/**
+	 * Get the pose of the tracked device. 
+	 * The pose is a 4x4 matrix than combine the {@link #getPosition() position} and the {@link #getOrientation() orientation} of the device.
+	 * @return the pose of the tracked device.
+	 * @see #getPosition()
+	 * @see #getOrientation()
+	 */
+	public Matrix4f getPose();
 }

+ 26 - 15
jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java

@@ -28,18 +28,20 @@ public abstract class AbstractVRViewManager implements VRViewManager {
     protected  VREnvironment environment = null;
     
     protected Camera leftCamera;
-    protected ViewPort leftViewport;
+    protected ViewPort leftViewPort;
     protected FilterPostProcessor leftPostProcessor;
     protected Texture2D leftEyeTexture;
     protected Texture2D leftEyeDepth;
     
     protected Camera rightCamera;
-    protected ViewPort rightViewport;
+    protected ViewPort rightViewPort;
     protected FilterPostProcessor rightPostProcessor;
     protected Texture2D rightEyeTexture;
     protected Texture2D rightEyeDepth;
 	
-    private float resMult = 1f;
+    protected ViewPort mirrorViewPort; 
+
+	private float resMult = 1f;
     
     private float heightAdjustment;
     
@@ -54,15 +56,24 @@ public abstract class AbstractVRViewManager implements VRViewManager {
     }
     
     @Override
-    public ViewPort getLeftViewport() {
-        return leftViewport;
+    public ViewPort getLeftViewPort() {
+        return leftViewPort;
     }
     
     @Override
-    public ViewPort getRightViewport() {
-        return rightViewport;
+    public ViewPort getRightViewPort() {
+        return rightViewPort;
     }
     
+    /**
+     * Get the {@link ViewPort view port} attached to the mirror display.
+     * @return the view port attached to the mirror display.
+     */
+    public ViewPort getMirrorViewPort() {
+		return mirrorViewPort;
+	}
+
+    
     @Override
     public Texture2D getLeftTexture(){
       return leftEyeTexture;
@@ -124,7 +135,7 @@ public abstract class AbstractVRViewManager implements VRViewManager {
     public void moveScreenProcessingToEyes() {
     	
     	if (environment != null){
-            if( getRightViewport() == null ){
+            if( getRightViewPort() == null ){
             	return;
             }
             
@@ -150,7 +161,7 @@ public abstract class AbstractVRViewManager implements VRViewManager {
     public void syncScreenProcessing(ViewPort sourceViewport) {
     	
     	if (environment != null){
-    		if(  getRightViewport() == null ){
+    		if(  getRightViewPort() == null ){
     			return;
     		}
     		
@@ -163,13 +174,13 @@ public abstract class AbstractVRViewManager implements VRViewManager {
                 // clear out all filters & processors, to start from scratch
                 getRightPostProcessor().removeAllFilters();
                 getLeftPostProcessor().removeAllFilters();
-                getLeftViewport().clearProcessors();
-                getRightViewport().clearProcessors();
+                getLeftViewPort().clearProcessors();
+                getRightViewPort().clearProcessors();
                 // if we have no processors to sync, don't add the FilterPostProcessor
                 if( sourceViewport.getProcessors().isEmpty() ) return;
                 // add post processors we just made, which are empty
-                getLeftViewport().addProcessor(getLeftPostProcessor());
-                getRightViewport().addProcessor(getRightPostProcessor());
+                getLeftViewPort().addProcessor(getLeftPostProcessor());
+                getRightViewPort().addProcessor(getRightPostProcessor());
                 // go through all of the filters in the processors list
                 // add them to the left viewport processor & clone them to the right
                 for(SceneProcessor sceneProcessor : sourceViewport.getProcessors()) {
@@ -202,8 +213,8 @@ public abstract class AbstractVRViewManager implements VRViewManager {
                         VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor;
                         VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone();
                         dlsrRight.setLight(dlsr.getLight());
-                        getRightViewport().getProcessors().add(0, dlsrRight);
-                        getLeftViewport().getProcessors().add(0, sceneProcessor);
+                        getRightViewPort().getProcessors().add(0, dlsrRight);
+                        getLeftViewPort().getProcessors().add(0, sceneProcessor);
                     }
                 }
                 // make sure each has a translucent filter renderer

+ 12 - 4
jme3-vr/src/main/java/com/jme3/util/VRViewManager.java

@@ -42,17 +42,25 @@ public interface VRViewManager {
     /**
      * Get the {@link ViewPort viewport} attached to the left eye.
      * @return the {@link ViewPort viewport} attached to the left eye.
-     * @see #getRightViewport()
+     * @see #getRightViewPort()
      */
-    public ViewPort getLeftViewport();
+    public ViewPort getLeftViewPort();
     
     
     /**
      * Get the {@link ViewPort viewport} attached to the right eye.
      * @return the {@link ViewPort viewport} attached to the right eye.
-     * @see #getLeftViewport()
+     * @see #getLeftViewPort()
      */
-    public ViewPort getRightViewport();
+    public ViewPort getRightViewPort();
+    
+    /**
+     * Get the {@link ViewPort view port} attached to the mirror display.
+     * @return the view port attached to the mirror display.
+     * @see #getLeftViewPort()
+     * @see #getRightViewPort()
+     */
+    public ViewPort getMirrorViewPort();
     
     /**
      * Get the texture attached to the left eye.

+ 4 - 4
jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java

@@ -117,18 +117,18 @@ public class VRViewManagerOSVR extends AbstractVRViewManager{
     /**
      * Get the {@link ViewPort viewport} attached to the left eye.
      * @return the {@link ViewPort viewport} attached to the left eye.
-     * @see #getRightViewport()
+     * @see #getRightViewPort()
      */
-    public ViewPort getLeftViewport() {
+    public ViewPort getLeftViewPort() {
         return leftViewport;
     }
     
     /**
      * Get the {@link ViewPort viewport} attached to the right eye.
      * @return the {@link ViewPort viewport} attached to the right eye.
-     * @see #getLeftViewport()
+     * @see #getLeftViewPort()
      */
-    public ViewPort getRightViewport() {
+    public ViewPort getRightViewPort() {
         return rightViewport;
     }
     

+ 36 - 8
jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java

@@ -7,6 +7,7 @@ package com.jme3.util;
 import com.jme3.app.VREnvironment;
 import com.jme3.input.vr.OpenVR;
 import com.jme3.input.vr.VRAPI;
+import com.jme3.input.vr.VRTrackedController;
 import com.jme3.material.Material;
 import com.jme3.math.ColorRGBA;
 import com.jme3.math.Quaternion;
@@ -346,10 +347,10 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager {
     	            return;
     	        }
     	        
-    	        leftEyeTexture  = (Texture2D) getLeftViewport().getOutputFrameBuffer().getColorBuffer().getTexture();
-    	        rightEyeTexture = (Texture2D)getRightViewport().getOutputFrameBuffer().getColorBuffer().getTexture();        
-    	        leftEyeDepth    = (Texture2D) getLeftViewport().getOutputFrameBuffer().getDepthBuffer().getTexture();
-    	        rightEyeDepth   = (Texture2D)getRightViewport().getOutputFrameBuffer().getDepthBuffer().getTexture();        
+    	        leftEyeTexture  = (Texture2D) getLeftViewPort().getOutputFrameBuffer().getColorBuffer().getTexture();
+    	        rightEyeTexture = (Texture2D)getRightViewPort().getOutputFrameBuffer().getColorBuffer().getTexture();        
+    	        leftEyeDepth    = (Texture2D) getLeftViewPort().getOutputFrameBuffer().getDepthBuffer().getTexture();
+    	        rightEyeDepth   = (Texture2D)getRightViewPort().getOutputFrameBuffer().getDepthBuffer().getTexture();        
     	      
     	        // main viewport is either going to be a distortion scene or nothing
     	        // mirroring is handled by copying framebuffers
@@ -387,6 +388,7 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager {
     	        
     	        if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) {
     	            setupMirrorBuffers(environment.getCamera(), leftEyeTexture, false);
+    	            
     	        } 
     		} else {
     			throw new IllegalStateException("This VR environment is not attached to any application.");
@@ -414,9 +416,35 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager {
             // grab the hardware handle
             VRAPI dev = environment.getVRHardware();
             if( dev != null ) {
+            	
+
                 // update the HMD's position & orientation
                 dev.updatePose();
                 dev.getPositionAndOrientation(hmdPos, hmdRot);
+/*                
+                // TOREMOVE
+                Vector3f v   = dev.getVRinput().getTrackedController(0).getPosition();
+                Quaternion q = dev.getVRinput().getTrackedController(0).getOrientation();
+                if ((v != null)&&(q != null)){
+                    hmdPos.set(v);
+                    hmdRot.set(q);
+                }
+                
+            	logger.severe("HMD controller ");
+            	logger.severe("  Position "+hmdPos);
+            	logger.severe("  Orientation "+hmdRot);
+            	
+            	VRTrackedController tc = null;
+                for(int i = 0; i < dev.getVRinput().getTrackedControllerCount(); i++){
+                	tc = dev.getVRinput().getTrackedController(i);
+                	logger.severe("Tracked controller "+i+": "+tc.getControllerName());
+                	logger.severe("  Position "+tc.getPosition());
+                	logger.severe("  Orientation "+tc.getOrientation());
+                	logger.severe("");
+                }
+*/                
+                // TOREMOVE
+                
                 if( obs != null ) {
                     // update hmdPos based on obs rotation
                     finalRotation.set(objRot);
@@ -490,18 +518,18 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager {
             //org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
             
             if( !environment.isInstanceRendering()) {
-                leftViewport = setupViewBuffers(getLeftCamera(), LEFT_VIEW_NAME);
+                leftViewPort = setupViewBuffers(getLeftCamera(), LEFT_VIEW_NAME);
                 rightCamera = getLeftCamera().clone();
                 if( environment.getVRHardware() != null ){
                 	getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera()));
                 }
-                rightViewport = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME);
+                rightViewPort = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME);
             } else {
             	
             	if (environment.getApplication() != null){
                 	
                 	logger.severe("THIS CODE NEED CHANGES !!!");
-                    leftViewport = environment.getApplication().getViewPort();
+                    leftViewPort = environment.getApplication().getViewPort();
                     //leftViewport.attachScene(app.getRootNode());
                     rightCamera = getLeftCamera().clone();
                     if( environment.getVRHardware() != null ){
@@ -520,7 +548,7 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager {
             }
             
             // setup gui
-            environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewport(), getRightViewport());
+            environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewPort(), getRightViewPort());
             
             if( environment.getVRHardware() != null ) {
                 // call these to cache the results internally