diff options
Diffstat (limited to 'java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java')
-rw-r--r-- | java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java new file mode 100644 index 00000000..b4ce67a1 --- /dev/null +++ b/java/tests/VrDemo/src/com/example/android/rs/vr/VrView.java @@ -0,0 +1,478 @@ +/* + * 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; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.SurfaceTexture; +import android.os.AsyncTask; +import android.renderscript.RenderScript; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.Surface; +import android.view.TextureView; + +import com.example.android.rs.vr.engine.Cube; +import com.example.android.rs.vr.engine.Pipeline; +import com.example.android.rs.vr.engine.RsBrickedBitMask; +import com.example.android.rs.vr.engine.TriData; +import com.example.android.rs.vr.engine.VectorUtil; +import com.example.android.rs.vr.engine.ViewMatrix; +import com.example.android.rs.vr.engine.Volume; +import com.example.android.rs.vr.engine.VrPipline1; +import com.example.android.rs.vr.engine.VrState; + +import java.util.Arrays; + +/** + * VrView runs a volume rendering on the screen + */ +public class VrView extends TextureView { + private static final String LOGTAG = "rsexample.google.com.vrdemo"; + private Pipeline mPipline = new VrPipline1();//BasicPipline(); + // private VrState mState4 = new VrState(); // for down sampled + private VrState mState1 = new VrState(); // for full res version + private VrState mStateLow = new VrState(); // for full res version + private VrState mLastDrawn = new VrState(); // for full res version + private Paint paint = new Paint(); + private SurfaceTexture mSurfaceTexture; + private Surface mSurface; + ///private Size mImageViewSize; + private int refresh = 0; // 0 is no refresh else refresh = downsample + int mPreviousMode = -1; + int last_look = 0; + + // int mDownSample = 4; + private final char[] looks = { + ViewMatrix.UP_AT, + ViewMatrix.DOWN_AT, + ViewMatrix.RIGHT_AT, + ViewMatrix.LEFT_AT, + ViewMatrix.FORWARD_AT, + ViewMatrix.BEHIND_AT}; + private byte mMode = ROTATE_MODE; + private ScaleGestureDetector mScaleDetector; + private boolean mInScale; + + public static final byte ROTATE_MODE = 1; + public static final byte CUT_X_MODE = 2; + public static final byte CUT_Y_MODE = 3; + public static final byte CUT_Z_MODE = 4; + + public void setMode(byte mode) { + mMode = mode; + } + + private float mDownPointX; + private float mDownPointY; + private double mDownScreenWidth; + private double[] mDownLookPoint = new double[3]; + private double[] mDownEyePoint = new double[3]; + private double[] mDownUpVector = new double[3]; + private double[] mDownRightVector = new double[3]; + private float[] mCurrentTrim = new float[6]; + VrRenderTesk mRenderTesk; + VrBinGridTask mBinGridTask; + + public VrView(Context context) { + super(context); + setup(context); + paint.setFilterBitmap(true); + } + + public VrView(Context context, AttributeSet attrs) { + super(context, attrs); + setup(context); + } + + + public VrView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(context); + } + + + public VrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + setup(context); + } + + private void setup(Context context) { + setBackgroundColor(Color.BLACK); + if (isInEditMode()) { + return; + } + setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + mSurfaceTexture = surface; + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + mSurfaceTexture = surface; + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + } + }); + + mScaleDetector = new ScaleGestureDetector(context, + new ScaleGestureDetector.OnScaleGestureListener() { + @Override + public boolean onScale(ScaleGestureDetector detector) { + double width = mState1.mTransform.getScreenWidth() / detector.getScaleFactor(); + mState1.mTransform.setScreenWidth(width); + panMove(detector.getFocusX(), detector.getFocusY()); + render(4); + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + panDown(detector.getFocusX(), detector.getFocusY()); + mInScale = true; + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + mInScale = false; + } + }); + } + + private void updateOutputDimensions(int width, int height) { + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (mPreviousMode == 1) { + mPipline.cancel(); + } + boolean handled = mScaleDetector.onTouchEvent(e); + if (e.getPointerCount() > 1) { + return true; + } + if (mInScale) { + return true; + } + int action = e.getAction(); + if (action == MotionEvent.ACTION_DOWN) { + actionDown(e); + } else if (action == MotionEvent.ACTION_MOVE) { + actionMove(e); + render(4); + } else if (action == MotionEvent.ACTION_UP) { + actionUp(e); + refresh = 1; + render(1); + } + return true; + } + + private void panMove(float x, float y) { + double dist_x = (mDownPointX - x) * mDownScreenWidth / getWidth(); + double dist_y = (y - mDownPointY) * mDownScreenWidth / getWidth(); + double[] p; + p = mState1.mTransform.getEyePoint(); + p[0] = mDownEyePoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; + p[1] = mDownEyePoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; + p[2] = mDownEyePoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; + mState1.mTransform.setEyePoint(p); + p = mState1.mTransform.getLookPoint(); + p[0] = mDownLookPoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; + p[1] = mDownLookPoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; + p[2] = mDownLookPoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; + mState1.mTransform.setLookPoint(p); + } + + private void panDown(float x, float y) { + mDownPointX = x; + mDownPointY = y; + mDownScreenWidth = mState1.mTransform.getScreenWidth(); + double[] p; + p = mState1.mTransform.getLookPoint(); + System.arraycopy(p, 0, mDownLookPoint, 0, 3); + p = mState1.mTransform.getEyePoint(); + System.arraycopy(p, 0, mDownEyePoint, 0, 3); + p = mState1.mTransform.getUpVector(); + System.arraycopy(p, 0, mDownUpVector, 0, 3); + mDownRightVector[0] = mDownLookPoint[0] - mDownEyePoint[0]; + mDownRightVector[1] = mDownLookPoint[1] - mDownEyePoint[1]; + mDownRightVector[2] = mDownLookPoint[2] - mDownEyePoint[2]; + VectorUtil.normalize(mDownRightVector); + VectorUtil.cross(mDownRightVector, mDownUpVector, mDownRightVector); + } + + private void actionDown(MotionEvent e) { + panDown(e.getX(), e.getY()); + + switch (mMode) { + case ROTATE_MODE: + mState1.mTransform.trackBallDown(e.getX(), e.getY()); + break; + + case CUT_X_MODE: + case CUT_Y_MODE: + case CUT_Z_MODE: + float[] trim = mState1.mCubeVolume.getTrim(); + System.arraycopy(trim, 0, mCurrentTrim, 0, 6); + break; + } + } + + private void actionMove(MotionEvent e) { + float deltax, deltay; + + switch (mMode) { + case ROTATE_MODE: + + mState1.mTransform.trackBallMove(e.getX(), e.getY()); + + break; + + case CUT_X_MODE: + deltax = (float) ((mDownPointX - e.getX()) / getWidth()); + deltay = (float) -((mDownPointY - e.getY()) / getWidth()); + cut(0, deltax, deltay); + break; + case CUT_Y_MODE: + deltax = (float) ((mDownPointX - e.getX()) / getWidth()); + deltay = (float) -((mDownPointY - e.getY()) / getWidth()); + cut(1, deltax, deltay); + break; + case CUT_Z_MODE: + deltax = (float) ((mDownPointX - e.getX()) / getWidth()); + deltay = (float) -((mDownPointY - e.getY()) / getWidth()); + cut(2, deltax, deltay); + break; + + } + } + + private void actionUp(MotionEvent e) { + } + + public void cut(int side, float fractionx, float fractiony) { + float[] f = Arrays.copyOf(mCurrentTrim, mCurrentTrim.length); + f[side] += fractionx; + if (f[side] < 0) f[side] = 0; + if (f[side] > .8) f[side] = .8f; + f[side + 3] += fractiony; + if (f[side + 3] < 0) f[side + 3] = 0; + if (f[side + 3] > .8) f[side + 3] = .8f; + mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, f); + mState1.mCubeScreen = new TriData(mState1.mCubeVolume); + mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); + } + + public void resetCut() { + Arrays.fill(mCurrentTrim, 0); + mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, mCurrentTrim); + mState1.mCubeScreen = new TriData(mState1.mCubeVolume); + mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); + mState1.mTransform.look(looks[last_look], mState1.mCubeScreen, getWidth(), getHeight()); + mState1.mTransform.setScreenWidth(.6f * mState1.mTransform.getScreenWidth()); + last_look = (last_look + 1) % looks.length; + render(4); + } + + public void setVolume(RenderScript rs, Volume v) { + mState1.mRs = rs; + mState1.mVolume = v; + mState1.mCubeVolume = new Cube(mState1.mVolume, 5f); + mState1.mCubeScreen = new TriData(mState1.mCubeVolume); + mState1.mCubeScreen.scale(v.mVoxelDim); + mState1.mTransform.setVoxelDim(v.mVoxelDim); + mState1.mTransform.look(ViewMatrix.DOWN_AT, mState1.mCubeScreen, getWidth(), getHeight()); + setLook(mState1.mVolume.getLookNames()[0]); + } + + protected void look(int k) { + mState1.mTransform.look(looks[k], mState1.mCubeVolume, getWidth(), getHeight()); + render(4); + render(1); + } + + void render(int downSample) { + + if (mRenderTesk == null) { + mRenderTesk = new VrRenderTesk(); + refresh = 0; + mRenderTesk.execute(downSample); + } else { + refresh = downSample; + } + } + + public String[] getLooks() { + return mState1.mVolume.getLookNames(); + } + + public void setLook(String look) { + int[][] color = mState1.mVolume.getLookColor(look); + int[][] opacity = mState1.mVolume.getLookOpactiy(look); + mState1.mMaterial.setup(opacity, color); + if (mBinGridTask == null) { + mBinGridTask = new VrBinGridTask(); + mBinGridTask.execute(mState1.mVolume); + } + } + + class VrRenderTesk extends AsyncTask<Integer, String, Long> { + + long m_last_time; + + @Override + protected void onPreExecute() { + mStateLow.copyData(mState1); + } + + @Override + protected void onCancelled() { + mPipline.cancel(); + } + + @Override + protected Long doInBackground(Integer... down) { + if (mState1.mRs == null) return 0L; + if (mSurfaceTexture == null) return 0L; + int sample = 4; + VrState state = mStateLow; + if (down[0] == 1) { + if (mPreviousMode == 4) { + mState1.copyData(mLastDrawn); + } else { + mState1.copyData(mStateLow); + } + // mStateLow.mScrAllocation.setSurface(null); + state = mState1; + sample = 1; + if (mStateLow.mScrAllocation != null) { + mStateLow.mScrAllocation.setSurface(null); + } + } else { + if (mState1.mScrAllocation != null) { + mState1.mScrAllocation.setSurface(null); + } + } + + if (mPreviousMode != sample) { + if (mSurface != null) { + mSurface.release(); + } + mSurface = new Surface(mSurfaceTexture); + } + mPreviousMode = sample; + + int img_width = getWidth() / sample; + int img_height = getHeight() / sample; + state.createOutputAllocation(mSurface, img_width, img_height); + + mPipline.initBuffers(state); + + if (mPipline.isCancel()) { + return 0L; + } + long start = System.nanoTime(); + addTimeLine(null); + mPipline.setupTriangles(state); + + if (mPipline.isCancel()) { + return 0L; + } + mPipline.rasterizeTriangles(state); + + if (mPipline.isCancel()) { + return 0L; + } + mPipline.raycast(state); + + if (mPipline.isCancel()) { + return 0L; + } + mLastDrawn.copyData(state); + state.mRs.finish(); + state.mScrAllocation.ioSend(); + + long time = System.nanoTime(); + addLine("vr(" + img_width + "," + img_height + "): " + (time - start) / 1E6f + " ms"); + return 0L; + } + + private void addTimeLine(String line) { + if (line == null) { + m_last_time = System.nanoTime(); + return; + } + long time = System.nanoTime(); + float ftime = (time - m_last_time) / 1E6f; + if (ftime > 100) + addLine(line + ": " + (ftime / 1E3f) + " sec"); + else + addLine(line + ": " + (ftime) + " ms"); + m_last_time = System.nanoTime(); + } + + private void addLine(String line) { + publishProgress(line); + } + + protected void onProgressUpdate(String... progress) { + Log.v(LOGTAG, progress[0]); + } + + protected void onPostExecute(Long result) { + invalidate(); + mRenderTesk = null; + if (refresh != 0) { + render(refresh); + } + } + } + + class VrBinGridTask extends AsyncTask<Volume, String, Long> { + + @Override + protected Long doInBackground(Volume... v) { + mState1.mRsMask = new RsBrickedBitMask(mState1); + mState1.mRs.finish(); + return 0L; + } + + protected void onProgressUpdate(String... progress) { + Log.v(LOGTAG, progress[0]); + } + + protected void onPostExecute(Long result) { + mBinGridTask = null; + render(4); + render(1); + } + } +} |