jogamp.opengl.util.stereo.GenericStereoDeviceRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jogl-all-android Show documentation
Show all versions of jogl-all-android Show documentation
Java™ Binding for the OpenGL® API (Android)
The newest version!
/**
* 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.util.stereo;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.Arrays;
import com.jogamp.nativewindow.util.Dimension;
import com.jogamp.nativewindow.util.DimensionImmutable;
import com.jogamp.nativewindow.util.RectangleImmutable;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLArrayData;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.GLUniformData;
import jogamp.common.os.PlatformPropsImpl;
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.os.Platform;
import com.jogamp.opengl.JoglVersion;
import com.jogamp.opengl.util.GLArrayDataServer;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderProgram;
import com.jogamp.opengl.util.stereo.EyeParameter;
import com.jogamp.opengl.util.stereo.ViewerPose;
import com.jogamp.opengl.util.stereo.StereoDevice;
import com.jogamp.opengl.util.stereo.StereoDeviceRenderer;
import com.jogamp.opengl.util.stereo.StereoUtil;
/**
* Generic Stereo Device Distortion and OpenGL Renderer Utility
*/
public class GenericStereoDeviceRenderer implements StereoDeviceRenderer {
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 class GenericEye implements StereoDeviceRenderer.Eye {
private final int eyeName;
private final int distortionBits;
private final int vertexCount;
private final int indexCount;
private final RectangleImmutable viewport;
private final GLUniformData eyeToSourceUVScale;
private final GLUniformData eyeToSourceUVOffset;
private final GLUniformData eyeRotationStart;
private final GLUniformData eyeRotationEnd;
/** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
private final GLArrayDataServer iVBO;
private final GLArrayData vboPos, vboParams, vboTexCoordsR, vboTexCoordsG, vboTexCoordsB;
private final GLArrayDataServer indices;
private final EyeParameter eyeParameter;
@Override
public final RectangleImmutable getViewport() { return viewport; }
@Override
public final EyeParameter getEyeParameter() { return eyeParameter; }
/* pp */ GenericEye(final GenericStereoDevice device, final int distortionBits,
final float[] eyePositionOffset, final EyeParameter eyeParam,
final DimensionImmutable textureSize, final RectangleImmutable eyeViewport) {
this.eyeName = eyeParam.number;
this.distortionBits = distortionBits;
this.viewport = eyeViewport;
final boolean usePP = null != device.config.distortionMeshProducer && 0 != distortionBits;
final boolean usesTimewarp = usePP && StereoUtil.usesTimewarpDistortion(distortionBits);
final FloatBuffer fstash = Buffers.newDirectFloatBuffer( 2 + 2 + ( usesTimewarp ? 16 + 16 : 0 ) ) ;
if( usePP ) {
eyeToSourceUVScale = new GLUniformData("svr_EyeToSourceUVScale", 2, Buffers.slice2Float(fstash, 0, 2));
eyeToSourceUVOffset = new GLUniformData("svr_EyeToSourceUVOffset", 2, Buffers.slice2Float(fstash, 2, 2));
} else {
eyeToSourceUVScale = null;
eyeToSourceUVOffset = null;
}
if( usesTimewarp ) {
eyeRotationStart = new GLUniformData("svr_EyeRotationStart", 4, 4, Buffers.slice2Float(fstash, 4, 16));
eyeRotationEnd = new GLUniformData("svr_EyeRotationEnd", 4, 4, Buffers.slice2Float(fstash, 20, 16));
} else {
eyeRotationStart = null;
eyeRotationEnd = null;
}
this.eyeParameter = eyeParam;
// Setup: eyeToSourceUVScale, eyeToSourceUVOffset
if( usePP ) {
final ScaleAndOffset2D textureScaleAndOffset = new ScaleAndOffset2D(eyeParam.fovhv, textureSize, eyeViewport);
if( StereoDevice.DEBUG ) {
System.err.println("XXX."+eyeName+": eyeParam "+eyeParam);
System.err.println("XXX."+eyeName+": uvScaleOffset "+textureScaleAndOffset);
System.err.println("XXX."+eyeName+": textureSize "+textureSize);
System.err.println("XXX."+eyeName+": viewport "+eyeViewport);
}
final FloatBuffer eyeToSourceUVScaleFB = eyeToSourceUVScale.floatBufferValue();
eyeToSourceUVScaleFB.put(0, textureScaleAndOffset.scale[0]);
eyeToSourceUVScaleFB.put(1, textureScaleAndOffset.scale[1]);
final FloatBuffer eyeToSourceUVOffsetFB = eyeToSourceUVOffset.floatBufferValue();
eyeToSourceUVOffsetFB.put(0, textureScaleAndOffset.offset[0]);
eyeToSourceUVOffsetFB.put(1, textureScaleAndOffset.offset[1]);
} else {
vertexCount = 0;
indexCount = 0;
iVBO = null;
vboPos = null;
vboParams = null;
vboTexCoordsR = null;
vboTexCoordsG = null;
vboTexCoordsB = null;
indices = null;
if( StereoDevice.DEBUG ) {
System.err.println("XXX."+eyeName+": "+this);
}
return;
}
final DistortionMesh meshData = device.config.distortionMeshProducer.create(eyeParam, distortionBits);
if( null == meshData ) {
throw new GLException("Failed to create meshData for eye "+eyeParam+", and "+StereoUtil.distortionBitsToString(distortionBits));
}
vertexCount = meshData.vertexCount;
indexCount = meshData.indexCount;
/** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
final boolean useChromatic = StereoUtil.usesChromaticDistortion(distortionBits);
final boolean useVignette = StereoUtil.usesVignetteDistortion(distortionBits);
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("svr_Position", 2, GL.GL_ARRAY_BUFFER);
vboParams = iVBO.addGLSLSubArray("svr_Params", 2, GL.GL_ARRAY_BUFFER);
vboTexCoordsR = iVBO.addGLSLSubArray("svr_TexCoordR", 2, GL.GL_ARRAY_BUFFER);
if( useChromatic ) {
vboTexCoordsG = iVBO.addGLSLSubArray("svr_TexCoordG", 2, GL.GL_ARRAY_BUFFER);
vboTexCoordsB = iVBO.addGLSLSubArray("svr_TexCoordB", 2, GL.GL_ARRAY_BUFFER);
} else {
vboTexCoordsG = null;
vboTexCoordsB = null;
}
indices = GLArrayDataServer.createData(1, GL.GL_SHORT, indexCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER);
/** 2+2+2+2+2: { vec2 position, vec2 color, vec2 texCoordR, vec2 texCoordG, vec2 texCoordB } */
final FloatBuffer iVBOFB = (FloatBuffer)iVBO.getBuffer();
for ( int vertNum = 0; vertNum < vertexCount; vertNum++ ) {
final DistortionMesh.DistortionVertex v = meshData.vertices[vertNum];
int dataIdx = 0;
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": START VERTEX "+vertNum+" / "+vertexCount);
}
// pos
if( v.pos_size >= 2 ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": pos ["+v.data[dataIdx]+", "+v.data[dataIdx+1]+"]");
}
iVBOFB.put(v.data[dataIdx]);
iVBOFB.put(v.data[dataIdx+1]);
} else {
iVBOFB.put(0f);
iVBOFB.put(0f);
}
dataIdx += v.pos_size;
// params
if( v.vignetteFactor_size >= 1 && useVignette ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": vignette "+v.data[dataIdx]);
}
iVBOFB.put(v.data[dataIdx]);
} else {
iVBOFB.put(1.0f);
}
dataIdx += v.vignetteFactor_size;
if( v.timewarpFactor_size >= 1 ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": timewarp "+v.data[dataIdx]);
}
iVBOFB.put(v.data[dataIdx]);
} else {
iVBOFB.put(1.0f);
}
dataIdx += v.timewarpFactor_size;
// texCoordR
if( v.texR_size >= 2 ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": texR ["+v.data[dataIdx]+", "+v.data[dataIdx+1]+"]");
}
iVBOFB.put(v.data[dataIdx]);
iVBOFB.put(v.data[dataIdx+1]);
} else {
iVBOFB.put(1f);
iVBOFB.put(1f);
}
dataIdx += v.texR_size;
if( useChromatic ) {
// texCoordG
if( v.texG_size >= 2 ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": texG ["+v.data[dataIdx]+", "+v.data[dataIdx+1]+"]");
}
iVBOFB.put(v.data[dataIdx]);
iVBOFB.put(v.data[dataIdx+1]);
} else {
iVBOFB.put(1f);
iVBOFB.put(1f);
}
dataIdx += v.texG_size;
// texCoordB
if( v.texB_size >= 2 ) {
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": texB ["+v.data[dataIdx]+", "+v.data[dataIdx+1]+"]");
}
iVBOFB.put(v.data[dataIdx]);
iVBOFB.put(v.data[dataIdx+1]);
} else {
iVBOFB.put(1f);
iVBOFB.put(1f);
}
dataIdx += v.texB_size;
} else {
dataIdx += v.texG_size;
dataIdx += v.texB_size;
}
}
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": iVBO "+iVBO);
}
{
if( StereoDevice.DUMP_DATA ) {
System.err.println("XXX."+eyeName+": idx "+indices+", count "+indexCount);
for(int i=0; i< indexCount; i++) {
if( 0 == i % 16 ) {
System.err.printf("%n%5d: ", i);
}
System.err.printf("%5d, ", (int)meshData.indices[i]);
}
System.err.println();
}
final ShortBuffer out = (ShortBuffer) indices.getBuffer();
out.put(meshData.indices, 0, meshData.indexCount);
}
if( StereoDevice.DEBUG ) {
System.err.println("XXX."+eyeName+": "+this);
}
}
private void linkData(final GL2ES2 gl, final ShaderProgram sp) {
if( null == iVBO ) return;
if( 0 > vboPos.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+vboPos);
}
if( 0 > vboParams.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+vboParams);
}
if( 0 > vboTexCoordsR.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+vboTexCoordsR);
}
if( StereoUtil.usesChromaticDistortion(distortionBits) ) {
if( 0 > vboTexCoordsG.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+vboTexCoordsG);
}
if( 0 > vboTexCoordsB.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+vboTexCoordsB);
}
}
if( 0 > eyeToSourceUVScale.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+eyeToSourceUVScale);
}
if( 0 > eyeToSourceUVOffset.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+eyeToSourceUVOffset);
}
if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
if( 0 > eyeRotationStart.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+eyeRotationStart);
}
if( 0 > eyeRotationEnd.setLocation(gl, sp.program()) ) {
throw new GLException("Couldn't locate "+eyeRotationEnd);
}
}
iVBO.seal(gl, true);
iVBO.enableBuffer(gl, false);
indices.seal(gl, true);
indices.enableBuffer(gl, false);
}
/* pp */ void dispose(final GL2ES2 gl) {
if( null == iVBO ) return;
iVBO.destroy(gl);
indices.destroy(gl);
}
/* pp */ void enableVBO(final GL2ES2 gl, final boolean enable) {
if( null == iVBO ) return;
iVBO.enableBuffer(gl, enable);
indices.bindBuffer(gl, enable); // keeps VBO binding if enable:=true
}
/* pp */ void updateUniform(final GL2ES2 gl, final ShaderProgram sp) {
if( null == iVBO ) return;
gl.glUniform(eyeToSourceUVScale);
gl.glUniform(eyeToSourceUVOffset);
if( StereoUtil.usesTimewarpDistortion(distortionBits) ) {
gl.glUniform(eyeRotationStart);
gl.glUniform(eyeRotationEnd);
}
}
@Override
public String toString() {
final String ppTxt = null == iVBO ? ", no post-processing" :
", uvScale["+eyeToSourceUVScale.floatBufferValue().get(0)+", "+eyeToSourceUVScale.floatBufferValue().get(1)+
"], uvOffset["+eyeToSourceUVOffset.floatBufferValue().get(0)+", "+eyeToSourceUVOffset.floatBufferValue().get(1)+"]";
return "Eye["+eyeName+", viewport "+viewport+
", "+eyeParameter+
", vertices "+vertexCount+", indices "+indexCount+
ppTxt+
", desc "+eyeParameter+"]";
}
}
private final GenericStereoDevice device;
private final GenericEye[] eyes;
private final ViewerPose viewerPose;
private final int distortionBits;
private final int textureCount;
private final DimensionImmutable[] eyeTextureSizes;
private final DimensionImmutable totalTextureSize;
/** if texUnit0 is null: no post-processing */
private final GLUniformData texUnit0;
private ShaderProgram sp;
private long frameStart = 0;
@Override
public String toString() {
return "GenericStereo[distortion["+StereoUtil.distortionBitsToString(distortionBits)+
"], eyeTexSize "+Arrays.toString(eyeTextureSizes)+
", sbsSize "+totalTextureSize+
", texCount "+textureCount+", texUnit "+(null != texUnit0 ? texUnit0.intValue() : "n/a")+
", "+PlatformPropsImpl.NEWLINE+" "+(0 < eyes.length ? eyes[0] : "none")+
", "+PlatformPropsImpl.NEWLINE+" "+(1 < eyes.length ? eyes[1] : "none")+"]";
}
private static final DimensionImmutable zeroSize = new Dimension(0, 0);
/* pp */ GenericStereoDeviceRenderer(final GenericStereoDevice context, final int distortionBits,
final int textureCount, final float[] eyePositionOffset,
final EyeParameter[] eyeParam, final float pixelsPerDisplayPixel, final int textureUnit,
final DimensionImmutable[] eyeTextureSizes, final DimensionImmutable totalTextureSize,
final RectangleImmutable[] eyeViewports) {
if( eyeParam.length != eyeTextureSizes.length ||
eyeParam.length != eyeViewports.length ) {
throw new IllegalArgumentException("eye arrays of different length");
}
this.device = context;
this.eyes = new GenericEye[eyeParam.length];
this.distortionBits = ( distortionBits | context.getMinimumDistortionBits() ) & context.getSupportedDistortionBits();
final boolean usePP = null != device.config.distortionMeshProducer && 0 != this.distortionBits;
final DimensionImmutable[] textureSizes;
if( usePP ) {
if( 1 > textureCount || 2 < textureCount ) {
this.textureCount = 2;
} else {
this.textureCount = textureCount;
}
this.eyeTextureSizes = eyeTextureSizes;
this.totalTextureSize = totalTextureSize;
if( 1 == textureCount ) {
textureSizes = new DimensionImmutable[eyeParam.length];
for(int i=0; i texUnit0.setLocation(gl2es2, sp.program()) ) {
throw new GLException("Couldn't locate "+texUnit0);
}
for(int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy