All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jogamp.opengl.util.stereo.GenericStereoDeviceRenderer Maven / Gradle / Ivy

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 - 2024 Weber Informatics LLC | Privacy Policy