/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.rs.vr.engine;
import android.util.Log;
import java.text.DecimalFormat;
import java.util.Arrays;
public class ViewMatrix extends Matrix {
private static final String LOGTAG = "ViewMatrix";
private double[] mLookPoint;
private double[] mEyePoint;
private double[] mUpVector;
private double mScreenWidth;
private int[] mScreenDim;
private Matrix mStartMatrix;
private double[] mStartV = new double[3];
private double[] mMoveToV = new double[3];
private double[] mStartEyePoint;
private double[] mStartUpVector;
private Quaternion mQ = new Quaternion(0, 0, 0, 0);
public final static char UP_AT = 0x001;
public final static char DOWN_AT = 0x002;
public final static char RIGHT_AT = 0x010;
public final static char LEFT_AT = 0x020;
public final static char FORWARD_AT = 0x100;
public final static char BEHIND_AT = 0x200;
public void clone(ViewMatrix src) {
if (src.mLookPoint != null) {
System.arraycopy(src.mLookPoint, 0, mLookPoint, 0, mLookPoint.length);
}
if (src.mEyePoint != null) {
System.arraycopy(src.mEyePoint, 0, mEyePoint, 0, mEyePoint.length);
}
if (src.mUpVector != null) {
System.arraycopy(src.mUpVector, 0, mUpVector, 0, mUpVector.length);
}
mScreenWidth = src.mScreenWidth;
if (src.mScreenDim != null) {
System.arraycopy(src.mScreenDim, 0, mScreenDim, 0, mScreenDim.length);
}
if (src.mStartV != null) {
System.arraycopy(src.mStartV, 0, mStartV, 0, mStartV.length);
}
if (src.mMoveToV != null) {
System.arraycopy(src.mMoveToV, 0, mMoveToV, 0, mMoveToV.length);
}
if (src.mStartEyePoint != null) {
if (mStartEyePoint == null) {
mStartEyePoint = Arrays.copyOf(src.mStartEyePoint,src.mStartEyePoint.length);
} else {
System.arraycopy(src.mStartEyePoint, 0, mStartEyePoint, 0, mStartEyePoint.length);
}
}
if (src.mStartUpVector != null) {
if (mStartUpVector == null) {
mStartUpVector = Arrays.copyOf(src.mStartUpVector,src.mStartUpVector.length);
} else {
System.arraycopy(src.mStartUpVector, 0, mStartUpVector, 0, mStartUpVector.length);
}
}
if (src.mStartMatrix != null) {
if (mStartMatrix == null) {
mStartMatrix = new Matrix();
}
mStartMatrix.clone(src.mStartMatrix);
}
mQ.clone(src.mQ);
super.clone(src);
}
private static String toStr(double d) {
String s = " " + df.format(d);
return s.substring(s.length() - 9);
}
private static String toStr(double[] d) {
String s = "[";
for (int i = 0; i < d.length; i++) {
s += toStr(d[i]);
}
return s + "]";
}
private static DecimalFormat df = new DecimalFormat("##0.000");
@Override
public void print() {
Log.v(LOGTAG, "mLookPoint :" + toStr(mLookPoint));
Log.v(LOGTAG, "mEyePoint :" + toStr(mEyePoint));
Log.v(LOGTAG, "mUpVector :" + toStr(mUpVector));
Log.v(LOGTAG, "mScreenWidth: " + toStr(mScreenWidth));
Log.v(LOGTAG, "mScreenDim :[" + mScreenDim[0] + ", " + mScreenDim[1] + "]");
}
public ViewMatrix() {
mLookPoint = new double[3];
mEyePoint = new double[3];
mUpVector = new double[3];
mScreenDim = new int[2];
}
public void setScreenDim(int x, int y) {
mScreenDim = new int[]{x, y};
}
public double[] getLookPoint() {
return mLookPoint;
}
public void setLookPoint(double[] mLookPoint) {
this.mLookPoint = mLookPoint;
}
public double[] getEyePoint() {
return mEyePoint;
}
public void setEyePoint(double[] mEyePoint) {
this.mEyePoint = mEyePoint;
}
public double[] getUpVector() {
return mUpVector;
}
public void setUpVector(double[] mUpVector) {
this.mUpVector = mUpVector;
}
public double getScreenWidth() {
return mScreenWidth;
}
public void setScreenWidth(double screenWidth) {
this.mScreenWidth = screenWidth;
}
public void makeUnit() {
}
public void calcMatrix() {
if (mScreenDim == null) {
return;
}
double scale = mScreenWidth / mScreenDim[0];
double[] zv = {
mLookPoint[0] - mEyePoint[0],
mLookPoint[1] - mEyePoint[1],
mLookPoint[2] - mEyePoint[2]
};
VectorUtil.normalize(zv);
double[] m = new double[16];
m[2] = zv[0];
m[6] = zv[1];
m[10] = zv[2];
m[14] = 0;
calcRight(zv, mUpVector, zv);
double[] right = zv;
m[0] = right[0] * scale;
m[4] = right[1] * scale;
m[8] = right[2] * scale;
m[12] = 0;
m[1] = -mUpVector[0] * scale;
m[5] = -mUpVector[1] * scale;
m[9] = -mUpVector[2] * scale;
m[13] = 0;
double sw = mScreenDim[0] / 2 - 0.5;
double sh = mScreenDim[1] / 2 - 0.5;
double sz = -0.5;
m[3] = mEyePoint[0] - (m[0] * sw + m[1] * sh + m[2] * sz);
m[7] = mEyePoint[1] - (m[4] * sw + m[5] * sh + m[6] * sz);
m[11] = mEyePoint[2] - (m[8] * sw + m[9] * sh + m[10] * sz);
m[15] = 1;
this.m = m;
}
static void calcRight(double[] a, double[] b, double[] out) {
VectorUtil.cross(a, b, out);
}
public static void main(String[] args) {
double[] up = {0, 0, 1};
double[] look = {0, 0, 0};
double[] eye = {-10, 0, 0};
ViewMatrix v = new ViewMatrix();
v.setEyePoint(eye);
v.setLookPoint(look);
v.setUpVector(up);
v.setScreenWidth(10);
v.setScreenDim(512, 512);
v.calcMatrix();
}
private void calcLook(TriData tri, float[] voxelDim, int w, int h) {
float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
for (int i = 0; i < tri.mVert.length; i += 3) {
maxx = Math.max(tri.mVert[i], maxx);
minx = Math.min(tri.mVert[i], minx);
maxy = Math.max(tri.mVert[i + 1], maxy);
miny = Math.min(tri.mVert[i + 1], miny);
maxz = Math.max(tri.mVert[i + 2], maxz);
minz = Math.min(tri.mVert[i + 2], minz);
}
mLookPoint = new double[]{voxelDim[0] * (maxx + minx) / 2,
voxelDim[1] * (maxy + miny) / 2,
voxelDim[2] * (maxz + minz) / 2};
mScreenWidth = Math.max(voxelDim[0] * (maxx - minx),
Math.max(voxelDim[1] * (maxy - miny),
voxelDim[2] * (maxz - minz))) * 2;
}
private void calcLook(TriData triW, int w, int h) {
float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
for (int i = 0; i < triW.mVert.length; i += 3) {
maxx = Math.max(triW.mVert[i], maxx);
minx = Math.min(triW.mVert[i], minx);
maxy = Math.max(triW.mVert[i + 1], maxy);
miny = Math.min(triW.mVert[i + 1], miny);
maxz = Math.max(triW.mVert[i + 2], maxz);
minz = Math.min(triW.mVert[i + 2], minz);
}
mLookPoint = new double[]{(maxx + minx) / 2, (maxy + miny) / 2, (maxz + minz) / 2};
mScreenWidth = 2 * Math.max((maxx - minx), Math.max((maxy - miny), (maxz - minz)));
}
public void look(char dir, TriData tri, float[] voxelDim, int w, int h) {
calcLook(tri, w, h);
int dx = ((dir >> 4) & 0xF);
int dy = ((dir >> 8) & 0xF);
int dz = ((dir >> 0) & 0xF);
if (dx > 1) {
dx = -1;
}
if (dy > 1) {
dy = -1;
}
if (dz > 1) {
dz = -1;
}
mEyePoint = new double[]{mLookPoint[0] + 2 * mScreenWidth * dx,
mLookPoint[1] + 2 * mScreenWidth * dy,
mLookPoint[2] + 2 * mScreenWidth * dz};
double[] zv = new double[]{-dx, -dy, -dz};
double[] rv = new double[]{(dx == 0) ? 1 : 0, (dx == 0) ? 0 : 1, 0};
double[] up = new double[3];
VectorUtil.norm(zv);
VectorUtil.norm(rv);
VectorUtil.cross(zv, rv, up);
VectorUtil.cross(zv, up, rv);
VectorUtil.cross(zv, rv, up);
mUpVector = up;
mScreenDim = new int[]{w, h};
calcMatrix();
}
public void lookAt(TriData tri, float[] voxelDim, int w, int h) {
calcLook(tri, voxelDim, w, h);
mEyePoint = new double[]{mLookPoint[0] + mScreenWidth,
mLookPoint[1] + mScreenWidth,
mLookPoint[2] + mScreenWidth};
double[] zv = new double[]{-1, -1, -1};
double[] rv = new double[]{1, 1, 0};
double[] up = new double[3];
VectorUtil.norm(zv);
VectorUtil.norm(rv);
VectorUtil.cross(zv, rv, up);
VectorUtil.cross(zv, up, rv);
VectorUtil.cross(zv, rv, up);
mUpVector = up;
mScreenDim = new int[]{w, h};
calcMatrix();
}
public void trackBallUP(float x, float y) {
}
public void trackBallDown(float x, float y) {
ballToVec(x, y, mStartV);
mStartEyePoint = Arrays.copyOf(mEyePoint, m.length);
mStartUpVector = Arrays.copyOf(mUpVector, m.length);
mStartMatrix = new Matrix(this);
mStartMatrix.makeRotation();
}
public void trackBallMove(float x, float y) {
ballToVec(x, y, mMoveToV);
double angle = Quaternion.calcAngle(mStartV, mMoveToV);
if (angle < 0.0001) {
calcMatrix();
return;
}
double[] axis = Quaternion.calcAxis(mStartV, mMoveToV);
axis = mStartMatrix.vecmult(axis);
mQ.set(angle, axis);
VectorUtil.sub(mLookPoint, mStartEyePoint, mEyePoint);
mEyePoint = mQ.rotateVec(mEyePoint);
mUpVector = mQ.rotateVec(mStartUpVector);
VectorUtil.sub(mLookPoint, mEyePoint, mEyePoint);
calcMatrix();
}
void ballToVec(float x, float y, double[] v) {
float ballRadius = Math.min(mScreenDim[0], mScreenDim[1]) * .4f;
double cx = mScreenDim[0] / 2.;
double cy = mScreenDim[1] / 2.;
double dx = (cx - x) / ballRadius;
double dy = (cy - y) / ballRadius;
double scale = dx * dx + dy * dy;
if (scale > 1) {
scale = Math.sqrt(scale);
dx = dx / scale;
dy = dy / scale;
}
double dz = Math.sqrt(Math.abs(1 - (dx * dx + dy * dy)));
v[0] = dx;
v[1] = dy;
v[2] = dz;
VectorUtil.normalize(v);
}
}