summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make/build-common.xml14
-rw-r--r--make/build-oculusvr.xml38
-rw-r--r--make/build-test.xml25
-rw-r--r--make/scripts/tests.sh8
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java83
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/Matrix4.java3
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java31
-rw-r--r--src/jogl/classes/javax/media/opengl/GLEventListener2.java74
-rw-r--r--src/oculusvr/classes/com/jogamp/oculusvr/OVRDynamicLibraryBundleInfo.java6
-rw-r--r--src/oculusvr/classes/com/jogamp/oculusvr/OVRException.java43
-rw-r--r--src/oculusvr/classes/com/jogamp/oculusvr/OVRVersion.java7
-rw-r--r--src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java232
-rw-r--r--src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java206
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java638
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java111
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.fp26
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.vp33
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.fp22
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.vp27
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp.vp44
-rw-r--r--src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp_chroma.vp65
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java164
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java208
23 files changed, 1975 insertions, 133 deletions
diff --git a/make/build-common.xml b/make/build-common.xml
index 4eafd5386..583425043 100644
--- a/make/build-common.xml
+++ b/make/build-common.xml
@@ -513,6 +513,20 @@
<path refid="nativewindow_all_atoms.classpath" />
<path refid="jogl_all_atoms.classpath" />
<path refid="newt_all_atoms.classpath" />
+ </path>
+
+ <path id="junit_jogl_newt_oculusvr.compile.classpath">
+ <pathelement location="${junit.jar}" />
+ <pathelement location="${ant.jar}" />
+ <pathelement location="${ant-junit.jar}" />
+ <pathelement location="${ant-junit4.jar}" />
+ <pathelement location="${semver.jar}" />
+ <pathelement location="${gluegen-rt.jar}" />
+ <pathelement location="${gluegen-test-util.jar}" />
+ <pathelement location="${swt.jar}" />
+ <path refid="nativewindow_all_atoms.classpath" />
+ <path refid="jogl_all_atoms.classpath" />
+ <path refid="newt_all_atoms.classpath" />
<path refid="oculusvr_all_atoms.classpath"/>
</path>
diff --git a/make/build-oculusvr.xml b/make/build-oculusvr.xml
index c9370c547..15ed6bde4 100644
--- a/make/build-oculusvr.xml
+++ b/make/build-oculusvr.xml
@@ -16,9 +16,15 @@
-->
<target name="partitioning.setup" depends="common.init">
<property name="java.part.oculusvr"
- value="com/jogamp/oculusvr/* jogamp/oculusvr/*"/>
+ value="com/jogamp/oculusvr/**"/>
+ <property name="java.part.opengl.oculusvr"
+ value="com/jogamp/opengl/oculusvr/** jogamp/opengl/oculusvr/**"/>
<property name="java.part.oculusvr.exclude"
- value="com/jogamp/oculusvr/renderer/*" />
+ value="" />
+ <property name="java.part.oculusvr.shadercode"
+ value="jogamp/opengl/oculusvr/shader/**"/>
+ <property name="java.part.nonjava"
+ value="${java.part.oculusvr.shadercode}"/>
</target>
<!-- ================================================================== -->
@@ -127,7 +133,7 @@
<!-- Now check for the presence of one well-known file -->
<uptodate property="java.generate.skip.ovr"
- targetfile="${src.generated.java}/com/jogamp/oculusvr/OVR.java">
+ targetfile="${src.generated.java}/com/jogamp/opengl/oculusvr/OVR.java">
<srcfiles refid="stub.includes.dependencies.fileset.1" />
<srcfiles refid="stub.includes.dependencies.fileset.2" />
<srcfiles refid="stub.includes.dependencies.fileset.3" />
@@ -161,7 +167,22 @@
<!-- ================================================================== -->
<target name="java.compile" depends="java.generate">
- <!-- Perform the second pass Java compile; everything except portion of fixed function emulation depending on generated code. -->
+ <!-- Pure non JOGL OculusVR part -->
+ <javac destdir="${classes}"
+ excludes="${java.part.oculusvr.exclude} ${java.part.opengl.oculusvr}"
+ fork="yes"
+ includeAntRuntime="false"
+ memoryMaximumSize="${javac.memorymax}"
+ encoding="UTF-8"
+ source="${target.sourcelevel}"
+ target="${target.targetlevel}"
+ bootclasspath="${target.rt.jar}"
+ debug="${javacdebug}" debuglevel="${javacdebuglevel}">
+ <classpath refid="nativewindow_gluegen.classpath"/>
+ <src path="${src.java}" />
+ <src path="${src.generated.java}" />
+ </javac>
+ <!-- Add JOGL OculusVR part -->
<javac destdir="${classes}"
excludes="${java.part.oculusvr.exclude}"
fork="yes"
@@ -172,11 +193,16 @@
target="${target.targetlevel}"
bootclasspath="${target.rt.jar}"
debug="${javacdebug}" debuglevel="${javacdebuglevel}">
- <!-- classpath refid="nativewindow_gluegen.classpath"/ -->
<classpath refid="jogl_nativewindow_gluegen.classpath"/>
<src path="${src.java}" />
<src path="${src.generated.java}" />
</javac>
+ <!-- make shadercode and fonts available in classpath -->
+ <copy todir="${classes}">
+ <fileset dir="${src.java}"
+ includes="${java.part.nonjava}"
+ excludes="**/*.java"/>
+ </copy>
</target>
<!-- ================================================================== -->
@@ -616,7 +642,7 @@
<target name="build-jars-javase" depends="setup-manifestfile">
<jar manifest="${build.oculusvr}/manifest.mf" destfile="${build.oculusvr}/oculusvr.jar" filesonly="true">
<fileset dir="${classes}"
- includes="${java.part.oculusvr}"
+ includes="${java.part.oculusvr} ${java.part.opengl.oculusvr}"
excludes="${java.part.oculusvr.exclude}"/>
</jar>
<jar manifest="${build.oculusvr}/manifest.mf" destfile="${build.oculusvr}/oculusvr-natives-${os.and.arch}.jar" filesonly="true">
diff --git a/make/build-test.xml b/make/build-test.xml
index 10c00c865..daa5e563d 100644
--- a/make/build-test.xml
+++ b/make/build-test.xml
@@ -28,6 +28,7 @@
<property name="java.part.test.all" value="com/jogamp/** jogamp/**"/>
<property name="java.part.test.android" value="com/jogamp/opengl/test/android/**"/>
+ <property name="java.part.test.oculusvr" value="com/jogamp/opengl/test/junit/jogl/stereo/ovr/**"/>
<property name="java.dir.test" value="com/jogamp/opengl/test"/>
<property name="java.dir.junit" value="${java.dir.test}/junit"/>
<property name="java.dir.bugs" value="${java.dir.test}/bugs"/>
@@ -62,10 +63,10 @@
<!--
- Build/run tests/junit.
-->
- <target name="test.compile.javase">
- <!-- Perform the junit pass Java SE compile -->
+
+ <target name="test.compile.javase.generic">
<javac destdir="${classes}"
- excludes="${java.part.test.android}"
+ excludes="${java.part.test.android} ${java.part.test.oculusvr}"
fork="yes"
includeAntRuntime="false"
memoryMaximumSize="${javac.memorymax}"
@@ -77,6 +78,24 @@
<classpath refid="junit_jogl_newt.compile.classpath"/>
<src path="${src.test}" />
</javac>
+ </target>
+ <target name="test.compile.javase.oculusvr" if="oculusvr.sdk.available">
+ <javac destdir="${classes}"
+ includes="${java.part.test.oculusvr}"
+ excludes="${java.part.test.android}"
+ fork="yes"
+ includeAntRuntime="false"
+ memoryMaximumSize="${javac.memorymax}"
+ encoding="UTF-8"
+ source="${target.sourcelevel}"
+ target="${target.targetlevel}"
+ bootclasspath="${target.rt.jar}"
+ debug="${javacdebug}" debuglevel="${javacdebuglevel}">
+ <classpath refid="junit_jogl_newt_oculusvr.compile.classpath"/>
+ <src path="${src.test}" />
+ </javac>
+ </target>
+ <target name="test.compile.javase" depends="test.compile.javase.generic, test.compile.javase.oculusvr">
<copy file="joglversion-test"
tofile="${build.test}/manifest-test.mf"
overwrite="true">
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh
index fe6d70db8..dd7c2c0a0 100644
--- a/make/scripts/tests.sh
+++ b/make/scripts/tests.sh
@@ -124,6 +124,7 @@ function jrun() {
#D_ARGS="-Djogl.debug.GLBufferObjectTracker"
#D_ARGS="-Djogl.debug.GLBufferObjectTracker -Djogl.debug.GLArrayData -Djogl.debug.TraceGL -Djogl.debug.DebugGL"
#D_ARGS="-Djogl.debug.GLSLCode"
+ #D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.TraceGL"
#D_ARGS="-Djogl.debug.GLSLCode -Djogl.debug.DebugGL"
#D_ARGS="-Djogl.debug.GLContext -Dnativewindow.debug.JAWT -Dnewt.debug.Window"
#D_ARGS="-Dnativewindow.debug.JAWT -Djogl.debug.GLCanvas"
@@ -367,6 +368,11 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.jogl.acore.TestVersionSemanticsNOUI $*
#
+# Stereo
+#
+testnoawt com.jogamp.opengl.test.junit.jogl.stereo.ovr.OVRDemo01 $*
+
+#
# HiDPI
#
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
@@ -394,7 +400,7 @@ function testawtswt() {
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestLandscapeES2NEWT $*
#testawtswt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasSWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestElektronenMultipliziererNEWT $*
-testnoawtatomics com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
+#testnoawtatomics com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NEWT $*
#testnoawt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestRedSquareES2NEWT $*
#testswt com.jogamp.opengl.test.junit.jogl.demos.es2.newt.TestGearsES2NewtCanvasSWT $*
diff --git a/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java
index 7e9d7cdd8..f5200443c 100644
--- a/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java
+++ b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java
@@ -705,8 +705,8 @@ public final class FloatUtil {
*
* @param msrc 4x4 matrix in column-major order, the source
* @param msrc_offset offset in given array <i>msrc</i>, i.e. start of the 4x4 matrix
- * @param mres 4x4 matrix in column-major order, the result - may be <code>msrc</code> (in-place)
- * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix - may be <code>msrc_offset</code> (in-place)
+ * @param mres 4x4 matrix in column-major order, the result
+ * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix
* @return given result matrix <i>mres</i> for chaining
*/
public static float[] transposeMatrix(final float[] msrc, final int msrc_offset, final float[] mres, final int mres_offset) {
@@ -737,16 +737,37 @@ public final class FloatUtil {
}
/**
- * Transpose the given matrix in place.
+ * Transpose the given matrix.
*
- * @param m 4x4 matrix in column-major order, the source
- * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix
- * @param temp temporary 4*4 float storage
- * @return given result matrix <i>m</i> for chaining
+ * @param msrc 4x4 matrix in column-major order, the source
+ * @param mres 4x4 matrix in column-major order, the result
+ * @return given result matrix <i>mres</i> for chaining
*/
- public static float[] transposeMatrix(final float[] m, final int m_offset, final float[/*4*4*/] temp) {
- System.arraycopy(m, m_offset, temp, 0, 16);
- return transposeMatrix(temp, 0, m, m_offset);
+ public static float[] transposeMatrix(final float[] msrc, final float[] mres) {
+ mres[0] = msrc[0*4];
+ mres[1] = msrc[1*4];
+ mres[2] = msrc[2*4];
+ mres[3] = msrc[3*4];
+
+ final int i4_1 = 1*4;
+ mres[0+i4_1] = msrc[1+0*4];
+ mres[1+i4_1] = msrc[1+1*4];
+ mres[2+i4_1] = msrc[1+2*4];
+ mres[3+i4_1] = msrc[1+3*4];
+
+ final int i4_2 = 2*4;
+ mres[0+i4_2] = msrc[2+0*4];
+ mres[1+i4_2] = msrc[2+1*4];
+ mres[2+i4_2] = msrc[2+2*4];
+ mres[3+i4_2] = msrc[2+3*4];
+
+ final int i4_3 = 3*4;
+ mres[0+i4_3] = msrc[3+0*4];
+ mres[1+i4_3] = msrc[3+1*4];
+ mres[2+i4_3] = msrc[3+2*4];
+ mres[3+i4_3] = msrc[3+3*4];
+
+ return mres;
}
/**
@@ -1422,8 +1443,9 @@ public final class FloatUtil {
* @param a 4x4 matrix in column-major order
* @param b 4x4 matrix in column-major order
* @param d result a*b in column-major order
+ * @return given result matrix <i>d</i> for chaining
*/
- public static void multMatrix(final float[] a, final int a_off, final float[] b, final int b_off, float[] d, final int d_off) {
+ public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off, float[] d, final int d_off) {
final float b00 = b[b_off+0+0*4];
final float b10 = b[b_off+1+0*4];
final float b20 = b[b_off+2+0*4];
@@ -1476,6 +1498,8 @@ public final class FloatUtil {
d[d_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[d_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[d_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
+
+ return d;
}
/**
@@ -1483,8 +1507,9 @@ public final class FloatUtil {
* @param a 4x4 matrix in column-major order
* @param b 4x4 matrix in column-major order
* @param d result a*b in column-major order
+ * @return given result matrix <i>d</i> for chaining
*/
- public static void multMatrix(final float[] a, final float[] b, float[] d) {
+ public static float[] multMatrix(final float[] a, final float[] b, float[] d) {
final float b00 = b[0+0*4];
final float b10 = b[1+0*4];
final float b20 = b[2+0*4];
@@ -1537,14 +1562,17 @@ public final class FloatUtil {
d[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
+
+ return d;
}
/**
* Multiply matrix: [a] = [a] x [b]
* @param a 4x4 matrix in column-major order (also result)
* @param b 4x4 matrix in column-major order
+ * @return given result matrix <i>a</i> for chaining
*/
- public static void multMatrix(final float[] a, final int a_off, final float[] b, final int b_off) {
+ public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off) {
final float b00 = b[b_off+0+0*4];
final float b10 = b[b_off+1+0*4];
final float b20 = b[b_off+2+0*4];
@@ -1597,14 +1625,17 @@ public final class FloatUtil {
a[a_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[a_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[a_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
+
+ return a;
}
/**
* Multiply matrix: [a] = [a] x [b]
* @param a 4x4 matrix in column-major order (also result)
* @param b 4x4 matrix in column-major order
+ * @return given result matrix <i>a</i> for chaining
*/
- public static void multMatrix(final float[] a, final float[] b) {
+ public static float[] multMatrix(final float[] a, final float[] b) {
final float b00 = b[0+0*4];
final float b10 = b[1+0*4];
final float b20 = b[2+0*4];
@@ -1657,6 +1688,8 @@ public final class FloatUtil {
a[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
+
+ return a;
}
/**
@@ -1705,10 +1738,11 @@ public final class FloatUtil {
* @param m_in_off
* @param v_in 4-component column-vector
* @param v_out m_in * v_in
+ * @return given result vector <i>v_out</i> for chaining
*/
- public static void multMatrixVec(final float[] m_in, final int m_in_off,
- final float[] v_in, final int v_in_off,
- final float[] v_out, final int v_out_off) {
+ public static float[] multMatrixVec(final float[] m_in, final int m_in_off,
+ final float[] v_in, final int v_in_off,
+ final float[] v_out, final int v_out_off) {
// (one matrix row in column-major order) X (column vector)
v_out[0 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off ] + v_in[1+v_in_off] * m_in[1*4+m_in_off ] +
v_in[2+v_in_off] * m_in[2*4+m_in_off ] + v_in[3+v_in_off] * m_in[3*4+m_in_off ];
@@ -1724,6 +1758,8 @@ public final class FloatUtil {
final int m_in_off_3 = 3+m_in_off;
v_out[3 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_3] + v_in[1+v_in_off] * m_in[1*4+m_in_off_3] +
v_in[2+v_in_off] * m_in[2*4+m_in_off_3] + v_in[3+v_in_off] * m_in[3*4+m_in_off_3];
+
+ return v_out;
}
/**
@@ -1731,8 +1767,9 @@ public final class FloatUtil {
* @param m_in_off
* @param v_in 4-component column-vector
* @param v_out m_in * v_in
+ * @return given result vector <i>v_out</i> for chaining
*/
- public static void multMatrixVec(final float[] m_in, final float[] v_in, final float[] v_out) {
+ public static float[] multMatrixVec(final float[] m_in, final float[] v_in, final float[] v_out) {
// (one matrix row in column-major order) X (column vector)
v_out[0] = v_in[0] * m_in[0*4 ] + v_in[1] * m_in[1*4 ] +
v_in[2] * m_in[2*4 ] + v_in[3] * m_in[3*4 ];
@@ -1745,6 +1782,8 @@ public final class FloatUtil {
v_out[3] = v_in[0] * m_in[0*4+3] + v_in[1] * m_in[1*4+3] +
v_in[2] * m_in[2*4+3] + v_in[3] * m_in[3*4+3];
+
+ return v_out;
}
/**
@@ -1776,14 +1815,16 @@ public final class FloatUtil {
* @param column named column to copy
* @param v_out the column-vector storage, at least 3 components long
* @param v_out_off offset to storage
+ * @return given result vector <i>v_out</i> for chaining
*/
- public static void copyMatrixColumn(final float[] m_in, final int m_in_off, final int column, final float[] v_out, final int v_out_off) {
+ public static float[] copyMatrixColumn(final float[] m_in, final int m_in_off, final int column, final float[] v_out, final int v_out_off) {
v_out[0+v_out_off]=m_in[0+column*4+m_in_off];
v_out[1+v_out_off]=m_in[1+column*4+m_in_off];
v_out[2+v_out_off]=m_in[2+column*4+m_in_off];
if( v_out.length > 3+v_out_off ) {
v_out[3+v_out_off]=m_in[3+column*4+m_in_off];
}
+ return v_out;
}
/**
@@ -1796,14 +1837,16 @@ public final class FloatUtil {
* @param row named row to copy
* @param v_out the row-vector storage, at least 3 components long
* @param v_out_off offset to storage
+ * @return given result vector <i>v_out</i> for chaining
*/
- public static void copyMatrixRow(final float[] m_in, final int m_in_off, final int row, final float[] v_out, final int v_out_off) {
+ public static float[] copyMatrixRow(final float[] m_in, final int m_in_off, final int row, final float[] v_out, final int v_out_off) {
v_out[0+v_out_off]=m_in[row+0*4+m_in_off];
v_out[1+v_out_off]=m_in[row+1*4+m_in_off];
v_out[2+v_out_off]=m_in[row+2*4+m_in_off];
if( v_out.length > 3+v_out_off ) {
v_out[3+v_out_off]=m_in[row+3*4+m_in_off];
}
+ return v_out;
}
/**
diff --git a/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java b/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java
index 069e6a0a9..830f1a882 100644
--- a/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java
+++ b/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java
@@ -123,7 +123,8 @@ public class Matrix4 {
}
public final void transpose() {
- FloatUtil.transposeMatrix(matrix, 0, mat4Tmp1);
+ System.arraycopy(matrix, 0, mat4Tmp1, 0, 16);
+ FloatUtil.transposeMatrix(mat4Tmp1, matrix);
}
public final float determinant() {
diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java b/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java
index dabde83dd..a1735766e 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java
@@ -45,7 +45,6 @@ import javax.media.opengl.GLCapabilitiesImmutable;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
-import javax.media.opengl.GLFBODrawable;
import jogamp.opengl.Debug;
/**
@@ -228,14 +227,14 @@ public abstract class TileRendererBase {
protected GLEventListener glEventListenerPre = null;
protected GLEventListener glEventListenerPost = null;
- private final String hashStr(Object o) {
+ private final String hashStr(final Object o) {
final int h = null != o ? o.hashCode() : 0;
return "0x"+Integer.toHexString(h);
}
- protected StringBuilder tileDetails(StringBuilder sb) {
+ protected StringBuilder tileDetails(final StringBuilder sb) {
return sb.append("cur "+currentTileXPos+"/"+currentTileYPos+" "+currentTileWidth+"x"+currentTileHeight+", buffer "+hashStr(tileBuffer));
}
- public StringBuilder toString(StringBuilder sb) {
+ public StringBuilder toString(final StringBuilder sb) {
final int gladListenerCount = null != listeners ? listeners.length : 0;
sb.append("tile[");
tileDetails(sb);
@@ -246,7 +245,7 @@ public abstract class TileRendererBase {
}
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
+ final StringBuilder sb = new StringBuilder();
return getClass().getSimpleName()+
"["+toString(sb).toString()+"]";
}
@@ -270,7 +269,7 @@ public abstract class TileRendererBase {
*
* @param buffer The buffer itself. Must be large enough to contain a random tile
*/
- public final void setTileBuffer(GLPixelBuffer buffer) {
+ public final void setTileBuffer(final GLPixelBuffer buffer) {
tileBuffer = buffer;
if( DEBUG ) {
System.err.println("TileRenderer: tile-buffer "+tileBuffer);
@@ -286,7 +285,7 @@ public abstract class TileRendererBase {
* @param width The width of the final image
* @param height The height of the final image
*/
- public void setImageSize(int width, int height) {
+ public void setImageSize(final int width, final int height) {
imageSize.set(width, height);
}
@@ -298,7 +297,7 @@ public abstract class TileRendererBase {
*
* @param buffer the buffer itself, must be large enough to hold the final image
*/
- public final void setImageBuffer(GLPixelBuffer buffer) {
+ public final void setImageBuffer(final GLPixelBuffer buffer) {
imageBuffer = buffer;
if( DEBUG ) {
System.err.println("TileRenderer: image-buffer "+imageBuffer);
@@ -308,7 +307,7 @@ public abstract class TileRendererBase {
/** @see #setImageBuffer(GLPixelBuffer) */
public final GLPixelBuffer getImageBuffer() { return imageBuffer; }
- /* pp */ final void validateGL(GL gl) throws GLException {
+ /* pp */ final void validateGL(final GL gl) throws GLException {
if( imageBuffer != null && !gl.isGL2ES3()) {
throw new GLException("Using image-buffer w/ inssufficient GL context: "+gl.getContext().getGLVersion()+", "+gl.getGLProfile());
}
@@ -415,7 +414,7 @@ public abstract class TileRendererBase {
* See {@link GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable)}.
* </p>
*/
- public final boolean reqPreSwapBuffers(GLCapabilitiesImmutable chosenCaps) {
+ public final boolean reqPreSwapBuffers(final GLCapabilitiesImmutable chosenCaps) {
return GLDrawableUtil.swapBuffersBeforeRead(chosenCaps);
}
@@ -466,7 +465,7 @@ public abstract class TileRendererBase {
* @see #getAttachedDrawable()
* @see #detachAutoDrawable()
*/
- public final void attachAutoDrawable(GLAutoDrawable glad) throws IllegalStateException {
+ public final void attachAutoDrawable(final GLAutoDrawable glad) throws IllegalStateException {
if( null != this.glad ) {
throw new IllegalStateException("GLAutoDrawable already attached");
}
@@ -551,7 +550,7 @@ public abstract class TileRendererBase {
* @param preTile the pre operations
* @param postTile the post operations
*/
- public final void setGLEventListener(GLEventListener preTile, GLEventListener postTile) {
+ public final void setGLEventListener(final GLEventListener preTile, final GLEventListener postTile) {
glEventListenerPre = preTile;
glEventListenerPost = postTile;
}
@@ -573,7 +572,7 @@ public abstract class TileRendererBase {
final TileRenderer tileRenderer = TileRendererBase.this instanceof TileRenderer ? (TileRenderer) TileRendererBase.this : null;
@Override
- public void init(GLAutoDrawable drawable) {
+ public void init(final GLAutoDrawable drawable) {
if( null != glEventListenerPre ) {
glEventListenerPre.init(drawable);
}
@@ -590,7 +589,7 @@ public abstract class TileRendererBase {
}
}
@Override
- public void dispose(GLAutoDrawable drawable) {
+ public void dispose(final GLAutoDrawable drawable) {
if( null != glEventListenerPre ) {
glEventListenerPre.dispose(drawable);
}
@@ -603,7 +602,7 @@ public abstract class TileRendererBase {
}
}
@Override
- public void display(GLAutoDrawable drawable) {
+ public void display(final GLAutoDrawable drawable) {
if( null != glEventListenerPre ) {
glEventListenerPre.reshape(drawable, 0, 0, currentTileWidth, currentTileHeight);
glEventListenerPre.display(drawable);
@@ -660,6 +659,6 @@ public abstract class TileRendererBase {
}
}
@Override
- public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { }
};
} \ No newline at end of file
diff --git a/src/jogl/classes/javax/media/opengl/GLEventListener2.java b/src/jogl/classes/javax/media/opengl/GLEventListener2.java
new file mode 100644
index 000000000..d4e8e84a4
--- /dev/null
+++ b/src/jogl/classes/javax/media/opengl/GLEventListener2.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package javax.media.opengl;
+
+/**
+ * Extended {@link GLEventListener} interface
+ * supporting more fine grained control over the implementation.
+ */
+public interface GLEventListener2 extends GLEventListener {
+ /**
+ * {@link #display(GLAutoDrawable, int) display flag}: Repeat last produced image.
+ * <p>
+ * While a repeated frame shall produce the same artifacts as the last <code>display</code> call,
+ * e.g. not change animated objects, it shall reflect the {@link #setProjectionModelview(GLAutoDrawable, float[], float[]) current matrix}.
+ * </p>
+ */
+ public static final int DISPLAY_REPEAT = 1 << 0;
+
+ /**
+ * {@link #display(GLAutoDrawable, int) display flag}: Do not clear any target buffer, e.g. color-, depth- or stencil-buffers.
+ */
+ public static final int DISPLAY_DONTCLEAR = 1 << 1;
+
+ /**
+ * Extended {@link #display(GLAutoDrawable) display} method,
+ * allowing to pass a display flag, e.g. {@link #DISPLAY_REPEAT} or {@link #DISPLAY_DONTCLEAR}.
+ * <p>
+ * Method is usually called by a custom rendering loop,
+ * e.g. for manual stereo rendering or the like.
+ * </p>
+ * @param drawable
+ * @param flags
+ */
+ public void display(final GLAutoDrawable drawable, final int flags);
+
+ /**
+ * Might be called instead of {@link #reshape(GLAutoDrawable, int, int, int, int) reshape}
+ * to specify a custom projection and modelview matrix determined by the caller.
+ * <p>
+ * Method is usually called by a custom rendering loop,
+ * e.g. for manual stereo rendering or the like.
+ * </p>
+ *
+ * @param drawable the triggering {@link GLAutoDrawable}
+ * @param mat4Projection float[16] projection matrix
+ * @param mat4Modelview float[16] modelview matrix
+ */
+ public void setProjectionModelview(final GLAutoDrawable drawable, final float[] mat4Projection, final float[] mat4Modelview);
+}
diff --git a/src/oculusvr/classes/com/jogamp/oculusvr/OVRDynamicLibraryBundleInfo.java b/src/oculusvr/classes/com/jogamp/oculusvr/OVRDynamicLibraryBundleInfo.java
index 6bd9d1a64..8eafb9927 100644
--- a/src/oculusvr/classes/com/jogamp/oculusvr/OVRDynamicLibraryBundleInfo.java
+++ b/src/oculusvr/classes/com/jogamp/oculusvr/OVRDynamicLibraryBundleInfo.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2010 JogAmp Community. All rights reserved.
+ * Copyright 2014 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
@@ -95,12 +95,12 @@ import java.util.*;
}
@Override
- public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) {
+ public final long toolGetProcAddress(final long toolGetProcAddressHandle, final String funcName) {
return 0;
}
@Override
- public final boolean useToolGetProcAdressFirst(String funcName) {
+ public final boolean useToolGetProcAdressFirst(final String funcName) {
return false;
}
diff --git a/src/oculusvr/classes/com/jogamp/oculusvr/OVRException.java b/src/oculusvr/classes/com/jogamp/oculusvr/OVRException.java
index e65338025..f2a254e14 100644
--- a/src/oculusvr/classes/com/jogamp/oculusvr/OVRException.java
+++ b/src/oculusvr/classes/com/jogamp/oculusvr/OVRException.java
@@ -1,22 +1,29 @@
/**
- * Copyright (C) 2014 JogAmp Community. All rights reserved.
+ * Copyright 2014 JogAmp Community. All rights reserved.
*
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
*
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
*/
package com.jogamp.oculusvr;
@@ -32,19 +39,19 @@ public class OVRException extends RuntimeException {
/** Constructs an ALException object with the specified detail
message. */
- public OVRException(String message) {
+ public OVRException(final String message) {
super(message);
}
/** Constructs an ALException object with the specified detail
message and root cause. */
- public OVRException(String message, Throwable cause) {
+ public OVRException(final String message, final Throwable cause) {
super(message, cause);
}
/** Constructs an ALException object with the specified root
cause. */
- public OVRException(Throwable cause) {
+ public OVRException(final Throwable cause) {
super(cause);
}
}
diff --git a/src/oculusvr/classes/com/jogamp/oculusvr/OVRVersion.java b/src/oculusvr/classes/com/jogamp/oculusvr/OVRVersion.java
index 25db640e4..eebd771d8 100644
--- a/src/oculusvr/classes/com/jogamp/oculusvr/OVRVersion.java
+++ b/src/oculusvr/classes/com/jogamp/oculusvr/OVRVersion.java
@@ -29,10 +29,15 @@
package com.jogamp.oculusvr;
import com.jogamp.common.GlueGenVersion;
-
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.VersionUtil;
import com.jogamp.common.util.JogampVersion;
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.oculusvr.ovrHmdDesc;
+import com.jogamp.oculusvr.ovrSensorDesc;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.oculusvr.ovrVector2i;
import java.util.jar.Manifest;
diff --git a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java
new file mode 100644
index 000000000..b10a58842
--- /dev/null
+++ b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererDualFBO.java
@@ -0,0 +1,232 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.oculusvr;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLEventListener2;
+
+import jogamp.opengl.oculusvr.OVRDistortion;
+
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.ovrFrameTiming;
+import com.jogamp.opengl.FBObject;
+import com.jogamp.opengl.FBObject.TextureAttachment;
+import com.jogamp.opengl.FBObject.Attachment.Type;
+import com.jogamp.opengl.math.FloatUtil;
+
+/**
+ * OculusVR (OVR) <i>Side By Side</i> Distortion Renderer utilizing {@link OVRDistortion}
+ * implementing {@link GLEventListener} for convenience.
+ * <p>
+ * Implementation renders an {@link GLEventListener2} instance
+ * side-by-side using two {@link FBObject}s according to {@link OVRDistortion}.
+ * </p>
+ */
+public class OVRSBSRendererDualFBO implements GLEventListener {
+
+ private final float[] mat4Projection = new float[16];
+ private final float[] mat4Modelview = new float[16];
+ private final OVRDistortion dist;
+ private final boolean ownsDist;
+ private final GLEventListener2 upstream;
+ private final FBObject[] fbos;
+
+ // final float[] eyePos = { 0.0f, 1.6f, -5.0f };
+ private final float[] eyePos = { 0.0f, 0.0f, -20.0f };
+ // EyePos.y = ovrHmd_GetFloat(HMD, OVR_KEY_EYE_HEIGHT, EyePos.y);
+ private float eyeYaw = FloatUtil.PI; // 180 degrees in radians
+ private float frustumNear = 0.1f;
+ private float frustumFar = 7000f;
+ private int numSamples;
+ private final TextureAttachment[] fboTexs;
+
+ public OVRSBSRendererDualFBO(final OVRDistortion dist, final boolean ownsDist, final GLEventListener2 upstream, final int numSamples) {
+ this.dist = dist;
+ this.ownsDist = ownsDist;
+ this.upstream = upstream;
+ this.numSamples = numSamples;
+ fbos = new FBObject[2];
+ fbos[0] = new FBObject();
+ fbos[1] = new FBObject();
+ fboTexs = new TextureAttachment[2];
+ }
+
+ /**
+ *
+ * @param eyePos
+ * @param eyeYaw
+ * @param frustumNear
+ * @param frustumFar
+ */
+ public void setUpstreamPMVParams(final float[] eyePos, final float eyeYaw, final float frustumNear, final float frustumFar) {
+ System.arraycopy(eyePos, 0, this.eyePos, 0, 3);
+ this.eyeYaw = eyeYaw;
+ this.frustumNear = frustumNear;
+ this.frustumFar = frustumFar;
+ }
+
+ private void initFBOs(final GL gl, final int width, final int height) {
+ // remove all texture attachments, since MSAA uses just color-render-buffer
+ // and non-MSAA uses texture2d-buffer
+ fbos[0].detachAllColorbuffer(gl);
+ fbos[1].detachAllColorbuffer(gl);
+
+ fbos[0].reset(gl, width, height, numSamples, false);
+ fbos[1].reset(gl, width, height, numSamples, false);
+ if(fbos[0].getNumSamples() != fbos[1].getNumSamples()) {
+ throw new InternalError("sample size mismatch: \n\t0: "+fbos[0]+"\n\t1: "+fbos[1]);
+ }
+ numSamples = fbos[0].getNumSamples();
+
+ if(numSamples>0) {
+ fbos[0].attachColorbuffer(gl, 0, true);
+ fbos[0].resetSamplingSink(gl);
+ fbos[1].attachColorbuffer(gl, 0, true);
+ fbos[1].resetSamplingSink(gl);
+ fboTexs[0] = fbos[0].getSamplingSink();
+ fboTexs[1] = fbos[1].getSamplingSink();
+ } else {
+ fboTexs[0] = fbos[0].attachTexture2D(gl, 0, true);
+ fboTexs[1] = fbos[1].attachTexture2D(gl, 0, true);
+ }
+ numSamples=fbos[0].getNumSamples();
+ fbos[0].attachRenderbuffer(gl, Type.DEPTH, 24);
+ fbos[0].unbind(gl);
+ fbos[1].attachRenderbuffer(gl, Type.DEPTH, 24);
+ fbos[1].unbind(gl);
+ }
+
+ @SuppressWarnings("unused")
+ private void resetFBOs(final GL gl, final int width, final int height) {
+ fbos[0].reset(gl, width, height, numSamples, true);
+ fbos[1].reset(gl, width, height, numSamples, true);
+ if(fbos[0].getNumSamples() != fbos[1].getNumSamples()) {
+ throw new InternalError("sample size mismatch: \n\t0: "+fbos[0]+"\n\t1: "+fbos[1]);
+ }
+ numSamples = fbos[0].getNumSamples();
+ if(numSamples>0) {
+ fboTexs[0] = fbos[0].getSamplingSink();
+ fboTexs[1] = fbos[1].getSamplingSink();
+ } else {
+ fboTexs[0] = (TextureAttachment) fbos[0].getColorbuffer(0);
+ fboTexs[1] = (TextureAttachment) fbos[1].getColorbuffer(0);
+ }
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ dist.init(gl);
+
+ // We will do some offscreen rendering, setup FBO...
+ if( null != upstream ) {
+ final int[] textureSize = dist.textureSize;
+ initFBOs(gl, textureSize[0], textureSize[1]);
+ upstream.init(drawable);
+ }
+
+ gl.setSwapInterval(1);
+ }
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ // FIXME complete release
+ if( null != upstream ) {
+ upstream.dispose(drawable);
+ fbos[0].destroy(gl);
+ fbos[1].destroy(gl);
+ }
+ if( ownsDist ) {
+ dist.dispose(gl);
+ }
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final ovrFrameTiming frameTiming = OVR.ovrHmd_BeginFrameTiming(dist.hmdCtx, 0);
+
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(0 < numSamples) {
+ gl.glEnable(GL.GL_MULTISAMPLE);
+ }
+
+ // FIXME: Instead of setting the viewport,
+ // it's better to change the projection matrix!
+ if( null != upstream ) {
+ for(int eyeNum=0; eyeNum<2; eyeNum++) {
+ // final ovrPosef eyeRenderPose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeNum);
+ // final float[] eyePos = OVRUtil.getVec3f(eyeRenderPose.getPosition());
+ fbos[eyeNum].bind(gl);
+
+ final OVRDistortion.EyeData eyeDist = dist.eyes[eyeNum];
+ final int[] viewport = eyeDist.viewport;
+ gl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+
+ dist.getSBSUpstreamPMV(eyeNum, eyePos, eyeYaw, frustumNear, frustumFar, mat4Projection, mat4Modelview);
+ upstream.setProjectionModelview(drawable, mat4Projection, mat4Modelview);
+ upstream.display(drawable, eyeNum > 0 ? GLEventListener2.DISPLAY_REPEAT : 0);
+ fbos[eyeNum].unbind(gl);
+ }
+ gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ }
+
+ gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT);
+ gl.glActiveTexture(GL.GL_TEXTURE0 + dist.texUnit0.intValue());
+
+ if( null != upstream ) {
+ dist.displayOneEyePre(gl, frameTiming.getTimewarpPointSeconds());
+ fbos[0].use(gl, fboTexs[0]);
+ dist.displayOneEye(gl, 0);
+ fbos[0].unuse(gl);
+ fbos[1].use(gl, fboTexs[1]);
+ dist.displayOneEye(gl, 1);
+ fbos[1].unuse(gl);
+ dist.displayOneEyePost(gl);
+ } else {
+ dist.display(gl, frameTiming.getTimewarpPointSeconds());
+ }
+
+ if( !drawable.getAutoSwapBufferMode() ) {
+ drawable.swapBuffers();
+ }
+ OVR.ovrHmd_EndFrameTiming(dist.hmdCtx);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
+ if( !drawable.getAutoSwapBufferMode() ) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ gl.glViewport(0, 0, width, height);
+ }
+ }
+}
diff --git a/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java
new file mode 100644
index 000000000..22b8b37ce
--- /dev/null
+++ b/src/oculusvr/classes/com/jogamp/opengl/oculusvr/OVRSBSRendererSingleFBO.java
@@ -0,0 +1,206 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.oculusvr;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLEventListener2;
+
+import jogamp.opengl.oculusvr.OVRDistortion;
+
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.ovrFrameTiming;
+import com.jogamp.opengl.FBObject;
+import com.jogamp.opengl.FBObject.TextureAttachment;
+import com.jogamp.opengl.FBObject.Attachment.Type;
+import com.jogamp.opengl.math.FloatUtil;
+
+/**
+ * OculusVR (OVR) <i>Side By Side</i> Distortion Renderer utilizing {@link OVRDistortion}
+ * implementing {@link GLEventListener} for convenience.
+ * <p>
+ * Implementation renders an {@link GLEventListener2} instance
+ * side-by-side within one {@link FBObject} according to {@link OVRDistortion}.
+ * </p>
+ */
+public class OVRSBSRendererSingleFBO implements GLEventListener {
+
+ private final float[] mat4Projection = new float[16];
+ private final float[] mat4Modelview = new float[16];
+ private final OVRDistortion dist;
+ private final boolean ownsDist;
+ private final GLEventListener2 upstream;
+ private final FBObject fbo0;
+
+ // final float[] eyePos = { 0.0f, 1.6f, -5.0f };
+ private final float[] eyePos = { 0.0f, 0.0f, -20.0f };
+ // EyePos.y = ovrHmd_GetFloat(HMD, OVR_KEY_EYE_HEIGHT, EyePos.y);
+ private float eyeYaw = FloatUtil.PI; // 180 degrees in radians
+ private float frustumNear = 0.1f;
+ private float frustumFar = 7000f;
+ private int numSamples;
+ private TextureAttachment fbo0Tex;
+
+ public OVRSBSRendererSingleFBO(final OVRDistortion dist, final boolean ownsDist, final GLEventListener2 upstream, final int numSamples) {
+ this.dist = dist;
+ this.ownsDist = ownsDist;
+ this.upstream = upstream;
+ this.numSamples = numSamples;
+ fbo0 = new FBObject();
+ }
+
+ /**
+ *
+ * @param eyePos
+ * @param eyeYaw
+ * @param frustumNear
+ * @param frustumFar
+ */
+ public void setUpstreamPMVParams(final float[] eyePos, final float eyeYaw, final float frustumNear, final float frustumFar) {
+ System.arraycopy(eyePos, 0, this.eyePos, 0, 3);
+ this.eyeYaw = eyeYaw;
+ this.frustumNear = frustumNear;
+ this.frustumFar = frustumFar;
+ }
+
+ private void initFBOs(final GL gl, final int width, final int height) {
+ // remove all texture attachments, since MSAA uses just color-render-buffer
+ // and non-MSAA uses texture2d-buffer
+ fbo0.detachAllColorbuffer(gl);
+
+ fbo0.reset(gl, width, height, numSamples, false);
+ numSamples = fbo0.getNumSamples();
+
+ if(numSamples>0) {
+ fbo0.attachColorbuffer(gl, 0, true);
+ fbo0.resetSamplingSink(gl);
+ fbo0Tex = fbo0.getSamplingSink();
+ } else {
+ fbo0Tex = fbo0.attachTexture2D(gl, 0, true);
+ }
+ numSamples=fbo0.getNumSamples();
+ fbo0.attachRenderbuffer(gl, Type.DEPTH, 24);
+ fbo0.unbind(gl);
+ }
+
+ @SuppressWarnings("unused")
+ private void resetFBOs(final GL gl, final int width, final int height) {
+ fbo0.reset(gl, width, height, numSamples, true);
+ numSamples = fbo0.getNumSamples();
+ if(numSamples>0) {
+ fbo0Tex = fbo0.getSamplingSink();
+ } else {
+ fbo0Tex = (TextureAttachment) fbo0.getColorbuffer(0);
+ }
+ }
+
+ @Override
+ public void init(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ dist.init(gl);
+
+ // We will do some offscreen rendering, setup FBO...
+ if( null != upstream ) {
+ final int[] textureSize = dist.textureSize;
+ initFBOs(gl, textureSize[0], textureSize[1]);
+ upstream.init(drawable);
+ }
+
+ gl.setSwapInterval(1);
+ }
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ // FIXME complete release
+ if( null != upstream ) {
+ upstream.dispose(drawable);
+ fbo0.destroy(gl);
+ }
+ if( ownsDist ) {
+ dist.dispose(gl);
+ }
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable) {
+ final ovrFrameTiming frameTiming = OVR.ovrHmd_BeginFrameTiming(dist.hmdCtx, 0);
+
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ if(0 < numSamples) {
+ gl.glEnable(GL.GL_MULTISAMPLE);
+ }
+
+ // FIXME: Instead of setting the viewport,
+ // it's better to change the projection matrix!
+ if( null != upstream ) {
+ fbo0.bind(gl);
+
+ for(int eyeNum=0; eyeNum<2; eyeNum++) {
+ // final ovrPosef eyeRenderPose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeNum);
+ // final float[] eyePos = OVRUtil.getVec3f(eyeRenderPose.getPosition());
+ final OVRDistortion.EyeData eyeDist = dist.eyes[eyeNum];
+ final int[] viewport = eyeDist.viewport;
+ gl.glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+
+ dist.getSBSUpstreamPMV(eyeNum, eyePos, eyeYaw, frustumNear, frustumFar, mat4Projection, mat4Modelview);
+ upstream.setProjectionModelview(drawable, mat4Projection, mat4Modelview);
+ upstream.display(drawable, eyeNum > 0 ? GLEventListener2.DISPLAY_REPEAT | GLEventListener2.DISPLAY_DONTCLEAR : 0);
+ }
+ fbo0.unbind(gl);
+ gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
+ }
+
+ gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT);
+ gl.glActiveTexture(GL.GL_TEXTURE0 + dist.texUnit0.intValue());
+
+ if( null != upstream ) {
+ fbo0.use(gl, fbo0Tex);
+ dist.display(gl, frameTiming.getTimewarpPointSeconds());
+ fbo0.unuse(gl);
+ } else {
+ dist.display(gl, frameTiming.getTimewarpPointSeconds());
+ }
+
+ if( !drawable.getAutoSwapBufferMode() ) {
+ drawable.swapBuffers();
+ }
+ OVR.ovrHmd_EndFrameTiming(dist.hmdCtx);
+ }
+
+ @Override
+ public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
+ if( !drawable.getAutoSwapBufferMode() ) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ gl.glViewport(0, 0, width, height);
+ }
+ }
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java
new file mode 100644
index 000000000..590b0e834
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRDistortion.java
@@ -0,0 +1,638 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.opengl.oculusvr;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLArrayData;
+import javax.media.opengl.GLException;
+import javax.media.opengl.GLUniformData;
+
+import com.jogamp.common.nio.Buffers;
+import com.jogamp.common.os.Platform;
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OVRException;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.oculusvr.ovrDistortionMesh;
+import com.jogamp.oculusvr.ovrDistortionVertex;
+import com.jogamp.oculusvr.ovrEyeRenderDesc;
+import com.jogamp.oculusvr.ovrFovPort;
+import com.jogamp.oculusvr.ovrMatrix4f;
+import com.jogamp.oculusvr.ovrPosef;
+import com.jogamp.oculusvr.ovrRecti;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.oculusvr.ovrVector2f;
+import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.math.FloatUtil;
+import com.jogamp.opengl.math.Quaternion;
+import com.jogamp.opengl.math.VectorUtil;
+import com.jogamp.opengl.util.GLArrayDataServer;
+import com.jogamp.opengl.util.glsl.ShaderCode;
+import com.jogamp.opengl.util.glsl.ShaderProgram;
+
+/**
+ * OculusVR Distortion Data and OpenGL Renderer Utility
+ */
+public class OVRDistortion {
+ public static final float[] VEC3_UP = { 0f, 1f, 0f };
+ public static final float[] VEC3_FORWARD = { 0f, 0f, -1f };
+
+ private static final String shaderPrefix01 = "dist01";
+ private static final String shaderTimewarpSuffix = "_timewarp";
+ private static final String shaderChromaSuffix = "_chroma";
+ private static final String shaderPlainSuffix = "_plain";
+
+ public static boolean useTimewarp(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_TimeWarp ) ; }
+ public static boolean useChromatic(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_Chromatic ) ; }
+ public static boolean useVignette(final int distortionCaps) { return 0 != ( distortionCaps & OVR.ovrDistortionCap_Vignette ) ; }
+
+ public static class EyeData {
+ public final int eyeName;
+ public final int distortionCaps;
+ public final int vertexCount;
+ public final int indexCount;
+ public final int[/*4*/] viewport;
+ public final float[/*3*/] viewAdjust;
+
+ public final GLUniformData eyeToSourceUVScale;
+ public final GLUniformData eyeToSourceUVOffset;
+ public final GLUniformData eyeRotationStart;
+ public final GLUniformData eyeRotationEnd;
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ public final GLArrayDataServer iVBO;
+ public final GLArrayData vboPos, vboParams, vboTexCoordsR, vboTexCoordsG, vboTexCoordsB;
+ public final GLArrayDataServer indices;
+
+ public final ovrEyeRenderDesc eyeRenderDesc;
+ public final ovrFovPort eyeRenderFov;
+
+ public ovrPosef eyeRenderPose;
+ public final Quaternion eyeRenderPoseOrientation;
+ public final float[] eyeRenderPosePosition;
+
+ public final boolean useTimewarp() { return OVRDistortion.useTimewarp(distortionCaps); }
+ public final boolean useChromatic() { return OVRDistortion.useChromatic(distortionCaps); }
+ public final boolean useVignette() { return OVRDistortion.useVignette(distortionCaps); }
+
+ private EyeData(final OvrHmdContext hmdCtx, final int distortionCaps,
+ final ovrEyeRenderDesc eyeRenderDesc, final ovrSizei ovrTextureSize, final int[] eyeRenderViewport) {
+ this.eyeName = eyeRenderDesc.getEye();
+ this.distortionCaps = distortionCaps;
+ viewport = new int[4];
+ viewAdjust = new float[3];
+ System.arraycopy(eyeRenderViewport, 0, viewport, 0, 4);
+
+ final FloatBuffer fstash = Buffers.newDirectFloatBuffer(2+2+16+26);
+
+ eyeToSourceUVScale = new GLUniformData("ovr_EyeToSourceUVScale", 2, Buffers.slice2Float(fstash, 0, 2));
+ eyeToSourceUVOffset = new GLUniformData("ovr_EyeToSourceUVOffset", 2, Buffers.slice2Float(fstash, 2, 2));
+
+ if( useTimewarp() ) {
+ eyeRotationStart = new GLUniformData("ovr_EyeRotationStart", 4, 4, Buffers.slice2Float(fstash, 4, 16));
+ eyeRotationEnd = new GLUniformData("ovr_EyeRotationEnd", 4, 4, Buffers.slice2Float(fstash, 20, 16));
+ } else {
+ eyeRotationStart = null;
+ eyeRotationEnd = null;
+ }
+
+ this.eyeRenderDesc = eyeRenderDesc;
+ this.eyeRenderFov = eyeRenderDesc.getFov();
+
+ this.eyeRenderPoseOrientation = new Quaternion();
+ this.eyeRenderPosePosition = new float[3];
+
+
+ final ovrDistortionMesh meshData = ovrDistortionMesh.create();
+ final ovrFovPort fov = eyeRenderDesc.getFov();
+
+ if( !OVR.ovrHmd_CreateDistortionMesh(hmdCtx, eyeName, fov, distortionCaps, meshData) ) {
+ throw new OVRException("Failed to create meshData for eye "+eyeName+" and "+OVRUtil.toString(fov));
+ }
+ vertexCount = meshData.getVertexCount();
+ indexCount = meshData.getIndexCount();
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ final boolean useChromatic = useChromatic();
+ final boolean useVignette = useVignette();
+ final int compsPerElement = 2+2+2+( useChromatic ? 2+2 /* texCoordG + texCoordB */: 0 );
+ iVBO = GLArrayDataServer.createGLSLInterleaved(compsPerElement, GL.GL_FLOAT, false, vertexCount, GL.GL_STATIC_DRAW);
+ vboPos = iVBO.addGLSLSubArray("ovr_Position", 2, GL.GL_ARRAY_BUFFER);
+ vboParams = iVBO.addGLSLSubArray("ovr_Params", 2, GL.GL_ARRAY_BUFFER);
+ vboTexCoordsR = iVBO.addGLSLSubArray("ovr_TexCoordR", 2, GL.GL_ARRAY_BUFFER);
+ if( useChromatic ) {
+ vboTexCoordsG = iVBO.addGLSLSubArray("ovr_TexCoordG", 2, GL.GL_ARRAY_BUFFER);
+ vboTexCoordsB = iVBO.addGLSLSubArray("ovr_TexCoordB", 2, GL.GL_ARRAY_BUFFER);
+ } else {
+ vboTexCoordsG = null;
+ vboTexCoordsB = null;
+ }
+ indices = GLArrayDataServer.createData(1, GL2ES2.GL_SHORT, indexCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER);
+ OVRUtil.copyVec3fToFloat(eyeRenderDesc.getViewAdjust(), viewAdjust);
+
+ // Setup: eyeToSourceUVScale, eyeToSourceUVOffset
+ {
+ final ovrVector2f[] uvScaleOffsetOut = new ovrVector2f[2];
+ uvScaleOffsetOut[0] = ovrVector2f.create(); // FIXME: remove ctor / double check
+ uvScaleOffsetOut[1] = ovrVector2f.create();
+
+ final ovrRecti ovrEyeRenderViewport = OVRUtil.createOVRRecti(eyeRenderViewport);
+ OVR.ovrHmd_GetRenderScaleAndOffset(fov, ovrTextureSize, ovrEyeRenderViewport, uvScaleOffsetOut);
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX."+eyeName+": fov "+OVRUtil.toString(fov));
+ System.err.println("XXX."+eyeName+": uvScale "+OVRUtil.toString(uvScaleOffsetOut[0]));
+ System.err.println("XXX."+eyeName+": uvOffset "+OVRUtil.toString(uvScaleOffsetOut[0]));
+ System.err.println("XXX."+eyeName+": textureSize "+OVRUtil.toString(ovrTextureSize));
+ System.err.println("XXX."+eyeName+": viewport "+OVRUtil.toString(ovrEyeRenderViewport));
+ }
+ final FloatBuffer eyeToSourceUVScaleFB = eyeToSourceUVScale.floatBufferValue();
+ eyeToSourceUVScaleFB.put(0, uvScaleOffsetOut[0].getX());
+ eyeToSourceUVScaleFB.put(1, uvScaleOffsetOut[0].getY());
+ final FloatBuffer eyeToSourceUVOffsetFB = eyeToSourceUVOffset.floatBufferValue();
+ eyeToSourceUVOffsetFB.put(0, uvScaleOffsetOut[1].getX());
+ eyeToSourceUVOffsetFB.put(1, uvScaleOffsetOut[1].getY());
+ }
+
+ /** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
+ final FloatBuffer iVBOFB = (FloatBuffer)iVBO.getBuffer();
+ final ovrDistortionVertex[] ovRes = new ovrDistortionVertex[1];
+ ovRes[0] = ovrDistortionVertex.create(); // FIXME: remove ctor / double check
+
+ for ( int vertNum = 0; vertNum < vertexCount; vertNum++ ) {
+ final ovrDistortionVertex ov = meshData.getPVertexData(vertNum, ovRes)[0];
+ ovrVector2f v;
+
+ // pos
+ v = ov.getPos();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ // params
+ if( useVignette ) {
+ iVBOFB.put(ov.getVignetteFactor());
+ } else {
+ iVBOFB.put(1.0f);
+ }
+ iVBOFB.put(ov.getTimeWarpFactor());
+
+ // texCoordR
+ v = ov.getTexR();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ if( useChromatic ) {
+ // texCoordG
+ v = ov.getTexG();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+
+ // texCoordB
+ v = ov.getTexB();
+ iVBOFB.put(v.getX());
+ iVBOFB.put(v.getY());
+ }
+ }
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX."+eyeName+": iVBO "+iVBO);
+ }
+ {
+ final ShortBuffer in = meshData.getPIndexData();
+ final ShortBuffer out = (ShortBuffer) indices.getBuffer();
+ out.put(in);
+ }
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX."+eyeName+": idx "+indices);
+ System.err.println("XXX."+eyeName+": distEye "+this);
+ }
+ OVR.ovrHmd_DestroyDistortionMesh(meshData);
+ }
+
+ private void linkData(final GL2ES2 gl, final ShaderProgram sp) {
+ if( 0 > vboPos.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboPos);
+ }
+ if( 0 > vboParams.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboParams);
+ }
+ if( 0 > vboTexCoordsR.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsR);
+ }
+ if( useChromatic() ) {
+ if( 0 > vboTexCoordsG.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsG);
+ }
+ if( 0 > vboTexCoordsB.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+vboTexCoordsB);
+ }
+ }
+ if( 0 > eyeToSourceUVScale.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeToSourceUVScale);
+ }
+ if( 0 > eyeToSourceUVOffset.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeToSourceUVOffset);
+ }
+ if( useTimewarp() ) {
+ if( 0 > eyeRotationStart.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeRotationStart);
+ }
+ if( 0 > eyeRotationEnd.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+eyeRotationEnd);
+ }
+ }
+ iVBO.seal(gl, true);
+ iVBO.enableBuffer(gl, false);
+ indices.seal(gl, true);
+ indices.enableBuffer(gl, false);
+ }
+
+ public void dispose(final GL2ES2 gl) {
+ iVBO.destroy(gl);
+ indices.destroy(gl);
+ }
+ public void enableVBO(final GL2ES2 gl, final boolean enable) {
+ iVBO.enableBuffer(gl, enable);
+ indices.bindBuffer(gl, enable); // keeps VBO binding if enable:=true
+ }
+
+ public void updateUniform(final GL2ES2 gl, final ShaderProgram sp) {
+ gl.glUniform(eyeToSourceUVScale);
+ gl.glUniform(eyeToSourceUVOffset);
+ if( useTimewarp() ) {
+ gl.glUniform(eyeRotationStart);
+ gl.glUniform(eyeRotationEnd);
+ }
+ }
+
+ public void updateTimewarp(final OvrHmdContext hmdCtx, final ovrPosef eyeRenderPose, final float[] mat4Tmp1, final float[] mat4Tmp2) {
+ final ovrMatrix4f[] timeWarpMatrices = new ovrMatrix4f[2];
+ timeWarpMatrices[0] = ovrMatrix4f.create(); // FIXME: remove ctor / double check
+ timeWarpMatrices[1] = ovrMatrix4f.create();
+ OVR.ovrHmd_GetEyeTimewarpMatrices(hmdCtx, eyeName, eyeRenderPose, timeWarpMatrices);
+
+ final float[] eyeRotationStartM = FloatUtil.transposeMatrix(timeWarpMatrices[0].getM(0, mat4Tmp1), mat4Tmp2);
+ final FloatBuffer eyeRotationStartU = eyeRotationStart.floatBufferValue();
+ eyeRotationStartU.put(eyeRotationStartM);
+ eyeRotationStartU.rewind();
+
+ final float[] eyeRotationEndM = FloatUtil.transposeMatrix(timeWarpMatrices[1].getM(0, mat4Tmp1), mat4Tmp2);
+ final FloatBuffer eyeRotationEndU = eyeRotationEnd.floatBufferValue();
+ eyeRotationEndU.put(eyeRotationEndM);
+ eyeRotationEndU.rewind();
+ }
+
+ /**
+ * Updates {@link #eyeRenderPose} and it's extracted
+ * {@link #eyeRenderPoseOrientation} and {@link #eyeRenderPosePosition}.
+ * @param hmdCtx used get the {@link #eyeRenderPose} via {@link OVR#ovrHmd_GetEyePose(OvrHmdContext, int)}
+ */
+ public void updateEyePose(final OvrHmdContext hmdCtx) {
+ eyeRenderPose = OVR.ovrHmd_GetEyePose(hmdCtx, eyeName);
+ OVRUtil.copyToQuaternion(eyeRenderPose.getOrientation(), eyeRenderPoseOrientation);
+ OVRUtil.copyVec3fToFloat(eyeRenderPose.getPosition(), eyeRenderPosePosition);
+ }
+
+ @Override
+ public String toString() {
+ return "Eye["+eyeName+", viewport "+viewport[0]+"/"+viewport[1]+" "+viewport[2]+"x"+viewport[3]+
+ " viewAdjust["+viewAdjust[0]+", "+viewAdjust[1]+", "+viewAdjust[2]+
+ "], vertices "+vertexCount+", indices "+indexCount+
+ ", uvScale["+eyeToSourceUVScale.floatBufferValue().get(0)+", "+eyeToSourceUVScale.floatBufferValue().get(1)+
+ "], uvOffset["+eyeToSourceUVOffset.floatBufferValue().get(0)+", "+eyeToSourceUVOffset.floatBufferValue().get(1)+
+ "], desc"+OVRUtil.toString(eyeRenderDesc)+"]";
+ }
+ }
+
+ public final OvrHmdContext hmdCtx;
+ public final EyeData[] eyes;
+ public final int distortionCaps;
+ public final int[/*2*/] textureSize;
+ public final GLUniformData texUnit0;
+ public final boolean usesDistMesh;
+
+ private final float[] mat4Tmp1 = new float[16];
+ private final float[] mat4Tmp2 = new float[16];
+ private final float[] vec3Tmp1 = new float[3];
+ private final float[] vec3Tmp2 = new float[3];
+ private final float[] vec3Tmp3 = new float[3];
+
+ private ShaderProgram sp;
+
+ @Override
+ public String toString() {
+ return "OVRDist[caps 0x"+Integer.toHexString(distortionCaps)+", "+
+ ", tex "+textureSize[0]+"x"+textureSize[1]+
+ ", vignette "+useVignette()+", chromatic "+useChromatic()+", timewarp "+useTimewarp()+
+ ", "+Platform.NEWLINE+" "+eyes[0]+", "+Platform.NEWLINE+" "+eyes[1]+"]";
+ }
+
+ public static OVRDistortion create(final OvrHmdContext hmdCtx, final boolean sbsSingleTexture,
+ final ovrFovPort[] eyeFov, final float pixelsPerDisplayPixel, final int distortionCaps) {
+ final ovrEyeRenderDesc[] eyeRenderDesc = new ovrEyeRenderDesc[2];
+ eyeRenderDesc[0] = OVR.ovrHmd_GetRenderDesc(hmdCtx, OVR.ovrEye_Left, eyeFov[0]);
+ eyeRenderDesc[1] = OVR.ovrHmd_GetRenderDesc(hmdCtx, OVR.ovrEye_Right, eyeFov[1]);
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX: eyeRenderDesc[0] "+OVRUtil.toString(eyeRenderDesc[0]));
+ System.err.println("XXX: eyeRenderDesc[1] "+OVRUtil.toString(eyeRenderDesc[1]));
+ }
+
+ final ovrSizei recommenedTex0Size = OVR.ovrHmd_GetFovTextureSize(hmdCtx, OVR.ovrEye_Left, eyeRenderDesc[0].getFov(), pixelsPerDisplayPixel);
+ final ovrSizei recommenedTex1Size = OVR.ovrHmd_GetFovTextureSize(hmdCtx, OVR.ovrEye_Right, eyeRenderDesc[1].getFov(), pixelsPerDisplayPixel);
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX: recommenedTex0Size "+OVRUtil.toString(recommenedTex0Size));
+ System.err.println("XXX: recommenedTex1Size "+OVRUtil.toString(recommenedTex1Size));
+ }
+ final int[] textureSize = new int[2];
+ if( sbsSingleTexture ) {
+ textureSize[0] = recommenedTex0Size.getW() + recommenedTex1Size.getW();
+ } else {
+ textureSize[0] = Math.max(recommenedTex0Size.getW(), recommenedTex1Size.getW());
+ }
+ textureSize[1] = Math.max(recommenedTex0Size.getH(), recommenedTex1Size.getH());
+ if( OVRUtil.DEBUG ) {
+ System.err.println("XXX: textureSize "+textureSize[0]+"x"+textureSize[1]);
+ }
+
+ final int[][] eyeRenderViewports = new int[2][4];
+ if( sbsSingleTexture ) {
+ eyeRenderViewports[0][0] = 0;
+ eyeRenderViewports[0][1] = 0;
+ eyeRenderViewports[0][2] = textureSize[0] / 2;
+ eyeRenderViewports[0][3] = textureSize[1];
+ eyeRenderViewports[1][0] = (textureSize[0] + 1) / 2;
+ eyeRenderViewports[1][1] = 0;
+ eyeRenderViewports[1][2] = textureSize[0] / 2;
+ eyeRenderViewports[1][3] = textureSize[1];
+ } else {
+ eyeRenderViewports[0][0] = 0;
+ eyeRenderViewports[0][1] = 0;
+ eyeRenderViewports[0][2] = textureSize[0];
+ eyeRenderViewports[0][3] = textureSize[1];
+ eyeRenderViewports[1][0] = 0;
+ eyeRenderViewports[1][1] = 0;
+ eyeRenderViewports[1][2] = textureSize[0];
+ eyeRenderViewports[1][3] = textureSize[1];
+ }
+ return new OVRDistortion(hmdCtx, sbsSingleTexture, eyeRenderDesc, textureSize, eyeRenderViewports, distortionCaps, 0);
+ }
+
+ public OVRDistortion(final OvrHmdContext hmdCtx, final boolean sbsSingleTexture, final ovrEyeRenderDesc[] eyeRenderDescs,
+ final int[] textureSize, final int[][] eyeRenderViewports,
+ final int distortionCaps, final int textureUnit) {
+ this.hmdCtx = hmdCtx;
+ this.eyes = new EyeData[2];
+ this.distortionCaps = distortionCaps;
+ this.textureSize = new int[2];
+ System.arraycopy(textureSize, 0, this.textureSize, 0, 2);
+
+ texUnit0 = new GLUniformData("ovr_Texture0", textureUnit);
+ usesDistMesh = true;
+
+ final ovrSizei ovrTextureSize = OVRUtil.createOVRSizei(textureSize);
+ eyes[0] = new EyeData(hmdCtx, distortionCaps, eyeRenderDescs[0], ovrTextureSize, eyeRenderViewports[0]);
+ eyes[1] = new EyeData(hmdCtx, distortionCaps, eyeRenderDescs[1], ovrTextureSize, eyeRenderViewports[1]);
+ sp = null;
+ }
+
+ public final boolean useTimewarp() { return useTimewarp(distortionCaps); }
+ public final boolean useChromatic() { return useChromatic(distortionCaps); }
+ public final boolean useVignette() { return useVignette(distortionCaps); }
+
+ public void updateTimewarp(final ovrPosef eyeRenderPose, final int eyeNum) {
+ eyes[eyeNum].updateTimewarp(hmdCtx, eyeRenderPose, mat4Tmp1, mat4Tmp2);
+ }
+ public void updateTimewarp(final ovrPosef[] eyeRenderPoses) {
+ eyes[0].updateTimewarp(hmdCtx, eyeRenderPoses[0], mat4Tmp1, mat4Tmp2);
+ eyes[1].updateTimewarp(hmdCtx, eyeRenderPoses[1], mat4Tmp1, mat4Tmp2);
+ }
+
+ public void enableVBO(final GL2ES2 gl, final boolean enable, final int eyeNum) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ eyes[eyeNum].enableVBO(gl, enable);
+ }
+
+ public void updateUniforms(final GL2ES2 gl, final int eyeNum) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ gl.glUniform(texUnit0);
+ eyes[eyeNum].updateUniform(gl, sp);
+ }
+
+ public final ShaderProgram getShaderProgram() { return sp; }
+
+ public void init(final GL2ES2 gl) {
+ if( OVRUtil.DEBUG ) {
+ System.err.println(JoglVersion.getGLInfo(gl, null).toString());
+ }
+ if( null != sp ) {
+ throw new IllegalStateException("Already initialized");
+ }
+ final String vertexShaderBasename;
+ final String fragmentShaderBasename;
+ {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(shaderPrefix01);
+ if( !useChromatic() && !useTimewarp() ) {
+ sb.append(shaderPlainSuffix);
+ } else if( useChromatic() && !useTimewarp() ) {
+ sb.append(shaderChromaSuffix);
+ } else if( useTimewarp() ) {
+ sb.append(shaderTimewarpSuffix);
+ if( useChromatic() ) {
+ sb.append(shaderChromaSuffix);
+ }
+ }
+ vertexShaderBasename = sb.toString();
+ sb.setLength(0);
+ sb.append(shaderPrefix01);
+ if( useChromatic() ) {
+ sb.append(shaderChromaSuffix);
+ } else {
+ sb.append(shaderPlainSuffix);
+ }
+ fragmentShaderBasename = sb.toString();
+ }
+ final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, OVRDistortion.class, "shader",
+ "shader/bin", vertexShaderBasename, true);
+ final ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, OVRDistortion.class, "shader",
+ "shader/bin", fragmentShaderBasename, true);
+ vp0.defaultShaderCustomization(gl, true, true);
+ fp0.defaultShaderCustomization(gl, true, true);
+
+ sp = new ShaderProgram();
+ sp.add(gl, vp0, System.err);
+ sp.add(gl, fp0, System.err);
+ if(!sp.link(gl, System.err)) {
+ throw new GLException("could not link program: "+sp);
+ }
+ sp.useProgram(gl, true);
+ if( 0 > texUnit0.setLocation(gl, sp.program()) ) {
+ throw new OVRException("Couldn't locate "+texUnit0);
+ }
+ eyes[0].linkData(gl, sp);
+ eyes[1].linkData(gl, sp);
+ sp.useProgram(gl, false);
+ }
+
+ public void dispose(final GL2ES2 gl) {
+ sp.useProgram(gl, false);
+ eyes[0].dispose(gl);
+ eyes[1].dispose(gl);
+ sp.destroy(gl);
+ }
+
+ public void display(final GL2ES2 gl, final double timewarpPointSeconds) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if( useTimewarp() ) {
+ OVR.ovr_WaitTillTime(timewarpPointSeconds);
+ }
+ gl.glDisable(GL.GL_CULL_FACE);
+ gl.glDisable(GL.GL_DEPTH_TEST);
+ gl.glDisable(GL.GL_BLEND);
+
+ if( !gl.isGLcore() ) {
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ sp.useProgram(gl, true);
+
+ gl.glUniform(texUnit0);
+
+ for(int eyeNum=0; eyeNum<2; eyeNum++) {
+ final EyeData eye = eyes[eyeNum];
+ if( useTimewarp() ) {
+ eye.updateTimewarp(hmdCtx, eye.eyeRenderPose, mat4Tmp1, mat4Tmp2);
+ }
+ eye.updateUniform(gl, sp);
+ eye.enableVBO(gl, true);
+ if( usesDistMesh ) {
+ gl.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL2ES2.GL_UNSIGNED_SHORT, 0);
+ } else {
+ gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, eye.vertexCount);
+ }
+ eyes[eyeNum].enableVBO(gl, false);
+ }
+
+ sp.useProgram(gl, false);
+ }
+
+ public void displayOneEyePre(final GL2ES2 gl, final double timewarpPointSeconds) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if( useTimewarp() ) {
+ OVR.ovr_WaitTillTime(timewarpPointSeconds);
+ }
+ gl.glDisable(GL.GL_CULL_FACE);
+ gl.glDisable(GL.GL_DEPTH_TEST);
+ gl.glDisable(GL.GL_BLEND);
+
+ if( !gl.isGLcore() ) {
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ sp.useProgram(gl, true);
+
+ gl.glUniform(texUnit0);
+ }
+
+ public void displayOneEye(final GL2ES2 gl, final int eyeNum) {
+ if( null == sp ) {
+ throw new IllegalStateException("Not initialized");
+ }
+ final EyeData eye = eyes[eyeNum];
+ if( useTimewarp() ) {
+ eye.updateTimewarp(hmdCtx, eye.eyeRenderPose, mat4Tmp1, mat4Tmp2);
+ }
+ eye.updateUniform(gl, sp);
+ eye.enableVBO(gl, true);
+ if( usesDistMesh ) {
+ gl.glDrawElements(GL.GL_TRIANGLES, eye.indexCount, GL2ES2.GL_UNSIGNED_SHORT, 0);
+ } else {
+ gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, eye.vertexCount);
+ }
+ eyes[eyeNum].enableVBO(gl, false);
+ }
+
+ public void displayOneEyePost(final GL2ES2 gl) {
+ sp.useProgram(gl, false);
+ }
+
+ /**
+ * Calculates the <i>Side By Side</i>, SBS, projection- and modelview matrix for one eye.
+ * <p>
+ * Method also issues {@link EyeData#updateEyePose(OvrHmdContext)}.
+ * </p>
+ * @param eyeNum eye denominator
+ * @param eyePos float[3] eye postion vector
+ * @param eyeYaw eye direction, i.e. {@link FloatUtil#PI} for 180 degrees
+ * @param near frustum near value
+ * @param far frustum far value
+ * @param mat4Projection float[16] projection matrix result
+ * @param mat4Modelview float[16] modelview matrix result
+ */
+ public void getSBSUpstreamPMV(final int eyeNum, final float[] eyePos, final float eyeYaw, final float near, final float far,
+ final float[] mat4Projection, final float[] mat4Modelview) {
+ final EyeData eyeDist = eyes[eyeNum];
+
+ eyeDist.updateEyePose(hmdCtx);
+
+ //
+ // Projection
+ //
+ final ovrMatrix4f pm = OVR.ovrMatrix4f_Projection(eyeDist.eyeRenderFov, near, far, true /* rightHanded*/);
+ /* final float[] mat4Projection = */ FloatUtil.transposeMatrix(pm.getM(0, mat4Tmp1), mat4Projection);
+
+ //
+ // Modelview
+ //
+ final Quaternion rollPitchYaw = new Quaternion();
+ rollPitchYaw.rotateByAngleY(eyeYaw);
+ final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, eyeDist.eyeRenderPosePosition, 0);
+ VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyePos);
+
+ rollPitchYaw.mult(eyeDist.eyeRenderPoseOrientation);
+ final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VEC3_UP, 0);
+ final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VEC3_FORWARD, 0);
+ final float[] center = VectorUtil.addVec3(forward, shiftedEyePos, forward);
+
+ final float[] mLookAt = FloatUtil.makeLookAt(mat4Tmp2, 0, shiftedEyePos, 0, center, 0, up, 0, mat4Tmp1);
+ final float[] mViewAdjust = FloatUtil.makeTranslation(mat4Modelview, true, eyeDist.viewAdjust[0], eyeDist.viewAdjust[1], eyeDist.viewAdjust[2]);
+
+ /* mat4Modelview = */ FloatUtil.multMatrix(mViewAdjust, mLookAt);
+ }
+
+
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java
new file mode 100644
index 000000000..e28a50203
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRUtil.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.opengl.oculusvr;
+
+import jogamp.opengl.Debug;
+
+import com.jogamp.oculusvr.ovrEyeRenderDesc;
+import com.jogamp.oculusvr.ovrFovPort;
+import com.jogamp.oculusvr.ovrQuatf;
+import com.jogamp.oculusvr.ovrRecti;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.oculusvr.ovrVector2f;
+import com.jogamp.oculusvr.ovrVector2i;
+import com.jogamp.oculusvr.ovrVector3f;
+import com.jogamp.opengl.math.Quaternion;
+
+/**
+ * OculusVR Data Conversion Helper Functions
+ */
+public class OVRUtil {
+ public static final boolean DEBUG = Debug.debug("OVR");
+
+ public static ovrRecti createOVRRecti(final int[] rect) {
+ final ovrRecti res = ovrRecti.create();
+ final ovrVector2i pos = res.getPos();
+ final ovrSizei size = res.getSize();
+ pos.setX(rect[0]);
+ pos.setY(rect[1]);
+ size.setW(rect[2]);
+ size.setH(rect[3]);
+ return res;
+ }
+ public static ovrRecti[] createOVRRectis(final int[][] rects) {
+ final ovrRecti[] res = new ovrRecti[rects.length];
+ for(int i=0; i<res.length; i++) {
+ res[0] = createOVRRecti(rects[i]);
+ }
+ return res;
+ }
+ public static ovrSizei createOVRSizei(final int[] size) {
+ final ovrSizei res = ovrSizei.create();
+ res.setW(size[0]);
+ res.setH(size[1]);
+ return res;
+ }
+ public static Quaternion getQuaternion(final ovrQuatf q) {
+ return new Quaternion(q.getX(), q.getY(), q.getZ(), q.getW());
+ }
+ public static void copyToQuaternion(final ovrQuatf in, final Quaternion out) {
+ out.set(in.getX(), in.getY(), in.getZ(), in.getW());
+ }
+ public static float[] getVec3f(final ovrVector3f v) {
+ return new float[] { v.getX(), v.getY(), v.getZ() };
+ }
+ public static void copyVec3fToFloat(final ovrVector3f v, final float[] res) {
+ res[0] = v.getX();
+ res[1] = v.getY();
+ res[2] = v.getZ();
+ }
+
+ public static String toString(final ovrFovPort fov) {
+ return "["+fov.getLeftTan()+" l, "+fov.getRightTan()+" r, "+
+ fov.getUpTan()+" u, "+fov.getDownTan()+" d]";
+ }
+ public static String toString(final ovrSizei rect) {
+ return "["+rect.getW()+" x "+rect.getH()+"]";
+ }
+ public static String toString(final ovrRecti rect) {
+ return "["+rect.getPos().getX()+" / "+rect.getPos().getY()+" "+
+ rect.getSize().getW()+" x "+rect.getSize().getH()+"]";
+ }
+ public static String toString(final ovrVector2f v2) {
+ return "["+v2.getX()+", "+v2.getY()+"]";
+ }
+ public static String toString(final ovrVector3f v3) {
+ return "["+v3.getX()+", "+v3.getY()+", "+v3.getZ()+"]";
+ }
+
+ public static String toString(final ovrEyeRenderDesc desc) {
+ return "["+desc.getEye()+", fov"+toString(desc.getFov())+
+ ", viewport"+toString(desc.getDistortedViewport())+
+ ", pptCtr"+toString(desc.getPixelsPerTanAngleAtCenter())+
+ ", view-adjust"+toString(desc.getViewAdjust())+"]";
+ }
+
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.fp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.fp
new file mode 100644
index 000000000..6d450fe40
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.fp
@@ -0,0 +1,26 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define varying in
+ out vec4 ovr_FragColor;
+ #define texture2D texture
+#else
+ #define ovr_FragColor gl_FragColor
+#endif
+
+uniform sampler2D ovr_Texture0;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+varying vec2 ovv_TexCoordG;
+varying vec2 ovv_TexCoordB;
+
+void main (void)
+{
+ // 3 samples for fixing chromatic aberrations
+ vec3 color = vec3(texture2D(ovr_Texture0, ovv_TexCoordR).r,
+ texture2D(ovr_Texture0, ovv_TexCoordG).g,
+ texture2D(ovr_Texture0, ovv_TexCoordB).b);
+ ovr_FragColor = vec4(ovv_Fade * color, 1.0); // include vignetteFade
+}
+
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.vp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.vp
new file mode 100644
index 000000000..254d4b081
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_chroma.vp
@@ -0,0 +1,33 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define attribute in
+ #define varying out
+#endif
+
+uniform vec2 ovr_EyeToSourceUVScale;
+uniform vec2 ovr_EyeToSourceUVOffset;
+
+attribute vec2 ovr_Position;
+attribute vec2 ovr_Params;
+attribute vec2 ovr_TexCoordR;
+attribute vec2 ovr_TexCoordG;
+attribute vec2 ovr_TexCoordB;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+varying vec2 ovv_TexCoordG;
+varying vec2 ovv_TexCoordB;
+
+void main(void)
+{
+ gl_Position = vec4(ovr_Position.xy, 0.5, 1.0);
+ ovv_Fade = vec3(ovr_Params.r); // vignetteFade
+
+ ovv_TexCoordR = ovr_TexCoordR * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordR.y = 1.0-ovv_TexCoordR.y;
+ ovv_TexCoordG = ovr_TexCoordG * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordG.y = 1.0-ovv_TexCoordG.y;
+ ovv_TexCoordB = ovr_TexCoordB * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordB.y = 1.0-ovv_TexCoordB.y;
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.fp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.fp
new file mode 100644
index 000000000..d3ee5d04d
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.fp
@@ -0,0 +1,22 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define varying in
+ out vec4 ovr_FragColor;
+ #define texture2D texture
+#else
+ #define ovr_FragColor gl_FragColor
+#endif
+
+uniform sampler2D ovr_Texture0;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+
+void main (void)
+{
+ // 3 samples for fixing chromatic aberrations
+ vec3 color = texture2D(ovr_Texture0, ovv_TexCoordR).rgb;
+ ovr_FragColor = vec4(ovv_Fade * color, 1.0); // include vignetteFade
+}
+
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.vp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.vp
new file mode 100644
index 000000000..6456c7a83
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_plain.vp
@@ -0,0 +1,27 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define attribute in
+ #define varying out
+#endif
+
+uniform vec2 ovr_EyeToSourceUVScale;
+uniform vec2 ovr_EyeToSourceUVOffset;
+
+attribute vec2 ovr_Position;
+attribute vec2 ovr_Params;
+attribute vec2 ovr_TexCoordR;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+
+void main(void)
+{
+ gl_Position = vec4(ovr_Position.xy, 0.5, 1.0);
+ ovv_Fade = vec3(ovr_Params.r); // vignetteFade
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ ovv_TexCoordR = ovr_TexCoordR * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordR.y = 1.0-ovv_TexCoordR.y;
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp.vp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp.vp
new file mode 100644
index 000000000..3485625d2
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp.vp
@@ -0,0 +1,44 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define attribute in
+ #define varying out
+#endif
+
+uniform vec2 ovr_EyeToSourceUVScale;
+uniform vec2 ovr_EyeToSourceUVOffset;
+uniform mat4 ovr_EyeRotationStart;
+uniform mat4 ovr_EyeRotationEnd;
+
+attribute vec2 ovr_Position;
+attribute vec2 ovr_Params;
+attribute vec2 ovr_TexCoordR;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+
+void main(void)
+{
+ gl_Position = vec4(ovr_Position.xy, 0.0, 1.0);
+ ovv_Fade = vec3(ovr_Params.r); // vignetteFade
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ vec3 TanEyeAngle = vec3 ( ovr_TexCoordR, 1.0 );
+
+ // Accurate time warp lerp vs. faster
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ vec3 TransformedStart = (ovr_EyeRotationStart * vec4(TanEyeAngle, 0)).xyz;
+ vec3 TransformedEnd = (ovr_EyeRotationEnd * vec4(TanEyeAngle, 0)).xyz;
+ // And blend between them.
+ vec3 Transformed = mix ( TransformedStart, TransformedEnd, ovr_Params.g /* timewarpLerpFactor */ );
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ float RecipZ = 1.0 / Transformed.z;
+ vec2 Flattened = vec2 ( Transformed.x * RecipZ, Transformed.y * RecipZ );
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ ovv_TexCoordR = Flattened * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordR.y = 1.0-ovv_TexCoordR.y;
+}
diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp_chroma.vp b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp_chroma.vp
new file mode 100644
index 000000000..e2a45e430
--- /dev/null
+++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/shader/dist01_timewarp_chroma.vp
@@ -0,0 +1,65 @@
+//Copyright 2014 JogAmp Community. All rights reserved.
+
+#if __VERSION__ >= 130
+ #define attribute in
+ #define varying out
+#endif
+
+uniform vec2 ovr_EyeToSourceUVScale;
+uniform vec2 ovr_EyeToSourceUVOffset;
+uniform mat4 ovr_EyeRotationStart;
+uniform mat4 ovr_EyeRotationEnd;
+
+attribute vec2 ovr_Position;
+attribute vec2 ovr_Params;
+attribute vec2 ovr_TexCoordR;
+attribute vec2 ovr_TexCoordG;
+attribute vec2 ovr_TexCoordB;
+
+varying vec3 ovv_Fade;
+varying vec2 ovv_TexCoordR;
+varying vec2 ovv_TexCoordG;
+varying vec2 ovv_TexCoordB;
+
+void main(void)
+{
+ gl_Position = vec4(ovr_Position.xy, 0.0, 1.0);
+ ovv_Fade = vec3(ovr_Params.r); // vignetteFade
+
+ // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion).
+ // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD.
+ vec3 TanEyeAngleR = vec3 ( ovr_TexCoordR, 1.0 );
+ vec3 TanEyeAngleG = vec3 ( ovr_TexCoordG, 1.0 );
+ vec3 TanEyeAngleB = vec3 ( ovr_TexCoordB, 1.0 );
+
+ // Accurate time warp lerp vs. faster
+ // Apply the two 3x3 timewarp rotations to these vectors.
+ vec3 TransformedRStart = (ovr_EyeRotationStart * vec4(TanEyeAngleR, 0)).xyz;
+ vec3 TransformedGStart = (ovr_EyeRotationStart * vec4(TanEyeAngleG, 0)).xyz;
+ vec3 TransformedBStart = (ovr_EyeRotationStart * vec4(TanEyeAngleB, 0)).xyz;
+ vec3 TransformedREnd = (ovr_EyeRotationEnd * vec4(TanEyeAngleR, 0)).xyz;
+ vec3 TransformedGEnd = (ovr_EyeRotationEnd * vec4(TanEyeAngleG, 0)).xyz;
+ vec3 TransformedBEnd = (ovr_EyeRotationEnd * vec4(TanEyeAngleB, 0)).xyz;
+
+ // And blend between them.
+ vec3 TransformedR = mix ( TransformedRStart, TransformedREnd, ovr_Params.g /* timewarpLerpFactor */ );
+ vec3 TransformedG = mix ( TransformedGStart, TransformedGEnd, ovr_Params.g /* timewarpLerpFactor */ );
+ vec3 TransformedB = mix ( TransformedBStart, TransformedBEnd, ovr_Params.g /* timewarpLerpFactor */ );
+
+ // Project them back onto the Z=1 plane of the rendered images.
+ float RecipZR = 1.0 / TransformedR.z;
+ float RecipZG = 1.0 / TransformedG.z;
+ float RecipZB = 1.0 / TransformedB.z;
+ vec2 FlattenedR = vec2 ( TransformedR.x * RecipZR, TransformedR.y * RecipZR );
+ vec2 FlattenedG = vec2 ( TransformedG.x * RecipZG, TransformedG.y * RecipZG );
+ vec2 FlattenedB = vec2 ( TransformedB.x * RecipZB, TransformedB.y * RecipZB );
+
+ // These are now still in TanEyeAngle space.
+ // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye)
+ ovv_TexCoordR = FlattenedR * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordR.y = 1.0-ovv_TexCoordR.y;
+ ovv_TexCoordG = FlattenedG * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordG.y = 1.0-ovv_TexCoordG.y;
+ ovv_TexCoordB = FlattenedB * ovr_EyeToSourceUVScale + ovr_EyeToSourceUVOffset;
+ ovv_TexCoordB.y = 1.0-ovv_TexCoordB.y;
+}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
index b4661e43d..177b573dd 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java
@@ -45,7 +45,7 @@ import javax.media.opengl.GL;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
-import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLEventListener2;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLUniformData;
@@ -53,7 +53,7 @@ import javax.media.opengl.GLUniformData;
* GearsES2.java <BR>
* @author Brian Paul (converted to Java by Ron Cemer and Sven Gothel) <P>
*/
-public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererListener {
+public class GearsES2 implements GLEventListener2, TileRendererBase.TileRendererListener {
private final FloatBuffer lightPos = Buffers.newDirectFloatBuffer( new float[] { 5.0f, 5.0f, 10.0f } );
private ShaderState st = null;
@@ -89,7 +89,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private PinchToZoomGesture pinchToZoomGesture = null;
- public GearsES2(int swapInterval) {
+ public GearsES2(final int swapInterval) {
this.swapInterval = swapInterval;
}
@@ -98,49 +98,49 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
@Override
- public void addTileRendererNotify(TileRendererBase tr) {
+ public void addTileRendererNotify(final TileRendererBase tr) {
tileRendererInUse = tr;
doRotateBeforePrinting = doRotate;
setDoRotation(false);
}
@Override
- public void removeTileRendererNotify(TileRendererBase tr) {
+ public void removeTileRendererNotify(final TileRendererBase tr) {
tileRendererInUse = null;
setDoRotation(doRotateBeforePrinting);
}
@Override
- public void startTileRendering(TileRendererBase tr) {
+ public void startTileRendering(final TileRendererBase tr) {
System.err.println("GearsES2.startTileRendering: "+sid()+""+tr);
}
@Override
- public void endTileRendering(TileRendererBase tr) {
+ public void endTileRendering(final TileRendererBase tr) {
System.err.println("GearsES2.endTileRendering: "+sid()+""+tr);
}
- public void setIgnoreFocus(boolean v) { ignoreFocus = v; }
- public void setDoRotation(boolean rotate) { this.doRotate = rotate; }
- public void setClearBuffers(boolean v) { clearBuffers = v; }
- public void setVerbose(boolean v) { verbose = v; }
- public void setFlipVerticalInGLOrientation(boolean v) { flipVerticalInGLOrientation=v; }
+ public void setIgnoreFocus(final boolean v) { ignoreFocus = v; }
+ public void setDoRotation(final boolean rotate) { this.doRotate = rotate; }
+ public void setClearBuffers(final boolean v) { clearBuffers = v; }
+ public void setVerbose(final boolean v) { verbose = v; }
+ public void setFlipVerticalInGLOrientation(final boolean v) { flipVerticalInGLOrientation=v; }
/** float[4] */
- public void setClearColor(float[] clearColor) {
+ public void setClearColor(final float[] clearColor) {
this.clearColor = clearColor;
}
- public void setGearsColors(FloatBuffer gear1Color, FloatBuffer gear2Color, FloatBuffer gear3Color) {
+ public void setGearsColors(final FloatBuffer gear1Color, final FloatBuffer gear2Color, final FloatBuffer gear3Color) {
this.gear1Color = gear1Color;
this.gear2Color = gear2Color;
this.gear3Color = gear3Color;
}
- public void setSharedGearsObjects(GearsObjectES2 g1, GearsObjectES2 g2, GearsObjectES2 g3) {
+ public void setSharedGearsObjects(final GearsObjectES2 g1, final GearsObjectES2 g2, final GearsObjectES2 g3) {
gear1 = g1;
gear2 = g2;
gear3 = g3;
}
- public void setSharedGears(GearsES2 shared) {
+ public void setSharedGears(final GearsES2 shared) {
sharedGears = shared;
}
@@ -161,8 +161,12 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
public boolean usesSharedGears() { return usesSharedGears; }
- public void setUseMappedBuffers(boolean v) { useMappedBuffers = v; }
- public void setValidateBuffers(boolean v) { validateBuffers = v; }
+ public void setUseMappedBuffers(final boolean v) { useMappedBuffers = v; }
+ public void setValidateBuffers(final boolean v) { validateBuffers = v; }
+
+ public PMVMatrix getPMVMatrix() {
+ return pmvMatrix;
+ }
private static final int TIME_OUT = 2000; // 2s
private static final int POLL_DIVIDER = 20; // TO/20
@@ -171,7 +175,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
/**
* @return True if this GLEventListener became initialized within TIME_OUT 2s
*/
- public boolean waitForInit(boolean initialized) throws InterruptedException {
+ public boolean waitForInit(final boolean initialized) throws InterruptedException {
int wait;
for (wait=0; wait<POLL_DIVIDER && initialized != isInit ; wait++) {
Thread.sleep(TIME_SLICE);
@@ -182,7 +186,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private final String sid() { return "0x"+Integer.toHexString(hashCode()); }
@Override
- public void init(GLAutoDrawable drawable) {
+ public void init(final GLAutoDrawable drawable) {
if(null != sharedGears && !sharedGears.isInit() ) {
System.err.println(Thread.currentThread()+" GearsES2.init.0 "+sid()+": pending shared Gears .. re-init later XXXXX");
drawable.setGLEventListenerInitState(this, false);
@@ -202,8 +206,6 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
return;
}
- gl.glEnable(GL.GL_DEPTH_TEST);
-
st = new ShaderState();
// st.setVerbose(true);
final ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), "shader",
@@ -225,7 +227,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
st.ownUniform(pmvMatrixUniform);
st.uniform(gl, pmvMatrixUniform);
- GLUniformData lightU = new GLUniformData("lightPos", 3, lightPos);
+ final GLUniformData lightU = new GLUniformData("lightPos", 3, lightPos);
st.ownUniform(lightU);
st.uniform(gl, lightU);
@@ -315,7 +317,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private final GestureHandler.GestureListener pinchToZoomListener = new GestureHandler.GestureListener() {
@Override
- public void gestureDetected(GestureEvent gh) {
+ public void gestureDetected(final GestureEvent gh) {
final PinchToZoomGesture.ZoomEvent ze = (PinchToZoomGesture.ZoomEvent) gh;
final float zoom = ze.getZoom(); // * ( ze.getTrigger().getPointerCount() - 1 ); <- too much ..
panZ = zoom * 30f - 30f; // [0 .. 2] -> [-30f .. 30f]
@@ -323,7 +325,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
};
@Override
- public void reshape(GLAutoDrawable glad, int x, int y, int width, int height) {
+ public void reshape(final GLAutoDrawable glad, final int x, final int y, final int width, final int height) {
if( !isInit ) { return; }
final GL2ES2 gl = glad.getGL().getGL2ES2();
if(-1 != swapInterval) {
@@ -333,16 +335,16 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
@Override
- public void reshapeTile(TileRendererBase tr,
- int tileX, int tileY, int tileWidth, int tileHeight,
- int imageWidth, int imageHeight) {
+ public void reshapeTile(final TileRendererBase tr,
+ final int tileX, final int tileY, final int tileWidth, final int tileHeight,
+ final int imageWidth, final int imageHeight) {
if( !isInit ) { return; }
final GL2ES2 gl = tr.getAttachedDrawable().getGL().getGL2ES2();
gl.setSwapInterval(0);
reshapeImpl(gl, tileX, tileY, tileWidth, tileHeight, imageWidth, imageHeight);
}
- void reshapeImpl(GL2ES2 gl, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight) {
+ void reshapeImpl(final GL2ES2 gl, final int tileX, final int tileY, final int tileWidth, final int tileHeight, final int imageWidth, final int imageHeight) {
final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers();
if(verbose) {
System.err.println(Thread.currentThread()+" GearsES2.reshape "+sid()+" "+tileX+"/"+tileY+" "+tileWidth+"x"+tileHeight+" of "+imageWidth+"x"+imageHeight+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(gl.getContext().getGLDrawable().getHandle())+", msaa "+msaa+", tileRendererInUse "+tileRendererInUse);
@@ -352,18 +354,16 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
return;
}
- st.useProgram(gl, true);
-
// compute projection parameters 'normal'
float left, right, bottom, top;
if( imageHeight > imageWidth ) {
- float a = (float)imageHeight / (float)imageWidth;
+ final float a = (float)imageHeight / (float)imageWidth;
left = -1.0f;
right = 1.0f;
bottom = -a;
top = a;
} else {
- float a = (float)imageWidth / (float)imageHeight;
+ final float a = (float)imageWidth / (float)imageHeight;
left = -a;
right = a;
bottom = -1.0f;
@@ -394,15 +394,32 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW);
pmvMatrix.glLoadIdentity();
pmvMatrix.glTranslatef(0.0f, 0.0f, -40.0f);
+ st.useProgram(gl, true);
st.uniform(gl, pmvMatrixUniform);
st.useProgram(gl, false);
-
- // System.err.println(Thread.currentThread()+" GearsES2.reshape FIN");
}
// private boolean useAndroidDebug = false;
@Override
- public void dispose(GLAutoDrawable drawable) {
+ public void setProjectionModelview(final GLAutoDrawable drawable, final float[] mat4Projection, final float[] mat4Modelview) {
+ final GL2ES2 gl = drawable.getGL().getGL2ES2();
+ pmvMatrix.glMatrixMode(PMVMatrix.GL_PROJECTION);
+ if( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() ) {
+ pmvMatrix.glLoadIdentity();
+ pmvMatrix.glScalef(1f, -1f, 1f);
+ pmvMatrix.glMultMatrixf(mat4Projection, 0);
+ } else {
+ pmvMatrix.glLoadMatrixf(mat4Projection, 0);
+ }
+ pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW);
+ pmvMatrix.glLoadMatrixf(mat4Modelview, 0);
+ st.useProgram(gl, true);
+ st.uniform(gl, pmvMatrixUniform);
+ st.useProgram(gl, false);
+ }
+
+ @Override
+ public void dispose(final GLAutoDrawable drawable) {
if( !isInit ) { return; }
isInit = false;
if(verbose) {
@@ -439,16 +456,25 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
@Override
- public void display(GLAutoDrawable drawable) {
+ public void display(final GLAutoDrawable drawable) {
+ display(drawable, 0);
+ }
+
+ @Override
+ public void display(final GLAutoDrawable drawable, final int flags) {
if( !isInit ) { return; }
if(null != sharedGears && !sharedGears.isInit() ) { return; }
- GLAnimatorControl anim = drawable.getAnimator();
+ final GLAnimatorControl anim = drawable.getAnimator();
if( verbose && ( null == anim || !anim.isAnimating() ) ) {
System.err.println(Thread.currentThread()+" GearsES2.display "+sid()+" "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(drawable.getHandle()));
}
+
+ final boolean repeatedFrame = 0 != ( GLEventListener2.DISPLAY_REPEAT & flags );
+ final boolean dontClear = 0 != ( GLEventListener2.DISPLAY_DONTCLEAR & flags );
+
// Turn the gears' teeth
- if(doRotate) {
- angle += 2.0f;
+ if( doRotate && !repeatedFrame ) {
+ angle += 0.5f;
}
// Get the GL corresponding to the drawable we are animating
@@ -462,7 +488,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
hasFocus = true;
}
- if( clearBuffers ) {
+ if( clearBuffers && !dontClear ) {
if( null != clearColor ) {
gl.glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
} else if( null != tileRendererInUse ) {
@@ -487,11 +513,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
return;
}
- // Only possible if we do not flip the projection matrix
- final boolean enableCullFace = ! ( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() );
- if( enableCullFace ) {
- gl.glEnable(GL.GL_CULL_FACE);
- }
+ setGLStates(gl, true);
st.useProgram(gl, true);
pmvMatrix.glPushMatrix();
@@ -506,8 +528,22 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
pmvMatrix.glPopMatrix();
st.useProgram(gl, false);
- if( enableCullFace ) {
- gl.glDisable(GL.GL_CULL_FACE);
+ setGLStates(gl, false);
+ }
+
+ public void setGLStates(final GL2ES2 gl, final boolean enable) {
+ // Culling only possible if we do not flip the projection matrix
+ final boolean enableCullFace = ! ( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() );
+ if( enable ) {
+ gl.glEnable(GL.GL_DEPTH_TEST);
+ if( enableCullFace ) {
+ gl.glEnable(GL.GL_CULL_FACE);
+ }
+ } else {
+ gl.glDisable(GL.GL_DEPTH_TEST);
+ if( enableCullFace ) {
+ gl.glDisable(GL.GL_CULL_FACE);
+ }
}
}
@@ -518,13 +554,13 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
boolean confinedFixedCenter = false;
- public void setConfinedFixedCenter(boolean v) {
+ public void setConfinedFixedCenter(final boolean v) {
confinedFixedCenter = v;
}
class GearsKeyAdapter extends KeyAdapter {
- public void keyPressed(KeyEvent e) {
- int kc = e.getKeyCode();
+ public void keyPressed(final KeyEvent e) {
+ final int kc = e.getKeyCode();
if(KeyEvent.VK_LEFT == kc) {
view_roty -= 1;
} else if(KeyEvent.VK_RIGHT == kc) {
@@ -541,20 +577,20 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
private int prevMouseX, prevMouseY;
@Override
- public void mouseClicked(MouseEvent e) {
+ public void mouseClicked(final MouseEvent e) {
}
@Override
- public void mouseEntered(MouseEvent e) {
+ public void mouseEntered(final MouseEvent e) {
}
@Override
- public void mouseExited(MouseEvent e) {
+ public void mouseExited(final MouseEvent e) {
}
@Override
- public void mouseWheelMoved(MouseEvent e) {
- float[] rot = e.getRotation();
+ public void mouseWheelMoved(final MouseEvent e) {
+ final float[] rot = e.getRotation();
if( e.isControlDown() ) {
// alternative zoom
final float incr = e.isShiftDown() ? rot[0] : rot[1] * 0.5f ;
@@ -567,7 +603,7 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
}
- public void mousePressed(MouseEvent e) {
+ public void mousePressed(final MouseEvent e) {
if( e.getPointerCount()==1 ) {
prevMouseX = e.getX();
prevMouseY = e.getY();
@@ -579,10 +615,10 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
}
- public void mouseReleased(MouseEvent e) {
+ public void mouseReleased(final MouseEvent e) {
}
- public void mouseMoved(MouseEvent e) {
+ public void mouseMoved(final MouseEvent e) {
if( e.isConfined() ) {
navigate(e);
} else {
@@ -593,27 +629,27 @@ public class GearsES2 implements GLEventListener, TileRendererBase.TileRendererL
}
}
- public void mouseDragged(MouseEvent e) {
+ public void mouseDragged(final MouseEvent e) {
navigate(e);
}
- private void navigate(MouseEvent e) {
+ private void navigate(final MouseEvent e) {
int x = e.getX();
int y = e.getY();
int width, height;
- Object source = e.getSource();
+ final Object source = e.getSource();
Window window = null;
if(source instanceof Window) {
window = (Window) source;
width=window.getSurfaceWidth();
height=window.getSurfaceHeight();
} else if (source instanceof GLAutoDrawable) {
- GLAutoDrawable glad = (GLAutoDrawable) source;
+ final GLAutoDrawable glad = (GLAutoDrawable) source;
width = glad.getSurfaceWidth();
height = glad.getSurfaceHeight();
} else if (GLProfile.isAWTAvailable() && source instanceof java.awt.Component) {
- java.awt.Component comp = (java.awt.Component) source;
+ final java.awt.Component comp = (java.awt.Component) source;
width=comp.getWidth(); // FIXME HiDPI: May need to convert window units -> pixel units!
height=comp.getHeight();
} else {
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java
new file mode 100644
index 000000000..c20af1389
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/ovr/OVRDemo01.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.test.junit.jogl.stereo.ovr;
+
+import javax.media.opengl.GLCapabilities;
+import javax.media.opengl.GLEventListener;
+import javax.media.opengl.GLProfile;
+
+import jogamp.opengl.oculusvr.OVRDistortion;
+
+import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.oculusvr.OVR;
+import com.jogamp.oculusvr.OVRException;
+import com.jogamp.oculusvr.OVRVersion;
+import com.jogamp.oculusvr.OvrHmdContext;
+import com.jogamp.oculusvr.ovrFovPort;
+import com.jogamp.oculusvr.ovrHmdDesc;
+import com.jogamp.oculusvr.ovrSizei;
+import com.jogamp.oculusvr.ovrVector2i;
+import com.jogamp.opengl.oculusvr.OVRSBSRendererDualFBO;
+import com.jogamp.opengl.oculusvr.OVRSBSRendererSingleFBO;
+import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.test.junit.util.QuitAdapter;
+import com.jogamp.opengl.util.Animator;
+
+/**
+ * All distortions, multisampling and using two FBOs
+ * <pre>
+ * java OVRDemo01 -time 10000000 -vignette -chromatic -timewarp -samples 8
+ * </pre>
+ * All distortions, multisampling and using a big single FBO
+ * <pre>
+ * java OVRDemo01 -time 10000000 -vignette -chromatic -timewarp -samples 8 -singleFBO
+ * </pre>
+ * Test on main screen:
+ * <pre>
+ * java OVRDemo01 -time 10000000 -mainScreen
+ * </pre>
+ *
+ */
+public class OVRDemo01 {
+ static long duration = 10000; // ms
+
+ static boolean useOVRScreen = true;
+
+ static int numSamples = 0;
+ static boolean useSingleFBO = false;
+ static boolean useVignette = false;
+ static boolean useChromatic = false;
+ static boolean useTimewarp = false;
+ static boolean useAutoSwap = false;
+
+ public static void main(final String args[]) throws InterruptedException {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ i++;
+ duration = MiscUtils.atol(args[i], duration);
+ } else if(args[i].equals("-samples")) {
+ i++;
+ numSamples = MiscUtils.atoi(args[i], numSamples);
+ } else if(args[i].equals("-singleFBO")) {
+ useSingleFBO = true;
+ } else if(args[i].equals("-vignette")) {
+ useVignette = true;
+ } else if(args[i].equals("-chromatic")) {
+ useChromatic = true;
+ } else if(args[i].equals("-timewarp")) {
+ useTimewarp = true;
+ } else if(args[i].equals("-vignette")) {
+ useVignette = true;
+ } else if(args[i].equals("-mainScreen")) {
+ useOVRScreen = false;
+ } else if(args[i].equals("-autoSwap")) {
+ useAutoSwap = true;
+ }
+ }
+ final OVRDemo01 demo01 = new OVRDemo01();
+ demo01.doIt(0, numSamples, useSingleFBO, useVignette, useChromatic, useTimewarp,
+ useAutoSwap, true /* useAnimator */, false /* exclusiveContext*/);
+ }
+
+ public void doIt(final int ovrHmdIndex, final int numSamples,
+ final boolean useSingleFBO,
+ final boolean useVignette, final boolean useChromatic, final boolean useTimewarp,
+ final boolean useAutoSwap,
+ final boolean useAnimator, final boolean exclusiveContext) throws InterruptedException {
+
+ System.err.println("glob duration "+duration);
+ System.err.println("glob useOVRScreen "+useOVRScreen);
+ System.err.println("numSamples "+numSamples);
+ System.err.println("useSingleFBO "+useSingleFBO);
+ System.err.println("useVignette "+useVignette);
+ System.err.println("useChromatic "+useChromatic);
+ System.err.println("useTimewarp "+useTimewarp);
+ System.err.println("useAutoSwap "+useAutoSwap);
+
+ // Initialize LibOVR...
+ if( !OVR.ovr_Initialize() ) { // recursive ..
+ throw new OVRException("OVR not available");
+ }
+ final OvrHmdContext hmdCtx = OVR.ovrHmd_Create(ovrHmdIndex);
+ if( null == hmdCtx ) {
+ throw new OVRException("OVR HMD #"+ovrHmdIndex+" not available");
+ }
+ final ovrHmdDesc hmdDesc = ovrHmdDesc.create();
+ OVR.ovrHmd_GetDesc(hmdCtx, hmdDesc);
+ System.err.println(OVRVersion.getAvailableCapabilitiesInfo(hmdDesc, ovrHmdIndex, null).toString());
+
+ // Start the sensor which provides the Rift’s pose and motion.
+ final int requiredSensorCaps = OVR.ovrSensorCap_Orientation;
+ final int supportedSensorCaps = requiredSensorCaps | OVR.ovrSensorCap_YawCorrection | OVR.ovrSensorCap_Position;
+ OVR.ovrHmd_StartSensor(hmdCtx, supportedSensorCaps, requiredSensorCaps);
+
+ //
+ //
+ //
+
+ final GLCapabilities caps = new GLCapabilities(GLProfile.getMaxProgrammable(true /* favorHardwareRasterizer */));
+ final GLWindow window = GLWindow.create(caps);
+ final ovrVector2i ovrPos = hmdDesc.getWindowsPos();
+ final ovrSizei ovrRes = hmdDesc.getResolution();
+ window.setSize(ovrRes.getW(), ovrRes.getH());
+ if( useOVRScreen ) {
+ window.setPosition(ovrPos.getX(), ovrPos.getY());
+ }
+ window.setAutoSwapBufferMode(useAutoSwap);
+ window.setUndecorated(true);
+
+ final Animator animator = useAnimator ? new Animator() : null;
+ if( useAnimator ) {
+ animator.setModeBits(false, Animator.MODE_EXPECT_AWT_RENDERING_THREAD);
+ animator.setExclusiveContext(exclusiveContext);
+ }
+
+ //
+ // Oculus Rift setup
+ //
+ final ovrFovPort[] defaultEyeFov = hmdDesc.getDefaultEyeFov(0, new ovrFovPort[2]);
+ final int distortionCaps = ( useVignette ? OVR.ovrDistortionCap_Vignette : 0 ) |
+ ( useChromatic ? OVR.ovrDistortionCap_Chromatic : 0 ) |
+ ( useTimewarp ? OVR.ovrDistortionCap_TimeWarp : 0 );
+ final float pixelsPerDisplayPixel = 1f;
+ final OVRDistortion dist = OVRDistortion.create(hmdCtx, useSingleFBO, defaultEyeFov, pixelsPerDisplayPixel, distortionCaps);
+ System.err.println("OVRDistortion: "+dist);
+
+ final GearsES2 upstream = new GearsES2(0);
+ upstream.setVerbose(false);
+ final GLEventListener renderer;
+ if( useSingleFBO ) {
+ renderer = new OVRSBSRendererSingleFBO(dist, true /* ownsDist */, upstream, numSamples);
+ } else {
+ renderer = new OVRSBSRendererDualFBO(dist, true /* ownsDist */, upstream, numSamples);
+ }
+ window.addGLEventListener(renderer);
+
+ final QuitAdapter quitAdapter = new QuitAdapter();
+ window.addKeyListener(quitAdapter);
+ window.addWindowListener(quitAdapter);
+
+ if( useAnimator ) {
+ animator.add(window);
+ animator.start();
+ }
+ window.setVisible(true);
+ if( useAnimator ) {
+ animator.setUpdateFPSFrames(60, System.err);
+ }
+
+ final long t0 = System.currentTimeMillis();
+ long t1 = t0;
+ while(!quitAdapter.shouldQuit() && t1-t0<duration) {
+ Thread.sleep(100);
+ t1 = System.currentTimeMillis();
+ }
+
+ if( useAnimator ) {
+ animator.stop();
+ }
+ window.destroy();
+ // OVR.ovr_Shutdown();
+ }
+}