package com.asha.vrlib.common;
import android.graphics.PointF;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.opengl.Matrix;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import com.asha.vrlib.model.MDDirectorSnapshot;
import com.asha.vrlib.model.MDHitPoint;
import com.asha.vrlib.model.MDRay;
import com.asha.vrlib.model.MDVector3D;
/**
* Created by hzqiujiadi on 16/3/13.
* hzqiujiadi ashqalcn@gmail.com
*/
public class VRUtil {
private static final String TAG = "VRUtil";
private static float[] sUIThreadTmp = new float[16];
private static float[] sGLThreadTmp = new float[16];
private static float[] sTruncatedVector = new float[4];
private static boolean sIsTruncated = false;
public static void sensorRotationVector2Matrix(SensorEvent event, int rotation, float[] output) {
if (!sIsTruncated) {
try {
SensorManager.getRotationMatrixFromVector(sUIThreadTmp, event.values);
} catch (Exception e) {
// On some Samsung devices, SensorManager#getRotationMatrixFromVector throws an exception
// if the rotation vector has more than 4 elements. Since only the four first elements are used,
// we can truncate the vector without losing precision.
Log.e(TAG, "maybe Samsung bug, will truncate vector");
sIsTruncated = true;
}
}
if (sIsTruncated){
System.arraycopy(event.values, 0, sTruncatedVector, 0, 4);
SensorManager.getRotationMatrixFromVector(sUIThreadTmp, sTruncatedVector);
}
float[] values = event.values;
switch (rotation){
case Surface.ROTATION_0:
case Surface.ROTATION_180: /* Notice: not supported for ROTATION_180! */
SensorManager.getRotationMatrixFromVector(output, values);
break;
case Surface.ROTATION_90:
SensorManager.getRotationMatrixFromVector(sUIThreadTmp, values);
SensorManager.remapCoordinateSystem(sUIThreadTmp, SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_X, output);
break;
case Surface.ROTATION_270:
SensorManager.getRotationMatrixFromVector(sUIThreadTmp, values);
SensorManager.remapCoordinateSystem(sUIThreadTmp, SensorManager.AXIS_MINUS_Y, SensorManager.AXIS_X, output);
break;
}
Matrix.rotateM(output, 0, 90.0F, 1.0F, 0.0F, 0.0F);
}
public static void notNull(Object object, String error){
if (object == null) {
throw new RuntimeException(error);
}
}
public static void checkMainThread(String error){
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException(error);
}
}
public static void checkGLThread(String error){
if (Looper.getMainLooper() == Looper.myLooper()) {
throw new RuntimeException(error);
}
}
public static void barrelDistortion(double paramA, double paramB, double paramC, PointF src){
double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image
float d = 1.0f;
// center of dst image
double centerX = 0f;
double centerY = 0f;
if (src.x == centerX && src.y == centerY){
return;
}
// cartesian coordinates of the destination point (relative to the centre of the image)
double deltaX = (src.x - centerX) / d;
double deltaY = (src.y - centerY) / d;
// distance or radius of dst image
double dstR = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// distance or radius of src image (with formula)
double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR;
// comparing old and new distance to get factor
double factor = Math.abs(dstR / srcR);
// coordinates in source image
float xResult = (float) (centerX + (deltaX * factor * d));
float yResult = (float) (centerY + (deltaY * factor * d));
src.set(xResult,yResult);
}
public static MDVector3D vec3Sub(MDVector3D v1, MDVector3D v2){
return new MDVector3D().setX(v1.getX() - v2.getX()).setY(v1.getY() - v2.getY()).setZ(v1.getZ() - v2.getZ());
}
public static MDVector3D vec3Cross(MDVector3D v1, MDVector3D v2){
return new MDVector3D().setX(v1.getY() * v2.getZ() - v2.getY() * v1.getZ())
.setY(v1.getZ() * v2.getX() - v2.getZ() * v1.getX())
.setZ(v1.getX() * v2.getY() - v2.getX() * v1.getY());
}
public static float vec3Dot(MDVector3D v1, MDVector3D v2){
return vec3Dot(v1.getX(), v1.getY(), v1.getZ(), v2.getX(), v2.getY(), v2.getZ());
}
public static float vec3Dot(float x1, float y1, float z1, float x2, float y2, float z2){
return x1 * x2 + y1 * y2 + z1 * z2;
}
public static boolean invertM(float[] output, float[] input){
if (input == output){
return false;
}
return Matrix.invertM(output, 0, input, 0);
}
public static MDRay point2Ray(float x, float y, MDDirectorSnapshot info){
checkMainThread("point2Ray must called in main Thread");
float[] view = info.getViewMatrix();
float[] temp = sUIThreadTmp;
boolean success = invertM(temp, view);
if (success){
MDVector3D v = new MDVector3D();
float[] projection = info.getProjectionMatrix();
v.setX(- ( ( ( 2.0f * x ) / info.getViewportWidth() ) - 1 ) / projection[0]);
v.setY(( ( ( 2.0f * y ) / info.getViewportHeight() ) - 1 ) / projection[5]);
v.setZ(1.0f);
MDVector3D vPickRayDir = new MDVector3D();
MDVector3D vPickRayOrig = new MDVector3D();
vPickRayDir.setX(v.getX() * temp[0] + v.getY() * temp[4] + v.getZ() * temp[8]);
vPickRayDir.setY(v.getX() * temp[1] + v.getY() * temp[5] + v.getZ() * temp[9]);
vPickRayDir.setZ(v.getX() * temp[2] + v.getY() * temp[6] + v.getZ() * temp[10]);
vPickRayOrig.setX(temp[12]);
vPickRayOrig.setY(temp[13]);
vPickRayOrig.setZ(temp[14]);
return new MDRay(vPickRayOrig,vPickRayDir);
} else {
return null;
}
}
public static void intersectTriangle(MDRay ray, MDVector3D v0, MDVector3D v1, MDVector3D v2, MDHitPoint result){
// Find vectors for two edges sharing vert0
MDVector3D edge1 = vec3Sub(v1 , v0);
MDVector3D edge2 = vec3Sub(v2 , v0);
// Begin calculating determinant - also used to calculate U parameter
MDVector3D pvec;
pvec = vec3Cross( ray.getDir(), edge2 );
// If determinant is near zero, ray lies in plane of triangle
float det = vec3Dot( edge1, pvec );
MDVector3D tvec;
if( det > 0 ) {
tvec = vec3Sub(ray.getOrig() , v0);
} else {
tvec = vec3Sub(v0 , ray.getOrig());
det = -det;
}
if( det < 0.0001f ){
result.asNotHit();
return;
}
// Calculate U parameter and test bounds
float u = vec3Dot(tvec, pvec);
if( u < 0.0f || u > det ){
result.asNotHit();
return;
}
// Prepare to test V parameter
MDVector3D qvec;
qvec = vec3Cross(tvec, edge1);
// Calculate V parameter and test bounds
float v = vec3Dot(ray.getDir(), qvec);
if( v < 0.0f || u + v > det ){
result.asNotHit();
return;
}
// Calculate t, scale parameters, ray intersects triangle
float t = vec3Dot(edge2, qvec);
float fInvDet = 1.0f / det;
t *= fInvDet;
u *= fInvDet;
v *= fInvDet;
if (t > 0){
result.asNotHit();
return;
}
result.set(t, u, v);
}
public static void getEulerAngles(float[] headView, float[] output) {
float pitch = (float) Math.asin((double) headView[6]);
float yaw;
float roll;
if (Math.abs(headView[6]) < 0.9999999999D) {
yaw = (float) Math.atan2((double) (-headView[2]), (double) headView[10]);
roll = (float) Math.atan2((double) (-headView[4]), (double) headView[5]);
} else {
yaw = 0.0F;
roll = (float) Math.atan2((double) headView[1], (double) headView[0]);
}
output[0] = -pitch;
output[1] = -yaw;
output[2] = -roll;
float pitchAngle = (float) Math.toDegrees(output[0]);
float yawAngle = (float) Math.toDegrees(output[1]);
float rollAngle = (float) Math.toDegrees(output[2]);
Log.e(TAG, String.format("pitchAngle=%f, yawAngle=%f, rollAngle=%f", pitchAngle, yawAngle, rollAngle));
}
public static void printMatrix(float[] m){
Log.d(TAG, "printMatrix");
Log.d(TAG, String.format("%f, %f, %f, %f",m[0],m[1],m[2],m[3]));
Log.d(TAG, String.format("%f, %f, %f, %f",m[4],m[5],m[6],m[7]));
Log.d(TAG, String.format("%f, %f, %f, %f",m[8],m[9],m[10],m[11]));
Log.d(TAG, String.format("%f, %f, %f, %f",m[12],m[13],m[14],m[15]));
}
}