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 setBackgroundColor(Color.BLACK); 119 if (isInEditMode()) { 120 return; 121 } 122 setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { 123 124 @Override 125 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 126 mSurfaceTexture = surface; 127 } 128 129 @Override 130 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 131 mSurfaceTexture = surface; 132 } 133 134 @Override 135 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 136 return false; 137 } 138 139 @Override 140 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 141 } 142 }); 143 144 mScaleDetector = new ScaleGestureDetector(context, 145 new ScaleGestureDetector.OnScaleGestureListener() { 146 @Override 147 public boolean onScale(ScaleGestureDetector detector) { 148 double width = mState1.mTransform.getScreenWidth() / detector.getScaleFactor(); 149 mState1.mTransform.setScreenWidth(width); 150 panMove(detector.getFocusX(), detector.getFocusY()); 151 render(4); 152 return true; 153 } 154 155 @Override 156 public boolean onScaleBegin(ScaleGestureDetector detector) { 157 panDown(detector.getFocusX(), detector.getFocusY()); 158 mInScale = true; 159 return true; 160 } 161 162 @Override 163 public void onScaleEnd(ScaleGestureDetector detector) { 164 mInScale = false; 165 } 166 }); 167 } 168 updateOutputDimensions(int width, int height)169 private void updateOutputDimensions(int width, int height) { 170 } 171 172 @Override onTouchEvent(MotionEvent e)173 public boolean onTouchEvent(MotionEvent e) { 174 if (mPreviousMode == 1) { 175 mPipline.cancel(); 176 } 177 boolean handled = mScaleDetector.onTouchEvent(e); 178 if (e.getPointerCount() > 1) { 179 return true; 180 } 181 if (mInScale) { 182 return true; 183 } 184 int action = e.getAction(); 185 if (action == MotionEvent.ACTION_DOWN) { 186 actionDown(e); 187 } else if (action == MotionEvent.ACTION_MOVE) { 188 actionMove(e); 189 render(4); 190 } else if (action == MotionEvent.ACTION_UP) { 191 actionUp(e); 192 refresh = 1; 193 render(1); 194 } 195 return true; 196 } 197 panMove(float x, float y)198 private void panMove(float x, float y) { 199 double dist_x = (mDownPointX - x) * mDownScreenWidth / getWidth(); 200 double dist_y = (y - mDownPointY) * mDownScreenWidth / getWidth(); 201 double[] p; 202 p = mState1.mTransform.getEyePoint(); 203 p[0] = mDownEyePoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; 204 p[1] = mDownEyePoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; 205 p[2] = mDownEyePoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; 206 mState1.mTransform.setEyePoint(p); 207 p = mState1.mTransform.getLookPoint(); 208 p[0] = mDownLookPoint[0] + dist_x * mDownRightVector[0] + dist_y * mDownUpVector[0]; 209 p[1] = mDownLookPoint[1] + dist_x * mDownRightVector[1] + dist_y * mDownUpVector[1]; 210 p[2] = mDownLookPoint[2] + dist_x * mDownRightVector[2] + dist_y * mDownUpVector[2]; 211 mState1.mTransform.setLookPoint(p); 212 } 213 panDown(float x, float y)214 private void panDown(float x, float y) { 215 mDownPointX = x; 216 mDownPointY = y; 217 mDownScreenWidth = mState1.mTransform.getScreenWidth(); 218 double[] p; 219 p = mState1.mTransform.getLookPoint(); 220 System.arraycopy(p, 0, mDownLookPoint, 0, 3); 221 p = mState1.mTransform.getEyePoint(); 222 System.arraycopy(p, 0, mDownEyePoint, 0, 3); 223 p = mState1.mTransform.getUpVector(); 224 System.arraycopy(p, 0, mDownUpVector, 0, 3); 225 mDownRightVector[0] = mDownLookPoint[0] - mDownEyePoint[0]; 226 mDownRightVector[1] = mDownLookPoint[1] - mDownEyePoint[1]; 227 mDownRightVector[2] = mDownLookPoint[2] - mDownEyePoint[2]; 228 VectorUtil.normalize(mDownRightVector); 229 VectorUtil.cross(mDownRightVector, mDownUpVector, mDownRightVector); 230 } 231 actionDown(MotionEvent e)232 private void actionDown(MotionEvent e) { 233 panDown(e.getX(), e.getY()); 234 235 switch (mMode) { 236 case ROTATE_MODE: 237 mState1.mTransform.trackBallDown(e.getX(), e.getY()); 238 break; 239 240 case CUT_X_MODE: 241 case CUT_Y_MODE: 242 case CUT_Z_MODE: 243 float[] trim = mState1.mCubeVolume.getTrim(); 244 System.arraycopy(trim, 0, mCurrentTrim, 0, 6); 245 break; 246 } 247 } 248 actionMove(MotionEvent e)249 private void actionMove(MotionEvent e) { 250 float deltax, deltay; 251 252 switch (mMode) { 253 case ROTATE_MODE: 254 255 mState1.mTransform.trackBallMove(e.getX(), e.getY()); 256 257 break; 258 259 case CUT_X_MODE: 260 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 261 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 262 cut(0, deltax, deltay); 263 break; 264 case CUT_Y_MODE: 265 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 266 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 267 cut(1, deltax, deltay); 268 break; 269 case CUT_Z_MODE: 270 deltax = (float) ((mDownPointX - e.getX()) / getWidth()); 271 deltay = (float) -((mDownPointY - e.getY()) / getWidth()); 272 cut(2, deltax, deltay); 273 break; 274 275 } 276 } 277 actionUp(MotionEvent e)278 private void actionUp(MotionEvent e) { 279 } 280 cut(int side, float fractionx, float fractiony)281 public void cut(int side, float fractionx, float fractiony) { 282 float[] f = Arrays.copyOf(mCurrentTrim, mCurrentTrim.length); 283 f[side] += fractionx; 284 if (f[side] < 0) f[side] = 0; 285 if (f[side] > .8) f[side] = .8f; 286 f[side + 3] += fractiony; 287 if (f[side + 3] < 0) f[side + 3] = 0; 288 if (f[side + 3] > .8) f[side + 3] = .8f; 289 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, f); 290 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 291 mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); 292 } 293 resetCut()294 public void resetCut() { 295 Arrays.fill(mCurrentTrim, 0); 296 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f, mCurrentTrim); 297 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 298 mState1.mCubeScreen.scale(mState1.mVolume.mVoxelDim); 299 mState1.mTransform.look(looks[last_look], mState1.mCubeScreen, getWidth(), getHeight()); 300 mState1.mTransform.setScreenWidth(.6f * mState1.mTransform.getScreenWidth()); 301 last_look = (last_look + 1) % looks.length; 302 render(4); 303 } 304 setVolume(RenderScript rs, Volume v)305 public void setVolume(RenderScript rs, Volume v) { 306 mState1.mRs = rs; 307 mState1.mVolume = v; 308 mState1.mCubeVolume = new Cube(mState1.mVolume, 5f); 309 mState1.mCubeScreen = new TriData(mState1.mCubeVolume); 310 mState1.mCubeScreen.scale(v.mVoxelDim); 311 mState1.mTransform.setVoxelDim(v.mVoxelDim); 312 mState1.mTransform.look(ViewMatrix.DOWN_AT, mState1.mCubeScreen, getWidth(), getHeight()); 313 setLook(mState1.mVolume.getLookNames()[0]); 314 } 315 look(int k)316 protected void look(int k) { 317 mState1.mTransform.look(looks[k], mState1.mCubeVolume, getWidth(), getHeight()); 318 render(4); 319 render(1); 320 } 321 render(int downSample)322 void render(int downSample) { 323 324 if (mRenderTesk == null) { 325 mRenderTesk = new VrRenderTesk(); 326 refresh = 0; 327 mRenderTesk.execute(downSample); 328 } else { 329 refresh = downSample; 330 } 331 } 332 getLooks()333 public String[] getLooks() { 334 return mState1.mVolume.getLookNames(); 335 } 336 setLook(String look)337 public void setLook(String look) { 338 int[][] color = mState1.mVolume.getLookColor(look); 339 int[][] opacity = mState1.mVolume.getLookOpactiy(look); 340 mState1.mMaterial.setup(opacity, color); 341 if (mBinGridTask == null) { 342 mBinGridTask = new VrBinGridTask(); 343 mBinGridTask.execute(mState1.mVolume); 344 } 345 } 346 347 class VrRenderTesk extends AsyncTask<Integer, String, Long> { 348 349 long m_last_time; 350 351 @Override onPreExecute()352 protected void onPreExecute() { 353 mStateLow.copyData(mState1); 354 } 355 356 @Override onCancelled()357 protected void onCancelled() { 358 mPipline.cancel(); 359 } 360 361 @Override doInBackground(Integer... down)362 protected Long doInBackground(Integer... down) { 363 if (mState1.mRs == null) return 0L; 364 if (mSurfaceTexture == null) return 0L; 365 int sample = 4; 366 VrState state = mStateLow; 367 if (down[0] == 1) { 368 if (mPreviousMode == 4) { 369 mState1.copyData(mLastDrawn); 370 } else { 371 mState1.copyData(mStateLow); 372 } 373 // mStateLow.mScrAllocation.setSurface(null); 374 state = mState1; 375 sample = 1; 376 if (mStateLow.mScrAllocation != null) { 377 mStateLow.mScrAllocation.setSurface(null); 378 } 379 } else { 380 if (mState1.mScrAllocation != null) { 381 mState1.mScrAllocation.setSurface(null); 382 } 383 } 384 385 if (mPreviousMode != sample) { 386 if (mSurface != null) { 387 mSurface.release(); 388 } 389 mSurface = new Surface(mSurfaceTexture); 390 } 391 mPreviousMode = sample; 392 393 int img_width = getWidth() / sample; 394 int img_height = getHeight() / sample; 395 state.createOutputAllocation(mSurface, img_width, img_height); 396 397 mPipline.initBuffers(state); 398 399 if (mPipline.isCancel()) { 400 return 0L; 401 } 402 long start = System.nanoTime(); 403 addTimeLine(null); 404 mPipline.setupTriangles(state); 405 406 if (mPipline.isCancel()) { 407 return 0L; 408 } 409 mPipline.rasterizeTriangles(state); 410 411 if (mPipline.isCancel()) { 412 return 0L; 413 } 414 mPipline.raycast(state); 415 416 if (mPipline.isCancel()) { 417 return 0L; 418 } 419 mLastDrawn.copyData(state); 420 state.mRs.finish(); 421 state.mScrAllocation.ioSend(); 422 423 long time = System.nanoTime(); 424 addLine("vr(" + img_width + "," + img_height + "): " + (time - start) / 1E6f + " ms"); 425 return 0L; 426 } 427 addTimeLine(String line)428 private void addTimeLine(String line) { 429 if (line == null) { 430 m_last_time = System.nanoTime(); 431 return; 432 } 433 long time = System.nanoTime(); 434 float ftime = (time - m_last_time) / 1E6f; 435 if (ftime > 100) 436 addLine(line + ": " + (ftime / 1E3f) + " sec"); 437 else 438 addLine(line + ": " + (ftime) + " ms"); 439 m_last_time = System.nanoTime(); 440 } 441 addLine(String line)442 private void addLine(String line) { 443 publishProgress(line); 444 } 445 onProgressUpdate(String... progress)446 protected void onProgressUpdate(String... progress) { 447 Log.v(LOGTAG, progress[0]); 448 } 449 onPostExecute(Long result)450 protected void onPostExecute(Long result) { 451 invalidate(); 452 mRenderTesk = null; 453 if (refresh != 0) { 454 render(refresh); 455 } 456 } 457 } 458 459 class VrBinGridTask extends AsyncTask<Volume, String, Long> { 460 461 @Override doInBackground(Volume... v)462 protected Long doInBackground(Volume... v) { 463 mState1.mRsMask = new RsBrickedBitMask(mState1); 464 mState1.mRs.finish(); 465 return 0L; 466 } 467 onProgressUpdate(String... progress)468 protected void onProgressUpdate(String... progress) { 469 Log.v(LOGTAG, progress[0]); 470 } 471 onPostExecute(Long result)472 protected void onPostExecute(Long result) { 473 mBinGridTask = null; 474 render(4); 475 render(1); 476 } 477 } 478 } 479