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; 18 19 import android.content.Context; 20 import android.graphics.Color; 21 import android.graphics.Paint; 22 import android.graphics.SurfaceTexture; 23 import android.os.AsyncTask; 24 import android.renderscript.RenderScript; 25 import android.util.AttributeSet; 26 import android.util.Log; 27 import android.view.MotionEvent; 28 import android.view.ScaleGestureDetector; 29 import android.view.Surface; 30 import android.view.TextureView; 31 32 import com.example.android.rs.vr.engine.Cube; 33 import com.example.android.rs.vr.engine.Pipeline; 34 import com.example.android.rs.vr.engine.RsBrickedBitMask; 35 import com.example.android.rs.vr.engine.TriData; 36 import com.example.android.rs.vr.engine.VectorUtil; 37 import com.example.android.rs.vr.engine.ViewMatrix; 38 import com.example.android.rs.vr.engine.Volume; 39 import com.example.android.rs.vr.engine.VrPipline1; 40 import com.example.android.rs.vr.engine.VrState; 41 42 import java.util.Arrays; 43 44 /** 45 * VrView runs a volume rendering on the screen 46 */ 47 public class VrView extends TextureView { 48 private static final String LOGTAG = "rsexample.google.com.vrdemo"; 49 private Pipeline mPipline = new VrPipline1();//BasicPipline(); 50 // private VrState mState4 = new VrState(); // for down sampled 51 private VrState mState1 = new VrState(); // for full res version 52 private VrState mStateLow = new VrState(); // for full res version 53 private VrState mLastDrawn = new VrState(); // for full res version 54 private Paint paint = new Paint(); 55 private SurfaceTexture mSurfaceTexture; 56 private Surface mSurface; 57 ///private Size mImageViewSize; 58 private int refresh = 0; // 0 is no refresh else refresh = downsample 59 int mPreviousMode = -1; 60 int last_look = 0; 61 62 // int mDownSample = 4; 63 private final char[] looks = { 64 ViewMatrix.UP_AT, 65 ViewMatrix.DOWN_AT, 66 ViewMatrix.RIGHT_AT, 67 ViewMatrix.LEFT_AT, 68 ViewMatrix.FORWARD_AT, 69 ViewMatrix.BEHIND_AT}; 70 private byte mMode = ROTATE_MODE; 71 private ScaleGestureDetector mScaleDetector; 72 private boolean mInScale; 73 74 public static final byte ROTATE_MODE = 1; 75 public static final byte CUT_X_MODE = 2; 76 public static final byte CUT_Y_MODE = 3; 77 public static final byte CUT_Z_MODE = 4; 78 setMode(byte mode)79 public void setMode(byte mode) { 80 mMode = mode; 81 } 82 83 private float mDownPointX; 84 private float mDownPointY; 85 private double mDownScreenWidth; 86 private double[] mDownLookPoint = new double[3]; 87 private double[] mDownEyePoint = new double[3]; 88 private double[] mDownUpVector = new double[3]; 89 private double[] mDownRightVector = new double[3]; 90 private float[] mCurrentTrim = new float[6]; 91 VrRenderTesk mRenderTesk; 92 VrBinGridTask mBinGridTask; 93 VrView(Context context)94 public VrView(Context context) { 95 super(context); 96 setup(context); 97 paint.setFilterBitmap(true); 98 } 99 VrView(Context context, AttributeSet attrs)100 public VrView(Context context, AttributeSet attrs) { 101 super(context, attrs); 102 setup(context); 103 } 104 105 VrView(Context context, AttributeSet attrs, int defStyleAttr)106 public VrView(Context context, AttributeSet attrs, int defStyleAttr) { 107 super(context, attrs, defStyleAttr); 108 setup(context); 109 } 110 111 VrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)112 public VrView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 113 super(context, attrs, defStyleAttr, defStyleRes); 114 setup(context); 115 } 116 setup(Context context)117 private void setup(Context context) { 118 if (isInEditMode()) { 119 return; 120 } 121 setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { 122 123 @Override 124 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 125 mSurfaceTexture = surface; 126 } 127 128 @Override 129 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 130 mSurfaceTexture = surface; 131 } 132 133 @Override 134 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 135 return false; 136 } 137 138 @Override 139 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 140 } 141 }); 142 143 mScaleDetector = new ScaleGestureDetector(context, 144 new ScaleGestureDetector.OnScaleGestureListener() { 145 @Override 146 public boolean onScale(ScaleGestureDetector detector) { 147 double width = mState1.mTransform.getScreenWidth() / detector.getScaleFactor(); 148 mState1.mTransform.setScreenWidth(width); 149 panMove(detector.getFocusX(), detector.getFocusY()); 150 render(4); 151 return true; 152 } 153 154 @Override 155 public boolean onScaleBegin(ScaleGestureDetector detector) { 156 panDown(detector.getFocusX(), detector.getFocusY()); 157 mInScale = true; 158 return true; 159 } 160 161 @Override 162 public void onScaleEnd(ScaleGestureDetector detector) { 163 mInScale = false; 164 } 165 }); 166 } 167 updateOutputDimensions(int width, int height)168 private void updateOutputDimensions(int width, int height) { 169 } 170 171 @Override onTouchEvent(MotionEvent e)172 public boolean onTouchEvent(MotionEvent e) { 173 if (mPreviousMode == 1) { 174 mPipline.cancel(); 175 } 176 boolean handled = mScaleDetector.onTouchEvent(e); 177 if (e.getPointerCount() > 1) { 178 return true; 179 } 180 if (mInScale) { 181 return true; 182 } 183 int action = e.getAction(); 184 if (action == MotionEvent.ACTION_DOWN) { 185 actionDown(e); 186 } else if (action == MotionEvent.ACTION_MOVE) { 187 actionMove(e); 188 render(4); 189 } else if (action == MotionEvent.ACTION_UP) { 190 actionUp(e); 191 refresh = 1; 192 render(1); 193 } 194 return true; 195 } 196 panMove(float x, float y)197 private void panMove(float x, float y) { 198 double dist_x = (mDownPointX - x) * mDownScreenWidth / getWidth(); 199 double dist_y = (y - mDownPointY) * mDownScreenWidth / getWidth(); 200 double[] p; 201 p = mState1.mTransform.getEyePoint(); 202 p[0] = mDownEyePoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; 203 p[1] = mDownEyePoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; 204 p[2] = mDownEyePoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; 205 mState1.mTransform.setEyePoint(p); 206 p = mState1.mTransform.getLookPoint(); 207 p[0] = mDownLookPoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; 208 p[1] = mDownLookPoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; 209 p[2] = mDownLookPoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; 210 mState1.mTransform.setLookPoint(p); 211 } 212 panDown(float x, float y)213 private void panDown(float x, float y) { 214 mDownPointX = x; 215 mDownPointY = y; 216 mDownScreenWidth = mState1.mTransform.getScreenWidth(); 217 double[] p; 218 p = mState1.mTransform.getLookPoint(); 219 System.arraycopy(p, 0, mDownLookPoint, 0, 3); 220 p = mState1.mTransform.getEyePoint(); 221 System.arraycopy(p, 0, mDownEyePoint, 0, 3); 222 p = mState1.mTransform.getUpVector(); 223 System.arraycopy(p, 0, mDownUpVector, 0, 3); 224 mDownRightVector[0] = mDownLookPoint[0] - mDownEyePoint[0]; 225 mDownRightVector[1] = mDownLookPoint[1] - mDownEyePoint[1]; 226 mDownRightVector[2] = mDownLookPoint[2] - mDownEyePoint[2]; 227 VectorUtil.normalize(mDownRightVector); 228 VectorUtil.cross(mDownRightVector, mDownUpVector, mDownRightVector); 229 } 230 actionDown(MotionEvent e)231 private void actionDown(MotionEvent e) { 232 panDown(e.getX(), e.getY()); 233 234 switch (mMode) { 235 case ROTATE_MODE: 236 mState1.mTransform.trackBallDown(e.getX(), e.getY()); 237 break; 238 239 case CUT_X_MODE: 240 case CUT_Y_MODE: 241 case CUT_Z_MODE: 242 float[] trim = mState1.mCubeVolume.getTrim(); 243 System.arraycopy(trim, 0, mCurrentTrim, 0, 6); 244 break; 245 } 246 } 247 actionMove(MotionEvent e)248 private void actionMove(MotionEvent e) { 249 float deltax, deltay; 250 251 switch (mMode) { 252 case ROTATE_MODE: 253 254 mState1.mTransform.trackBallMove(e.getX(), e.getY()); 255 256 break; 257 258 case CUT_X_MODE: 259 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 260 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 261 cut(0, deltax, deltay); 262 break; 263 case CUT_Y_MODE: 264 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 265 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 266 cut(1, deltax, deltay); 267 break; 268 case CUT_Z_MODE: 269 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 270 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 271 cut(2, deltax, deltay); 272 break; 273 274 } 275 } 276 actionUp(MotionEvent e)277 private void actionUp(MotionEvent e) { 278 } 279 cut(int side, float fractionx, float fractiony)280 public void cut(int side, float fractionx, float fractiony) { 281 float[] f = Arrays.copyOf(mCurrentTrim, mCurrentTrim.length); 282 f[side] += fractionx; 283 if (f[side] < 0) f[side] = 0; 284 if (f[side] > .8) f[side] = .8f; 285 f[side + 3] += fractiony; 286 if (f[side + 3] < 0) f[side + 3] = 0; 287 if (f[side + 3] > .8) f[side + 3] = .8f; 288 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, f); 289 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 290 mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); 291 } 292 resetCut()293 public void resetCut() { 294 Arrays.fill(mCurrentTrim, 0); 295 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, mCurrentTrim); 296 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 297 mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); 298 mState1.mTransform.look(looks[last_look], mState1.mCubeScreen, getWidth(), getHeight()); 299 mState1.mTransform.setScreenWidth(.6f * mState1.mTransform.getScreenWidth()); 300 last_look = (last_look + 1) % looks.length; 301 render(4); 302 } 303 setVolume(RenderScript rs, Volume v)304 public void setVolume(RenderScript rs, Volume v) { 305 mState1.mRs = rs; 306 mState1.mVolume = v; 307 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f); 308 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 309 mState1.mCubeScreen.scale(v.mVoxelDim); 310 mState1.mTransform.setVoxelDim(v.mVoxelDim); 311 mState1.mTransform.look(ViewMatrix.DOWN_AT, mState1.mCubeScreen, getWidth(), getHeight()); 312 setLook(mState1.mVolume.getLookNames()[0]); 313 } 314 look(int k)315 protected void look(int k) { 316 mState1.mTransform.look(looks[k], mState1.mCubeVolume, getWidth(), getHeight()); 317 render(4); 318 render(1); 319 } 320 render(int downSample)321 void render(int downSample) { 322 323 if (mRenderTesk == null) { 324 mRenderTesk = new VrRenderTesk(); 325 refresh = 0; 326 mRenderTesk.execute(downSample); 327 } else { 328 refresh = downSample; 329 } 330 } 331 getLooks()332 public String[] getLooks() { 333 return mState1.mVolume.getLookNames(); 334 } 335 setLook(String look)336 public void setLook(String look) { 337 int[][] color = mState1.mVolume.getLookColor(look); 338 int[][] opacity = mState1.mVolume.getLookOpactiy(look); 339 mState1.mMaterial.setup(opacity, color); 340 if (mBinGridTask == null) { 341 mBinGridTask = new VrBinGridTask(); 342 mBinGridTask.execute(mState1.mVolume); 343 } 344 } 345 346 class VrRenderTesk extends AsyncTask<Integer, String, Long> { 347 348 long m_last_time; 349 350 @Override onPreExecute()351 protected void onPreExecute() { 352 mStateLow.copyData(mState1); 353 } 354 355 @Override onCancelled()356 protected void onCancelled() { 357 mPipline.cancel(); 358 } 359 360 @Override doInBackground(Integer... down)361 protected Long doInBackground(Integer... down) { 362 if (mState1.mRs == null) return 0L; 363 if (mSurfaceTexture == null) return 0L; 364 int sample = 4; 365 VrState state = mStateLow; 366 if (down[0] == 1) { 367 if (mPreviousMode == 4) { 368 mState1.copyData(mLastDrawn); 369 } else { 370 mState1.copyData(mStateLow); 371 } 372 // mStateLow.mScrAllocation.setSurface(null); 373 state = mState1; 374 sample = 1; 375 if (mStateLow.mScrAllocation != null) { 376 mStateLow.mScrAllocation.setSurface(null); 377 } 378 } else { 379 if (mState1.mScrAllocation != null) { 380 mState1.mScrAllocation.setSurface(null); 381 } 382 } 383 384 if (mPreviousMode != sample) { 385 if (mSurface != null) { 386 mSurface.release(); 387 } 388 mSurface = new Surface(mSurfaceTexture); 389 } 390 mPreviousMode = sample; 391 392 int img_width = getWidth() / sample; 393 int img_height = getHeight() / sample; 394 state.createOutputAllocation(mSurface, img_width, img_height); 395 396 mPipline.initBuffers(state); 397 398 if (mPipline.isCancel()) { 399 return 0L; 400 } 401 long start = System.nanoTime(); 402 addTimeLine(null); 403 mPipline.setupTriangles(state); 404 405 if (mPipline.isCancel()) { 406 return 0L; 407 } 408 mPipline.rasterizeTriangles(state); 409 410 if (mPipline.isCancel()) { 411 return 0L; 412 } 413 mPipline.raycast(state); 414 415 if (mPipline.isCancel()) { 416 return 0L; 417 } 418 mLastDrawn.copyData(state); 419 state.mRs.finish(); 420 state.mScrAllocation.ioSend(); 421 422 long time = System.nanoTime(); 423 addLine("vr(" + img_width + "," + img_height + "): " + (time - start) / 1E6f + " ms"); 424 return 0L; 425 } 426 addTimeLine(String line)427 private void addTimeLine(String line) { 428 if (line == null) { 429 m_last_time = System.nanoTime(); 430 return; 431 } 432 long time = System.nanoTime(); 433 float ftime = (time - m_last_time) / 1E6f; 434 if (ftime > 100) 435 addLine(line + ": " + (ftime / 1E3f) + " sec"); 436 else 437 addLine(line + ": " + (ftime) + " ms"); 438 m_last_time = System.nanoTime(); 439 } 440 addLine(String line)441 private void addLine(String line) { 442 publishProgress(line); 443 } 444 onProgressUpdate(String... progress)445 protected void onProgressUpdate(String... progress) { 446 Log.v(LOGTAG, progress[0]); 447 } 448 onPostExecute(Long result)449 protected void onPostExecute(Long result) { 450 invalidate(); 451 mRenderTesk = null; 452 if (refresh != 0) { 453 render(refresh); 454 } 455 } 456 } 457 458 class VrBinGridTask extends AsyncTask<Volume, String, Long> { 459 460 @Override doInBackground(Volume... v)461 protected Long doInBackground(Volume... v) { 462 mState1.mRsMask = new RsBrickedBitMask(mState1); 463 mState1.mRs.finish(); 464 return 0L; 465 } 466 onProgressUpdate(String... progress)467 protected void onProgressUpdate(String... progress) { 468 Log.v(LOGTAG, progress[0]); 469 } 470 onPostExecute(Long result)471 protected void onPostExecute(Long result) { 472 mBinGridTask = null; 473 render(4); 474 render(1); 475 } 476 } 477 } 478