package com.asha.vrlib.plugins; import android.content.Context; import android.graphics.PointF; import android.opengl.GLES20; import com.asha.vrlib.MD360Director; import com.asha.vrlib.MD360DirectorFactory; import com.asha.vrlib.MD360Program; import com.asha.vrlib.MDVRLibrary; import com.asha.vrlib.common.VRUtil; import com.asha.vrlib.model.BarrelDistortionConfig; import com.asha.vrlib.model.MDPosition; import com.asha.vrlib.objects.MDAbsObject3D; import com.asha.vrlib.objects.MDObject3DHelper; import com.asha.vrlib.strategy.display.DisplayModeManager; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import static com.asha.vrlib.common.GLUtil.glCheck; /** * Created by hzqiujiadi on 16/7/27. * hzqiujiadi ashqalcn@gmail.com * * Barrel Distortion * * For more info, * http://stackoverflow.com/questions/12620025/barrel-distortion-correction-algorithm-to-correct-fisheye-lens-failing-to-impl */ public class MDBarrelDistortionLinePipe extends MDAbsLinePipe { private MD360Program mProgram; private MDBarrelDistortionMesh object3D; private MD360Director mDirector; private boolean mEnabled; private MDDrawingCache mDrawingCache; private BarrelDistortionConfig mConfiguration; private DisplayModeManager mDisplayModeManager; public MDBarrelDistortionLinePipe(DisplayModeManager displayModeManager) { mDisplayModeManager = displayModeManager; mConfiguration = displayModeManager.getBarrelDistortionConfig(); mProgram = new MD360Program(MDVRLibrary.ContentType.FBO); mDirector = new MD360DirectorFactory.OrthogonalImpl().createDirector(0); object3D = new MDBarrelDistortionMesh(); mDrawingCache = new MDDrawingCache(); } @Override public void init(final Context context) { mProgram.build(context); MDObject3DHelper.loadObj(context,object3D); } @Override public void takeOver(int totalWidth, int totalHeight, int size) { mEnabled = mDisplayModeManager.isAntiDistortionEnabled(); if (!mEnabled){ return; } mDrawingCache.bind(totalWidth,totalHeight); mDirector.setViewport(totalWidth, totalHeight); object3D.setMode(size); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); glCheck("MDBarrelDistortionLinePipe glClear"); } @Override public void commit(int totalWidth, int totalHeight, int size){ if (!mEnabled){ return; } mDrawingCache.unbind(); int width = totalWidth / size; for (int i = 0; i < size; i++){ GLES20.glViewport(width * i, 0, width, totalHeight); GLES20.glEnable(GLES20.GL_SCISSOR_TEST); GLES20.glScissor(width * i, 0, width, totalHeight); draw(i); GLES20.glDisable(GLES20.GL_SCISSOR_TEST); } } private void draw(int index){ // Set our per-vertex lighting program. mProgram.use(); glCheck("MDBarrelDistortionLinePipe mProgram use"); object3D.uploadVerticesBufferIfNeed(mProgram, index); object3D.uploadTexCoordinateBufferIfNeed(mProgram, index); // Pass in the combined matrix. mDirector.beforeShot(); mDirector.shot(mProgram, MDPosition.getOriginalPosition()); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mDrawingCache.getTextureOutput()); object3D.draw(); } private class MDBarrelDistortionMesh extends MDAbsObject3D { private static final String TAG = "MDBarrelDistortionMesh"; private int mode; private FloatBuffer singleTexCoordinateBuffer; public MDBarrelDistortionMesh() { } @Override public FloatBuffer getTexCoordinateBuffer(int index) { if (mode == 1){ return singleTexCoordinateBuffer; } else if (mode == 2){ return super.getTexCoordinateBuffer(index); } else { return null; } } @Override protected void executeLoad(Context context) { generateMesh(this); } private void generateMesh(MDAbsObject3D object3D){ int rows = 10; int columns = 10; int numPoint = (rows + 1) * (columns + 1); short r, s; float z = -8; float R = 1f/(float) rows; float S = 1f/(float) columns; float[] vertexs = new float[numPoint * 3]; float[] texcoords = new float[numPoint * 2]; float[] texcoords1 = new float[numPoint * 2]; float[] texcoords2 = new float[numPoint * 2]; short[] indices = new short[numPoint * 6]; int t = 0; int v = 0; for(r = 0; r < rows + 1; r++) { for(s = 0; s < columns + 1; s++) { int tu = t++; int tv = t++; texcoords[tu] = s*S; texcoords[tv] = r*R; texcoords1[tu] = s*S*0.5f; texcoords1[tv] = r*R; texcoords2[tu] = s*S*0.5f + 0.5f; texcoords2[tv] = r*R; vertexs[v++] = (s * S * 2 - 1); vertexs[v++] = (r * R * 2 - 1); vertexs[v++] = z; } } applyBarrelDistortion(numPoint, vertexs); int counter = 0; int sectorsPlusOne = columns + 1; for(r = 0; r < rows; r++){ for(s = 0; s < columns; s++) { short k0 = (short) ((r) * sectorsPlusOne + (s+1)); // (c) short k1 = (short) ((r+1) * sectorsPlusOne + (s)); //(b) short k2 = (short) (r * sectorsPlusOne + s); //(a); short k3 = (short) ((r) * sectorsPlusOne + (s+1)); // (c) short k4 = (short) ((r+1) * sectorsPlusOne + (s+1)); // (d) short k5 = (short) ((r+1) * sectorsPlusOne + (s)); //(b) indices[counter++] = k0; indices[counter++] = k1; indices[counter++] = k2; indices[counter++] = k3; indices[counter++] = k4; indices[counter++] = k5; } } // initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (# of coordinate values * 4 bytes per float) vertexs.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertexs); vertexBuffer.position(0); // initialize vertex byte buffer for shape coordinates ByteBuffer ee = ByteBuffer.allocateDirect( texcoords.length * 4); ee.order(ByteOrder.nativeOrder()); FloatBuffer texBuffer = ee.asFloatBuffer(); texBuffer.put(texcoords); texBuffer.position(0); // initialize vertex byte buffer for shape coordinates ByteBuffer cc = ByteBuffer.allocateDirect( texcoords1.length * 4); cc.order(ByteOrder.nativeOrder()); FloatBuffer texBuffer1 = cc.asFloatBuffer(); texBuffer1.put(texcoords1); texBuffer1.position(0); // initialize vertex byte buffer for shape coordinates ByteBuffer dd = ByteBuffer.allocateDirect( texcoords2.length * 4); dd.order(ByteOrder.nativeOrder()); FloatBuffer texBuffer2 = dd.asFloatBuffer(); texBuffer2.put(texcoords2); texBuffer2.position(0); // initialize byte buffer for the draw list ByteBuffer dlb = ByteBuffer.allocateDirect( // (# of coordinate values * 2 bytes per short) indices.length * 2); dlb.order(ByteOrder.nativeOrder()); ShortBuffer indexBuffer = dlb.asShortBuffer(); indexBuffer.put(indices); indexBuffer.position(0); object3D.setIndicesBuffer(indexBuffer); object3D.setTexCoordinateBuffer(0,texBuffer1); object3D.setTexCoordinateBuffer(1,texBuffer2); object3D.setVerticesBuffer(0,vertexBuffer); object3D.setVerticesBuffer(1,vertexBuffer); object3D.setNumIndices(indices.length); singleTexCoordinateBuffer = texBuffer; } private void applyBarrelDistortion(int numPoint, float[] vertexs) { PointF pointF = new PointF(); for (int i = 0; i < numPoint; i++){ int xIndex = i * 3; int yIndex = i * 3 + 1; float xValue = vertexs[xIndex]; float yValue = vertexs[yIndex]; pointF.set(xValue,yValue); VRUtil.barrelDistortion(mConfiguration.getParamA(), mConfiguration.getParamB(), mConfiguration.getParamC(), pointF); vertexs[xIndex] = pointF.x * mConfiguration.getScale(); vertexs[yIndex] = pointF.y * mConfiguration.getScale(); // Log.e(TAG,String.format("%f %f => %f %f",xValue,yValue,pointF.x,pointF.y)); } } public void setMode(int mode) { this.mode = mode; } } }