1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.rs.vr.engine;
18 
19 import android.util.Log;
20 
21 import java.text.DecimalFormat;
22 import java.util.Arrays;
23 
24 public class ViewMatrix extends Matrix {
25     private static final String LOGTAG = "ViewMatrix";
26     private double[] mLookPoint;
27     private double[] mEyePoint;
28     private double[] mUpVector;
29     private double mScreenWidth;
30     private int[] mScreenDim;
31 
32     private Matrix mStartMatrix;
33     private double[] mStartV = new double[3];
34     private double[] mMoveToV = new double[3];
35     private double[] mStartEyePoint;
36     private double[] mStartUpVector;
37     private Quaternion mQ = new Quaternion(0, 0, 0, 0);
38 
39     public final static char UP_AT = 0x001;
40     public final static char DOWN_AT = 0x002;
41     public final static char RIGHT_AT = 0x010;
42     public final static char LEFT_AT = 0x020;
43     public final static char FORWARD_AT = 0x100;
44     public final static char BEHIND_AT = 0x200;
45 
clone(ViewMatrix src)46     public void clone(ViewMatrix src) {
47         if (src.mLookPoint != null) {
48             System.arraycopy(src.mLookPoint, 0, mLookPoint, 0, mLookPoint.length);
49         }
50         if (src.mEyePoint != null) {
51             System.arraycopy(src.mEyePoint, 0, mEyePoint, 0, mEyePoint.length);
52         }
53         if (src.mUpVector != null) {
54             System.arraycopy(src.mUpVector, 0, mUpVector, 0, mUpVector.length);
55         }
56         mScreenWidth = src.mScreenWidth;
57 
58         if (src.mScreenDim != null) {
59             System.arraycopy(src.mScreenDim, 0, mScreenDim, 0, mScreenDim.length);
60         }
61         if (src.mStartV != null) {
62             System.arraycopy(src.mStartV, 0, mStartV, 0, mStartV.length);
63         }
64         if (src.mMoveToV != null) {
65             System.arraycopy(src.mMoveToV, 0, mMoveToV, 0, mMoveToV.length);
66         }
67 
68 
69         if (src.mStartEyePoint != null) {
70             if (mStartEyePoint == null) {
71                 mStartEyePoint = Arrays.copyOf(src.mStartEyePoint,src.mStartEyePoint.length);
72             } else {
73                 System.arraycopy(src.mStartEyePoint, 0, mStartEyePoint, 0, mStartEyePoint.length);
74             }
75         }
76         if (src.mStartUpVector != null) {
77             if (mStartUpVector == null) {
78                 mStartUpVector = Arrays.copyOf(src.mStartUpVector,src.mStartUpVector.length);
79             } else {
80                 System.arraycopy(src.mStartUpVector, 0, mStartUpVector, 0, mStartUpVector.length);
81             }
82         }
83         if (src.mStartMatrix != null) {
84             if (mStartMatrix == null) {
85                 mStartMatrix = new Matrix();
86             }
87             mStartMatrix.clone(src.mStartMatrix);
88         }
89         mQ.clone(src.mQ);
90         super.clone(src);
91     }
92 
toStr(double d)93     private static String toStr(double d) {
94         String s = "         " + df.format(d);
95         return s.substring(s.length() - 9);
96     }
97 
toStr(double[] d)98     private static String toStr(double[] d) {
99         String s = "[";
100         for (int i = 0; i < d.length; i++) {
101             s += toStr(d[i]);
102         }
103 
104         return s + "]";
105     }
106 
107     private static DecimalFormat df = new DecimalFormat("##0.000");
108 
109     @Override
print()110     public void print() {
111         Log.v(LOGTAG, "mLookPoint  :" + toStr(mLookPoint));
112         Log.v(LOGTAG, "mEyePoint   :" + toStr(mEyePoint));
113         Log.v(LOGTAG, "mUpVector   :" + toStr(mUpVector));
114         Log.v(LOGTAG, "mScreenWidth: " + toStr(mScreenWidth));
115         Log.v(LOGTAG, "mScreenDim  :[" + mScreenDim[0] + ", " + mScreenDim[1] + "]");
116     }
117 
ViewMatrix()118     public ViewMatrix() {
119         mLookPoint = new double[3];
120         mEyePoint = new double[3];
121         mUpVector = new double[3];
122         mScreenDim = new int[2];
123     }
124 
setScreenDim(int x, int y)125     public void setScreenDim(int x, int y) {
126         mScreenDim = new int[]{x, y};
127     }
128 
getLookPoint()129     public double[] getLookPoint() {
130         return mLookPoint;
131     }
132 
setLookPoint(double[] mLookPoint)133     public void setLookPoint(double[] mLookPoint) {
134         this.mLookPoint = mLookPoint;
135     }
136 
getEyePoint()137     public double[] getEyePoint() {
138         return mEyePoint;
139     }
140 
setEyePoint(double[] mEyePoint)141     public void setEyePoint(double[] mEyePoint) {
142         this.mEyePoint = mEyePoint;
143     }
144 
getUpVector()145     public double[] getUpVector() {
146         return mUpVector;
147     }
148 
setUpVector(double[] mUpVector)149     public void setUpVector(double[] mUpVector) {
150         this.mUpVector = mUpVector;
151     }
152 
getScreenWidth()153     public double getScreenWidth() {
154         return mScreenWidth;
155     }
156 
setScreenWidth(double screenWidth)157     public void setScreenWidth(double screenWidth) {
158         this.mScreenWidth = screenWidth;
159     }
160 
makeUnit()161     public void makeUnit() {
162 
163     }
164 
calcMatrix()165     public void calcMatrix() {
166         if (mScreenDim == null) {
167             return;
168         }
169         double scale = mScreenWidth / mScreenDim[0];
170         double[] zv = {
171                 mLookPoint[0] - mEyePoint[0],
172                 mLookPoint[1] - mEyePoint[1],
173                 mLookPoint[2] - mEyePoint[2]
174         };
175         VectorUtil.normalize(zv);
176 
177 
178         double[] m = new double[16];
179         m[2] = zv[0];
180         m[6] = zv[1];
181         m[10] = zv[2];
182         m[14] = 0;
183 
184         calcRight(zv, mUpVector, zv);
185         double[] right = zv;
186 
187         m[0] = right[0] * scale;
188         m[4] = right[1] * scale;
189         m[8] = right[2] * scale;
190         m[12] = 0;
191 
192         m[1] = -mUpVector[0] * scale;
193         m[5] = -mUpVector[1] * scale;
194         m[9] = -mUpVector[2] * scale;
195         m[13] = 0;
196         double sw = mScreenDim[0] / 2 - 0.5;
197         double sh = mScreenDim[1] / 2 - 0.5;
198         double sz = -0.5;
199         m[3] = mEyePoint[0] - (m[0] * sw + m[1] * sh + m[2] * sz);
200         m[7] = mEyePoint[1] - (m[4] * sw + m[5] * sh + m[6] * sz);
201         m[11] = mEyePoint[2] - (m[8] * sw + m[9] * sh + m[10] * sz);
202 
203         m[15] = 1;
204         this.m = m;
205     }
206 
calcRight(double[] a, double[] b, double[] out)207     static void calcRight(double[] a, double[] b, double[] out) {
208         VectorUtil.cross(a, b, out);
209     }
210 
main(String[] args)211     public static void main(String[] args) {
212         double[] up = {0, 0, 1};
213         double[] look = {0, 0, 0};
214         double[] eye = {-10, 0, 0};
215         ViewMatrix v = new ViewMatrix();
216         v.setEyePoint(eye);
217         v.setLookPoint(look);
218         v.setUpVector(up);
219         v.setScreenWidth(10);
220         v.setScreenDim(512, 512);
221         v.calcMatrix();
222     }
223 
calcLook(TriData tri, float[] voxelDim, int w, int h)224     private void calcLook(TriData tri, float[] voxelDim, int w, int h) {
225         float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
226         float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
227 
228         for (int i = 0; i < tri.mVert.length; i += 3) {
229             maxx = Math.max(tri.mVert[i], maxx);
230             minx = Math.min(tri.mVert[i], minx);
231             maxy = Math.max(tri.mVert[i + 1], maxy);
232             miny = Math.min(tri.mVert[i + 1], miny);
233             maxz = Math.max(tri.mVert[i + 2], maxz);
234             minz = Math.min(tri.mVert[i + 2], minz);
235         }
236         mLookPoint = new double[]{voxelDim[0] * (maxx + minx) / 2,
237                 voxelDim[1] * (maxy + miny) / 2,
238                 voxelDim[2] * (maxz + minz) / 2};
239         mScreenWidth = Math.max(voxelDim[0] * (maxx - minx),
240                 Math.max(voxelDim[1] * (maxy - miny),
241                         voxelDim[2] * (maxz - minz))) * 2;
242     }
243 
calcLook(TriData triW, int w, int h)244     private void calcLook(TriData triW, int w, int h) {
245         float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, minz = Float.MAX_VALUE;
246         float maxx = -Float.MAX_VALUE, maxy = -Float.MAX_VALUE, maxz = -Float.MAX_VALUE;
247 
248         for (int i = 0; i < triW.mVert.length; i += 3) {
249             maxx = Math.max(triW.mVert[i], maxx);
250             minx = Math.min(triW.mVert[i], minx);
251             maxy = Math.max(triW.mVert[i + 1], maxy);
252             miny = Math.min(triW.mVert[i + 1], miny);
253             maxz = Math.max(triW.mVert[i + 2], maxz);
254             minz = Math.min(triW.mVert[i + 2], minz);
255         }
256         mLookPoint = new double[]{(maxx + minx) / 2, (maxy + miny) / 2, (maxz + minz) / 2};
257 
258         mScreenWidth = 2 * Math.max((maxx - minx), Math.max((maxy - miny), (maxz - minz)));
259     }
260 
look(char dir, TriData tri, float[] voxelDim, int w, int h)261     public void look(char dir, TriData tri, float[] voxelDim, int w, int h) {
262         calcLook(tri, w, h);
263         int dx = ((dir >> 4) & 0xF);
264         int dy = ((dir >> 8) & 0xF);
265         int dz = ((dir >> 0) & 0xF);
266         if (dx > 1) {
267             dx = -1;
268         }
269         if (dy > 1) {
270             dy = -1;
271         }
272         if (dz > 1) {
273             dz = -1;
274         }
275         mEyePoint = new double[]{mLookPoint[0] + 2 * mScreenWidth * dx,
276                 mLookPoint[1] + 2 * mScreenWidth * dy,
277                 mLookPoint[2] + 2 * mScreenWidth * dz};
278         double[] zv = new double[]{-dx, -dy, -dz};
279         double[] rv = new double[]{(dx == 0) ? 1 : 0, (dx == 0) ? 0 : 1, 0};
280         double[] up = new double[3];
281         VectorUtil.norm(zv);
282         VectorUtil.norm(rv);
283 
284         VectorUtil.cross(zv, rv, up);
285         VectorUtil.cross(zv, up, rv);
286         VectorUtil.cross(zv, rv, up);
287         mUpVector = up;
288         mScreenDim = new int[]{w, h};
289         calcMatrix();
290     }
291 
lookAt(TriData tri, float[] voxelDim, int w, int h)292     public void lookAt(TriData tri, float[] voxelDim, int w, int h) {
293         calcLook(tri, voxelDim, w, h);
294 
295         mEyePoint = new double[]{mLookPoint[0] + mScreenWidth,
296                 mLookPoint[1] + mScreenWidth,
297                 mLookPoint[2] + mScreenWidth};
298         double[] zv = new double[]{-1, -1, -1};
299         double[] rv = new double[]{1, 1, 0};
300         double[] up = new double[3];
301         VectorUtil.norm(zv);
302         VectorUtil.norm(rv);
303 
304         VectorUtil.cross(zv, rv, up);
305         VectorUtil.cross(zv, up, rv);
306         VectorUtil.cross(zv, rv, up);
307         mUpVector = up;
308         mScreenDim = new int[]{w, h};
309         calcMatrix();
310     }
311 
trackBallUP(float x, float y)312     public void trackBallUP(float x, float y) {
313 
314     }
315 
trackBallDown(float x, float y)316     public void trackBallDown(float x, float y) {
317 
318         ballToVec(x, y, mStartV);
319         mStartEyePoint = Arrays.copyOf(mEyePoint, m.length);
320         mStartUpVector = Arrays.copyOf(mUpVector, m.length);
321         mStartMatrix = new Matrix(this);
322         mStartMatrix.makeRotation();
323     }
324 
trackBallMove(float x, float y)325     public void trackBallMove(float x, float y) {
326         ballToVec(x, y, mMoveToV);
327 
328         double angle = Quaternion.calcAngle(mStartV, mMoveToV);
329         if (angle < 0.0001) {
330             calcMatrix();
331             return;
332         }
333         double[] axis = Quaternion.calcAxis(mStartV, mMoveToV);
334 
335 
336         axis = mStartMatrix.vecmult(axis);
337 
338         mQ.set(angle, axis);
339 
340         VectorUtil.sub(mLookPoint, mStartEyePoint, mEyePoint);
341 
342         mEyePoint = mQ.rotateVec(mEyePoint);
343         mUpVector = mQ.rotateVec(mStartUpVector);
344 
345         VectorUtil.sub(mLookPoint, mEyePoint, mEyePoint);
346         calcMatrix();
347     }
348 
ballToVec(float x, float y, double[] v)349     void ballToVec(float x, float y, double[] v) {
350         float ballRadius = Math.min(mScreenDim[0], mScreenDim[1]) * .4f;
351         double cx = mScreenDim[0] / 2.;
352         double cy = mScreenDim[1] / 2.;
353 
354         double dx = (cx - x) / ballRadius;
355         double dy = (cy - y) / ballRadius;
356         double scale = dx * dx + dy * dy;
357         if (scale > 1) {
358             scale = Math.sqrt(scale);
359             dx = dx / scale;
360             dy = dy / scale;
361         }
362 
363         double dz = Math.sqrt(Math.abs(1 - (dx * dx + dy * dy)));
364         v[0] = dx;
365         v[1] = dy;
366         v[2] = dz;
367         VectorUtil.normalize(v);
368     }
369 }
370