package com.asha.vrlib.plugins;
import android.content.Context;
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.MDDirection;
import com.asha.vrlib.model.MDMainPluginBuilder;
import com.asha.vrlib.model.MDPosition;
import com.asha.vrlib.objects.MDAbsObject3D;
import com.asha.vrlib.objects.MDObject3DHelper;
import com.asha.vrlib.strategy.projection.ProjectionModeManager;
import com.asha.vrlib.texture.MD360Texture;
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/8/20.
* hzqiujiadi ashqalcn@gmail.com
*/
public class MDMultiFishEyePlugin extends MDAbsPlugin {
private MD360Program mProgram;
private MD360Program mBitmapProgram;
private MD360Texture mTexture;
private ProjectionModeManager mProjectionModeManager;
private MDMesh mConverterObject3D;
private MD360Director mFixedDirector;
private MDDrawingCache mDrawingCache;
public MDMultiFishEyePlugin(MDMainPluginBuilder builder, float radius, MDDirection direction) {
mTexture = builder.getTexture();
mProgram = new MD360Program(builder.getContentType());
mBitmapProgram = new MD360Program(MDVRLibrary.ContentType.FBO);
mProjectionModeManager = builder.getProjectionModeManager();
mFixedDirector = new MD360DirectorFactory.OrthogonalImpl().createDirector(0);
mConverterObject3D = new MDMesh(radius, direction);
mDrawingCache = new MDDrawingCache();
}
@Override
public void initInGL(Context context) {
mProgram.build(context);
mBitmapProgram.build(context);
mTexture.create();
MDObject3DHelper.loadObj(context, mConverterObject3D);
}
@Override
public void beforeRenderer(int totalWidth, int totalHeight) {
mFixedDirector.setViewport(totalWidth, totalHeight);
mDrawingCache.bind(totalWidth, totalHeight);
drawConverter(totalWidth,totalHeight);
mDrawingCache.unbind();
}
@Override
public void renderer(int index, int width, int height, MD360Director director) {
MDAbsObject3D object3D = mProjectionModeManager.getObject3D();
// check obj3d
if (object3D == null) return;
// Update Projection
director.setViewport(width, height);
// Set our per-vertex lighting program.
mBitmapProgram.use();
glCheck("MDPanoramaPlugin mProgram use");
// mTexture.texture(mProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mDrawingCache.getTextureOutput());
object3D.uploadVerticesBufferIfNeed(mBitmapProgram, index);
object3D.uploadTexCoordinateBufferIfNeed(mBitmapProgram, index);
// Pass in the combined matrix.
director.beforeShot();
director.shot(mBitmapProgram, getModelPosition());
object3D.draw();
}
@Override
public void destroyInGL() {
mTexture = null;
}
@Override
protected MDPosition getModelPosition() {
return mProjectionModeManager.getModelPosition();
}
@Override
protected boolean removable() {
return false;
}
private void drawConverter(int width, int height){
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
glCheck("MDMultiFisheyeConvertLinePipe glClear");
int itemWidth = width / 2;
for (int index = 0; index < 2; index++){
GLES20.glViewport(itemWidth * index, 0, itemWidth, height);
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
GLES20.glScissor(itemWidth * index, 0, itemWidth, height);
mProgram.use();
mTexture.texture(mProgram);
mFixedDirector.setViewport(itemWidth, height);
mConverterObject3D.uploadVerticesBufferIfNeed(mProgram, index);
mConverterObject3D.uploadTexCoordinateBufferIfNeed(mProgram, index);
// Pass in the combined matrix.
mFixedDirector.beforeShot();
mFixedDirector.shot(mProgram, MDPosition.getOriginalPosition());
mConverterObject3D.draw();
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
}
}
private class MDMesh extends MDAbsObject3D {
private static final String TAG = "MDMesh";
private final MDDirection direction;
private final float radius;
public MDMesh(float radius, MDDirection direction) {
this.radius = radius;
this.direction = direction;
}
@Override
protected void executeLoad(Context context) {
generateMesh(this);
}
private void generateMesh(MDAbsObject3D object3D){
final float PI = (float) Math.PI;
int rows = 16;
int columns = 16;
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[] 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++) {
vertexs[v++] = (s * S * 2 - 1);
vertexs[v++] = (r * R * 2 - 1);
vertexs[v++] = z;
float FOV = 3.141592654f; // FOV of the fisheye, eg: 180 degrees
float width = 1;
float height = 1;
float theta = PI * (s * S - 0.5f); // -pi to pi
float phi = PI * (r * R - 0.5f); // -pi/2 to pi/2
float psphx = (float) (Math.cos(phi) * Math.sin(theta));
float psphy = (float) (Math.cos(phi) * Math.cos(theta));
float psphz = (float) Math.sin(phi);
theta = (float) Math.atan2(psphz, psphx);
phi = (float) Math.atan2(Math.sqrt(psphx*psphx + psphz*psphz), psphy);
float rr = radius * phi / FOV;
float a = (float) (0.5f * width + rr * Math.cos(theta));
float b = (float) (0.5f * height + rr * Math.sin(theta));
if (direction == MDDirection.HORIZONTAL){
texcoords[t*2] = a * 0.5f;
texcoords[t*2 + 1] = b;
texcoords2[t*2] = a * 0.5f + 0.5f;
texcoords2[t*2 + 1] = b;
} else {
texcoords[t*2] = a;
texcoords[t*2 + 1] = b * 0.5f;
texcoords2[t*2] = a;
texcoords2[t*2 + 1] = b * 0.5f + 0.5f;
}
t++;
}
}
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 ee2 = ByteBuffer.allocateDirect(
texcoords2.length * 4);
ee2.order(ByteOrder.nativeOrder());
FloatBuffer texBuffer2 = ee2.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,texBuffer);
object3D.setTexCoordinateBuffer(1,texBuffer2);
object3D.setVerticesBuffer(0,vertexBuffer);
object3D.setVerticesBuffer(1,vertexBuffer);
object3D.setNumIndices(indices.length);
}
}
}