Project

General

Profile

Download (18 KB) Statistics
| Branch: | Revision:

examples / src / main / java / org / distorted / examples / rubik / RubikSurfaceView.java @ 8be29dfc

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted is distributed in the hope that it will be useful,                                  //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.examples.rubik;
21

    
22
import android.app.ActivityManager;
23
import android.content.Context;
24
import android.content.pm.ConfigurationInfo;
25
import android.opengl.GLSurfaceView;
26
import android.view.MotionEvent;
27

    
28
import org.distorted.library.type.Static4D;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31

    
32
class RubikSurfaceView extends GLSurfaceView
33
{
34
    private final static int NONE   =-1;
35
    private final static int FRONT  = 0;  // has to be 6 consecutive ints
36
    private final static int BACK   = 1;  // FRONT ... BOTTOM
37
    private final static int LEFT   = 2;  //
38
    private final static int RIGHT  = 3;  //
39
    private final static int TOP    = 4;  //
40
    private final static int BOTTOM = 5;  //
41

    
42
    private static final int DIR_UP   =0;
43
    private static final int DIR_DOWN =1;
44
    private static final int DIR_LEFT =2;
45
    private static final int DIR_RIGHT=3;
46

    
47
    static final int VECTX = 0;
48
    static final int VECTY = 1;
49
    static final int VECTZ = 2;
50
    static final int VECTN = 3;
51

    
52
    private boolean mDragging, mRotating;
53
    private int mX, mY;
54
    private Static4D mQuatCurrent, mQuatAccumulated;
55
    private int mRotationVect;
56
    private RubikRenderer mRenderer;
57

    
58
    private float mPoiX, mPoiY, mPoiZ, mCamX, mCamY, mCamZ;
59
    private float mStartX, mStartY, mStartZ;
60
    private int mLastTouchedFace;
61

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

    
64
    public RubikSurfaceView(Context context)
65
      {
66
      super(context);
67

    
68
      mDragging = false;
69
      mRotating = false;
70
      mRotationVect = VECTN;
71

    
72
      mRenderer = new RubikRenderer(this);
73

    
74
      mQuatCurrent     = new Static4D(0,0,0,1);
75
      mQuatAccumulated = mRenderer.initializeQuat();
76

    
77
      final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
78
      final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
79
      setEGLContextClientVersion( (configurationInfo.reqGlEsVersion>>16) >= 3 ? 3:2 );
80
      setRenderer(mRenderer);
81
      }
82

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84

    
85
    public RubikRenderer getRenderer()
86
      {
87
      return mRenderer;
88
      }
89

    
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91

    
92
    @Override
93
    public boolean onTouchEvent(MotionEvent event)
94
      {
95
      int action = event.getAction();
96
      int x = (int)event.getX();
97
      int y = (int)event.getY();
98

    
99
      switch(action)
100
         {
101
         case MotionEvent.ACTION_DOWN: mX = x;
102
                                       mY = y;
103

    
104
                                       mLastTouchedFace = faceTouched(x,y);
105

    
106
                                       if( mLastTouchedFace != NONE )
107
                                         {
108
                                         mRotating = true;
109
                                         }
110
                                       else
111
                                         {
112
                                         mDragging = true;
113
                                         }
114
                                       break;
115
         case MotionEvent.ACTION_MOVE: if( mDragging )
116
                                         {
117
                                         mQuatCurrent.set(quatFromDrag(mX-x,mY-y));
118
                                         mRenderer.setQuatCurrent(mQuatCurrent);
119
                                         }
120
                                       else if( mRotating )
121
                                         {
122
                                         int minimumToRotate = (mRenderer.mScreenMin*mRenderer.mScreenMin)/36;
123

    
124
                                         if( (mX-x)*(mX-x)+(mY-y)*(mY-y)>minimumToRotate )
125
                                           {
126
                                           addNewRotation(x,y);
127
                                           mX = x;
128
                                           mY = y;
129
                                           mRotating = false;
130
                                           }
131
                                         }
132
                                       else
133
                                         {
134
                                         continueRotation(x,y);
135
                                         }
136
                                       break;
137
         case MotionEvent.ACTION_UP  : if( !mDragging ) finishRotation();
138

    
139
                                       mDragging = false;
140
                                       mRotating = false;
141

    
142
                                       mQuatAccumulated.set(quatMultiply(mQuatCurrent, mQuatAccumulated));
143
                                       mQuatCurrent.set(0f, 0f, 0f, 1f);
144
                                       mRenderer.setQuatCurrent(mQuatCurrent);
145
                                       mRenderer.setQuatAccumulated(mQuatAccumulated);
146
                                       break;
147
         }
148

    
149
      return true;
150
      }
151

    
152
///////////////////////////////////////////////////////////////////////////////////////////////////
153

    
154
    private void addNewRotation(int x, int y)
155
      {
156
      fillTouchPoint(x,y);
157

    
158
      float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
159
      float tmp = RubikRenderer.NUM_CUBES/(2*cubeHalfSize);
160
      float A=retA(mLastTouchedFace,cubeHalfSize);
161

    
162
      float diffX = (mPoiX-mCamX)*A + mCamX - mStartX;
163
      float diffY = (mPoiY-mCamY)*A + mCamY - mStartY;
164
      float diffZ = (mPoiZ-mCamZ)*A + mCamZ - mStartZ;
165

    
166
      int dir, row=1;
167

    
168
      switch(mLastTouchedFace)
169
        {
170
        case FRONT : dir = retDirection( diffX, diffY);
171
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTY;
172
                     row = (int)( tmp*((mRotationVect==VECTX?mStartX:mStartY)+cubeHalfSize) );
173
                     break;
174
        case BACK  : dir = retDirection(-diffX, diffY);
175
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTY;
176
                     row = (int)( tmp*((mRotationVect==VECTX?mStartX:mStartY)+cubeHalfSize) );
177
                     break;
178
        case LEFT  : dir = retDirection( diffZ, diffY);
179
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTZ:VECTY;
180
                     row = (int)( tmp*((mRotationVect==VECTZ?mStartZ:mStartY)+cubeHalfSize) );
181
                     break;
182
        case RIGHT : dir = retDirection(-diffZ, diffY);
183
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTZ:VECTY;
184
                     row = (int)( tmp*((mRotationVect==VECTZ?mStartZ:mStartY)+cubeHalfSize) );
185
                     break;
186
        case TOP   : dir = retDirection( diffX,-diffZ);
187
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTZ;
188
                     row = (int)( tmp*((mRotationVect==VECTX?mStartX:mStartZ)+cubeHalfSize) );
189
                     break;
190
        case BOTTOM: dir = retDirection( diffX, diffZ);
191
                     mRotationVect = (dir==DIR_UP || dir==DIR_DOWN) ? VECTX:VECTZ;
192
                     row = (int)( tmp*((mRotationVect==VECTX?mStartX:mStartZ)+cubeHalfSize) );
193
                     break;
194
        }
195

    
196
      mStartX = diffX + mStartX;
197
      mStartY = diffY + mStartY;
198
      mStartZ = diffZ + mStartZ;
199

    
200
      mRenderer.addNewRotation(mRotationVect,row);
201
      }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
    private void continueRotation(int x, int y)
206
      {
207
      fillTouchPoint(x,y);
208

    
209
      int sign=1;
210
      float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
211
      float A=retA(mLastTouchedFace,cubeHalfSize);
212

    
213
      float diffX = (mPoiX-mCamX)*A + mCamX - mStartX;
214
      float diffY = (mPoiY-mCamY)*A + mCamY - mStartY;
215
      float diffZ = (mPoiZ-mCamZ)*A + mCamZ - mStartZ;
216

    
217
      switch(mRotationVect)
218
        {
219
        case VECTX: switch(mLastTouchedFace)
220
                      {
221
                      case FRONT : sign = returnSign(-diffY); break;
222
                      case BACK  : sign = returnSign( diffY); break;
223
                      case TOP   : sign = returnSign( diffZ); break;
224
                      case BOTTOM: sign = returnSign(-diffZ); break;
225
                      }
226
                    break;
227
        case VECTY: switch(mLastTouchedFace)
228
                      {
229
                      case FRONT : sign = returnSign( diffX); break;
230
                      case BACK  : sign = returnSign(-diffX); break;
231
                      case LEFT  : sign = returnSign( diffZ); break;
232
                      case RIGHT : sign = returnSign(-diffZ); break;
233
                      }
234
                    break;
235
        case VECTZ: switch(mLastTouchedFace)
236
                      {
237
                      case TOP   : sign = returnSign(-diffX); break;
238
                      case BOTTOM: sign = returnSign( diffX); break;
239
                      case LEFT  : sign = returnSign(-diffY); break;
240
                      case RIGHT : sign = returnSign( diffY); break;
241
                      }
242
                    break;
243
        default   : android.util.Log.e("View", "impossible vector: "+mRotationVect);
244
        }
245

    
246
      float dX = mX-x;
247
      float dY = mY-y;
248
      float calibration = (float)Math.sqrt(dX*dX+dY*dY);
249

    
250
      mRenderer.continueRotation(calibration*sign);
251
      }
252

    
253
///////////////////////////////////////////////////////////////////////////////////////////////////
254

    
255
    private void finishRotation()
256
      {
257
      mRotationVect = VECTN;
258

    
259
      mRenderer.finishRotation();
260
      }
261

    
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263

    
264
    private int returnSign(float diff)
265
      {
266
      return diff>0 ? 1 : -1;
267
      }
268

    
269
///////////////////////////////////////////////////////////////////////////////////////////////////
270

    
271
    private int retDirection(float a, float b)
272
      {
273
      if( b>a ) return a>-b ? DIR_UP   :DIR_LEFT;
274
      else      return a>-b ? DIR_RIGHT:DIR_DOWN;
275
      }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
// return quat1*quat2
279

    
280
    static Static4D quatMultiply( Static4D quat1, Static4D quat2 )
281
      {
282
      float qx = quat1.get1();
283
      float qy = quat1.get2();
284
      float qz = quat1.get3();
285
      float qw = quat1.get4();
286

    
287
      float rx = quat2.get1();
288
      float ry = quat2.get2();
289
      float rz = quat2.get3();
290
      float rw = quat2.get4();
291

    
292
      float tx = rw*qx - rz*qy + ry*qz + rx*qw;
293
      float ty = rw*qy + rz*qx + ry*qw - rx*qz;
294
      float tz = rw*qz + rz*qw - ry*qx + rx*qy;
295
      float tw = rw*qw - rz*qz - ry*qy - rx*qx;
296

    
297
      return new Static4D(tx,ty,tz,tw);
298
      }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
    private Static4D quatFromDrag(float dragX, float dragY)
303
      {
304
      float axisX = dragY;  // inverted X and Y - rotation axis is
305
      float axisY = dragX;  // perpendicular to (dragX,dragY)
306
      float axisZ = 0;
307
      float axisL = (float)Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
308

    
309
      if( axisL>0 )
310
        {
311
        axisX /= axisL;
312
        axisY /= axisL;
313
        axisZ /= axisL;
314

    
315
        float cosA = (float)Math.cos(axisL*Math.PI/mRenderer.mScreenMin);
316
        float sinA = (float)Math.sqrt(1-cosA*cosA);
317

    
318
        return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
319
        }
320

    
321
      return new Static4D(0f, 0f, 0f, 1f);
322
      }
323

    
324
///////////////////////////////////////////////////////////////////////////////////////////////////
325

    
326
    private int returnFrontFace()
327
      {
328
      Static4D rotated = rotateVector(new Static4D(0,0,1,0));
329

    
330
      float rotatedX = rotated.get1();
331
      float rotatedY = rotated.get2();
332
      float rotatedZ = rotated.get3();
333

    
334
      float absX = rotatedX>0 ? rotatedX : -rotatedX;
335
      float absY = rotatedY>0 ? rotatedY : -rotatedY;
336
      float absZ = rotatedZ>0 ? rotatedZ : -rotatedZ;
337

    
338
      if( absX>absY && absX>absZ ) return rotatedX>0 ? RIGHT:LEFT;
339
      if( absY>absX && absY>absZ ) return rotatedY>0 ? TOP:BOTTOM;
340
      if( absZ>absX && absZ>absY ) return rotatedZ>0 ? FRONT:BACK;
341

    
342
      return NONE;
343
      }
344

    
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346

    
347
    private boolean faceIsVisible(int face)
348
      {
349
      float cameraDistance = mRenderer.returnCameraDistance();
350
      float cubeHalfSize   = mRenderer.returnCubeSize()*0.5f;
351

    
352
      Static4D rotated = rotateVector(new Static4D(0,0,cameraDistance,0));
353

    
354
      switch(face)
355
        {
356
        case FRONT : return rotated.get3() >  cubeHalfSize;
357
        case BACK  : return rotated.get3() < -cubeHalfSize;
358
        case LEFT  : return rotated.get1() < -cubeHalfSize;
359
        case RIGHT : return rotated.get1() >  cubeHalfSize;
360
        case TOP   : return rotated.get2() >  cubeHalfSize;
361
        case BOTTOM: return rotated.get2() < -cubeHalfSize;
362
        }
363

    
364
      return false;
365
      }
366

    
367
///////////////////////////////////////////////////////////////////////////////////////////////////
368
// rotate 'vector' by quaternion q=mQuatAccumulated^(-1)  ( i.e. return (q^-1)*vector*q )
369

    
370
    private Static4D rotateVector(Static4D vector)
371
      {
372
      float qx = mQuatAccumulated.get1();
373
      float qy = mQuatAccumulated.get2();
374
      float qz = mQuatAccumulated.get3();
375
      float qw = mQuatAccumulated.get4();
376

    
377
      Static4D quatInverted= new Static4D(-qx,-qy,-qz,qw);
378
      Static4D tmp = quatMultiply(quatInverted,vector);
379

    
380
      return quatMultiply(tmp,mQuatAccumulated);
381
      }
382

    
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384

    
385
    private int faceTouched(int xTouch, int yTouch)
386
      {
387
      float cubeHalfSize= mRenderer.returnCubeSize()*0.5f;
388

    
389
      fillTouchPoint(xTouch,yTouch);
390
      fillCamera();
391

    
392
      for(int face=FRONT; face<=BOTTOM; face++)
393
        {
394
        if( faceIsVisible(face) && faceCondition(face) )
395
          {
396
          float A = retA(face,cubeHalfSize);
397

    
398
          mStartX = (mPoiX-mCamX)*A + mCamX;
399
          mStartY = (mPoiY-mCamY)*A + mCamY;
400
          mStartZ = (mPoiZ-mCamZ)*A + mCamZ;
401

    
402
          float qX= (mStartX+cubeHalfSize) / (2*cubeHalfSize);
403
          float qY= (mStartY+cubeHalfSize) / (2*cubeHalfSize);
404
          float qZ= (mStartZ+cubeHalfSize) / (2*cubeHalfSize);
405

    
406
          if( qX<=1 && qX>=0 && qY<=1 && qY>=0 && qZ<=1 && qZ>=0 ) return face;
407
          }
408
        }
409

    
410
      return NONE;
411
      }
412

    
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414

    
415
    void fillTouchPoint(int x, int y)
416
      {
417
      float halfScrWidth  = mRenderer.getScreenWidth() *0.5f;
418
      float halfScrHeight = mRenderer.getScreenHeight()*0.5f;
419
      Static4D touchPoint = new Static4D(x-halfScrWidth, halfScrHeight-y, 0, 0);
420
      Static4D rotatedTouchPoint= rotateVector(touchPoint);
421

    
422
      mPoiX = rotatedTouchPoint.get1();
423
      mPoiY = rotatedTouchPoint.get2();
424
      mPoiZ = rotatedTouchPoint.get3();
425
      }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428

    
429
    void fillCamera()
430
      {
431
      float cameraDistance = mRenderer.returnCameraDistance();
432

    
433
      Static4D cameraPoint = new Static4D(0, 0, cameraDistance, 0);
434
      Static4D rotatedCamera= rotateVector(cameraPoint);
435

    
436
      mCamX = rotatedCamera.get1();
437
      mCamY = rotatedCamera.get2();
438
      mCamZ = rotatedCamera.get3();
439
      }
440

    
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442

    
443
    float retA(int face, float cubeHalfSize)
444
      {
445
      switch(face)
446
        {
447
        case FRONT : return ( mPoiZ!=mCamZ ? ( cubeHalfSize-mCamZ)/(mPoiZ-mCamZ) : 0f);
448
        case BACK  : return ( mPoiZ!=mCamZ ? (-cubeHalfSize-mCamZ)/(mPoiZ-mCamZ) : 0f);
449
        case LEFT  : return ( mPoiX!=mCamX ? (-cubeHalfSize-mCamX)/(mPoiX-mCamX) : 0f);
450
        case RIGHT : return ( mPoiX!=mCamX ? ( cubeHalfSize-mCamX)/(mPoiX-mCamX) : 0f);
451
        case TOP   : return ( mPoiY!=mCamY ? ( cubeHalfSize-mCamY)/(mPoiY-mCamY) : 0f);
452
        case BOTTOM: return ( mPoiY!=mCamY ? (-cubeHalfSize-mCamY)/(mPoiY-mCamY) : 0f);
453
        }
454

    
455
      return 0.0f;
456
      }
457

    
458
///////////////////////////////////////////////////////////////////////////////////////////////////
459

    
460
    boolean faceCondition(int face)
461
      {
462
      switch(face)
463
        {
464
        case FRONT :
465
        case BACK  : return ( mPoiZ!=mCamZ );
466
        case LEFT  :
467
        case RIGHT : return ( mPoiX!=mCamX );
468
        case TOP   :
469
        case BOTTOM: return ( mPoiY!=mCamY );
470
        }
471

    
472
      return false;
473
      }
474
}
475

    
(3-3/3)