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
17package com.example.android.rs.vr.engine;
18
19import android.util.Log;
20
21import java.text.DecimalFormat;
22import java.util.Arrays;
23
24public 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
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
93    private static String toStr(double d) {
94        String s = "         " + df.format(d);
95        return s.substring(s.length() - 9);
96    }
97
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
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
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
125    public void setScreenDim(int x, int y) {
126        mScreenDim = new int[]{x, y};
127    }
128
129    public double[] getLookPoint() {
130        return mLookPoint;
131    }
132
133    public void setLookPoint(double[] mLookPoint) {
134        this.mLookPoint = mLookPoint;
135    }
136
137    public double[] getEyePoint() {
138        return mEyePoint;
139    }
140
141    public void setEyePoint(double[] mEyePoint) {
142        this.mEyePoint = mEyePoint;
143    }
144
145    public double[] getUpVector() {
146        return mUpVector;
147    }
148
149    public void setUpVector(double[] mUpVector) {
150        this.mUpVector = mUpVector;
151    }
152
153    public double getScreenWidth() {
154        return mScreenWidth;
155    }
156
157    public void setScreenWidth(double screenWidth) {
158        this.mScreenWidth = screenWidth;
159    }
160
161    public void makeUnit() {
162
163    }
164
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
207    static void calcRight(double[] a, double[] b, double[] out) {
208        VectorUtil.cross(a, b, out);
209    }
210
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
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
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
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
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
312    public void trackBallUP(float x, float y) {
313
314    }
315
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
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
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