Project

General

Profile

Download (19.9 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / magic / RubikCube.java @ 1cbcc6bf

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 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.magic;
21

    
22
import android.graphics.Bitmap;
23
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import org.distorted.library.effect.Effect;
27
import org.distorted.library.effect.EffectType;
28
import org.distorted.library.effect.MatrixEffectMove;
29
import org.distorted.library.effect.MatrixEffectQuaternion;
30
import org.distorted.library.effect.MatrixEffectRotate;
31
import org.distorted.library.effect.MatrixEffectScale;
32
import org.distorted.library.effect.VertexEffectSink;
33
import org.distorted.library.main.DistortedEffects;
34
import org.distorted.library.main.DistortedNode;
35
import org.distorted.library.main.DistortedTexture;
36
import org.distorted.library.mesh.MeshCubes;
37
import org.distorted.library.mesh.MeshQuad;
38
import org.distorted.library.message.EffectListener;
39
import org.distorted.library.type.Dynamic1D;
40
import org.distorted.library.type.Static1D;
41
import org.distorted.library.type.Static3D;
42
import org.distorted.library.type.Static4D;
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

    
46
public class RubikCube extends DistortedNode
47
{
48
    private static final int POST_ROTATION_MILLISEC = 500;
49
    private static final int TEXTURE_SIZE = 100;
50

    
51
    private static final Static3D VectX = new Static3D(1,0,0);
52
    private static final Static3D VectY = new Static3D(0,1,0);
53
    private static final Static3D VectZ = new Static3D(0,0,1);
54

    
55
    private DistortedNode[][][] mNodes;
56
    private MeshCubes[][][] mCubes;
57
    private DistortedEffects[][][] mEffects;
58
    private Static4D[][][] mQuatScramble;
59
    private Static3D[][][] mRotationAxis;
60
    private Dynamic1D[][][] mRotationAngle;
61
    private Static3D[][][] mCurrentPosition;
62
    private MatrixEffectRotate[][][] mRotateEffect;
63
    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
64
    private Static3D mMove, mScale, mNodeMove, mNodeScale;
65
    private DistortedTexture mTexture;
66
    private DistortedEffects mEffectsListeningForNow;
67

    
68
    private int mRotAxis, mRotRow;
69
    private int mSize;
70

    
71
    private DistortedTexture mNodeTexture;
72

    
73
///////////////////////////////////////////////////////////////////////////////////////////////////
74

    
75
    RubikCube(int size, Static4D quatC, Static4D quatA, DistortedTexture texture, MeshQuad mesh, DistortedEffects effects)
76
      {
77
      super(texture,effects,mesh);
78

    
79
      mNodeTexture = texture;
80

    
81
      mSize = size;
82

    
83
      mRotationAngleStatic = new Static1D(0);
84
      mRotationAngleMiddle = new Static1D(0);
85
      mRotationAngleFinal  = new Static1D(0);
86

    
87
      mMove     = new Static3D(0,0,0);
88
      mScale    = new Static3D(1,1,1);
89
      mNodeMove = new Static3D(0,0,0);
90
      mNodeScale= new Static3D(1,1,1);
91

    
92
      mRotAxis= RubikSurfaceView.VECTX;
93
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
94

    
95
      mNodes          = new DistortedNode[mSize][mSize][mSize];
96
      mCubes          = new MeshCubes[mSize][mSize][mSize];
97
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
98
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
99
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
100
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
101
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
102
      mRotateEffect   = new MatrixEffectRotate[mSize][mSize][mSize];
103

    
104
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
105

    
106
      Static3D center = new Static3D(TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f);
107
      Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
108

    
109
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
110
      MatrixEffectMove        moveEffect = new MatrixEffectMove(mMove);
111
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(mScale);
112
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
113
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
114

    
115
      MatrixEffectMove       nodeMoveEffect  = new MatrixEffectMove(mNodeMove);
116
      MatrixEffectScale      nodeScaleEffect = new MatrixEffectScale(mNodeScale);
117

    
118
      effects.apply(nodeMoveEffect);
119
      effects.apply(nodeScaleEffect);
120

    
121
      // 3x2 bitmap = 6 squares:
122
      //
123
      // RED     GREEN   BLUE
124
      // YELLOW  WHITE   BROWN
125

    
126
      final float ze = 0.0f;
127
      final float ot = 1.0f/3.0f;
128
      final float tt = 2.0f/3.0f;
129
      final float oh = 1.0f/2.0f;
130
      final float of = 1.0f/40.0f;
131

    
132
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
133
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
134
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
135
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
136
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
137
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
138

    
139
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
140

    
141
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
142
      float nc = 0.5f*(mSize-1);
143
      int vertices = (int)(24.0f/mSize + 2.0f);
144

    
145
      for(int x = 0; x< mSize; x++)
146
        for(int y = 0; y< mSize; y++)
147
          for(int z = 0; z< mSize; z++)
148
            {
149
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
150
              {
151
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
152
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
153
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
154
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
155
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
156
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
157

    
158
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
159
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
160
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
161
              mRotationAngle[x][y][z]   = new Dynamic1D();
162
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
163
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
164
              mRotateEffect[x][y][z]    = new MatrixEffectRotate(mRotationAngle[x][y][z], mRotationAxis[x][y][z], center);
165

    
166
              mEffects[x][y][z] = new DistortedEffects();
167
              mEffects[x][y][z].apply(sinkEffect);
168
              mEffects[x][y][z].apply(moveEffect);
169
              mEffects[x][y][z].apply(scaleEffect);
170
              mEffects[x][y][z].apply(quatCEffect);
171
              mEffects[x][y][z].apply(quatAEffect);
172
              mEffects[x][y][z].apply(mRotateEffect[x][y][z]);
173
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
174
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
175

    
176
              mNodes[x][y][z] = new DistortedNode(mTexture,mEffects[x][y][z],mCubes[x][y][z]);
177

    
178
              attach(mNodes[x][y][z]);
179
              }
180
            }
181
      }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

    
185
    public void apply(Effect effect, int position)
186
      {
187
      for(int x=0; x<mSize; x++)
188
        for(int y=0; y<mSize; y++)
189
          for(int z=0; z<mSize; z++)
190
            {
191
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
192
              {
193
              mEffects[x][y][z].apply(effect, position);
194
              }
195
            }
196
      }
197

    
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199

    
200
    public void remove(long effectID)
201
      {
202
      for(int x=0; x<mSize; x++)
203
        for(int y=0; y<mSize; y++)
204
          for(int z=0; z<mSize; z++)
205
            {
206
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
207
              {
208
              mEffects[x][y][z].abortById(effectID);
209
              }
210
            }
211
      }
212

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

    
215
    public int getNumEffects(EffectType type)
216
      {
217
      return mEffects[0][0][0].getNumEffects(type);
218
      }
219

    
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
    public void registerForMessages(EffectListener listener)
223
      {
224
      mEffects[0][0][0].registerForMessages(listener);
225
      }
226

    
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

    
229
    public void deregisterForMessages(EffectListener listener)
230
      {
231
      mEffects[0][0][0].deregisterForMessages(listener);
232
      }
233

    
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
236
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
237
// memory leak.
238

    
239
    void releaseResources()
240
      {
241
      mTexture.markForDeletion();
242

    
243
      for(int x=0; x<mSize; x++)
244
        for(int y=0; y<mSize; y++)
245
          for(int z=0; z<mSize; z++)
246
            {
247
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
248
              {
249
              mCubes[x][y][z].markForDeletion();
250
              mNodes[x][y][z].markForDeletion();
251
              }
252
            }
253
      }
254

    
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256

    
257
    void addNewRotation(int vector, float offset )
258
      {
259
      Static3D axis = VectX;
260

    
261
      switch(vector)
262
        {
263
        case RubikSurfaceView.VECTX: axis = VectX; break;
264
        case RubikSurfaceView.VECTY: axis = VectY; break;
265
        case RubikSurfaceView.VECTZ: axis = VectZ; break;
266
        }
267

    
268
      mRotAxis = vector;
269
      mRotRow  = (int)(mSize*offset);
270

    
271
      mRotationAngleStatic.set1(0.0f);
272

    
273
      for(int x=0; x<mSize; x++)
274
        for(int y=0; y<mSize; y++)
275
          for(int z=0; z<mSize; z++)
276
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
277
              {
278
              if( belongsToRotation(x,y,z,vector,mRotRow) )
279
                {
280
                mRotationAxis[x][y][z].set(axis);
281
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
282
                }
283
              }
284
      }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287

    
288
    void continueRotation(float angleInDegrees)
289
      {
290
      mRotationAngleStatic.set1(angleInDegrees);
291
      }
292

    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294

    
295
    private int computeNearestAngle(float angle)
296
      {
297
      final int NEAREST = 90;
298

    
299
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
300
      if( angle< -(NEAREST/2) ) tmp-=1;
301

    
302
      return NEAREST*tmp;
303
      }
304

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306

    
307
    long finishRotationNow(EffectListener listener)
308
      {
309
      boolean first = true;
310
      float startingAngle = mRotationAngleStatic.get1();
311
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
312
      long effectID=0;
313

    
314
      mRotationAngleFinal.set1(nearestAngleInDegrees);
315
      mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
316

    
317
      for(int x=0; x<mSize; x++)
318
        for(int y=0; y<mSize; y++)
319
          for(int z=0; z<mSize; z++)
320
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
321
              {
322
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
323
                {
324
                mRotationAngle[x][y][z].makeRunNowFor(POST_ROTATION_MILLISEC);
325
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
326
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
327

    
328
                if( first )
329
                  {
330
                  first = false;
331
                  mEffectsListeningForNow = mEffects[x][y][z];
332
                  mEffectsListeningForNow.registerForMessages(listener);
333
                  effectID = mRotateEffect[x][y][z].getID();
334
                  }
335
                }
336
              }
337

    
338
      return effectID;
339
      }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342

    
343
    void removeRotationNow(EffectListener listener)
344
      {
345
      mEffectsListeningForNow.deregisterForMessages(listener);
346

    
347
      int nearestAngleInDegrees = computeNearestAngle(mRotationAngleStatic.get1());
348
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
349
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
350
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
351

    
352
      mRotationAngleStatic.set1(0);
353

    
354
      float qx=0,qy=0,qz=0;
355

    
356
      switch(mRotAxis)
357
        {
358
        case RubikSurfaceView.VECTX: qx=1; break;
359
        case RubikSurfaceView.VECTY: qy=1; break;
360
        case RubikSurfaceView.VECTZ: qz=1; break;
361
        }
362

    
363
      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
364

    
365
      for(int x=0; x<mSize; x++)
366
        for(int y=0; y<mSize; y++)
367
          for(int z=0; z<mSize; z++)
368
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
369
              {
370
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
371
                {
372
                mRotationAngle[x][y][z].makeRunNowFor(0);
373
                mRotationAngle[x][y][z].removeAll();
374
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
375
                modifyCurrentPosition(x,y,z,quat);
376
                }
377
              }
378
      }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

    
382
    private float getSinkStrength()
383
      {
384
      switch(mSize)
385
        {
386
        case 1 : return 1.1f;
387
        case 2 : return 1.5f;
388
        case 3 : return 1.8f;
389
        case 4 : return 2.0f;
390
        default: return 3.0f - 4.0f/mSize;
391
        }
392
      }
393

    
394
///////////////////////////////////////////////////////////////////////////////////////////////////
395

    
396
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
397
      {
398
      switch(vector)
399
        {
400
        case RubikSurfaceView.VECTX: return mCurrentPosition[x][y][z].get1()==row;
401
        case RubikSurfaceView.VECTY: return mCurrentPosition[x][y][z].get2()==row;
402
        case RubikSurfaceView.VECTZ: return mCurrentPosition[x][y][z].get3()==row;
403
        }
404

    
405
      return false;
406
      }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409

    
410
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
411
      {
412
      Static3D current = mCurrentPosition[x][y][z];
413
      float diff = 0.5f*(mSize-1);
414
      float cubitCenterX = current.get1() - diff;
415
      float cubitCenterY = current.get2() - diff;
416
      float cubitCenterZ = current.get3() - diff;
417

    
418
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
419
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
420

    
421
      float rotatedX = rotatedCenter.get1() + diff;
422
      float rotatedY = rotatedCenter.get2() + diff;
423
      float rotatedZ = rotatedCenter.get3() + diff;
424

    
425
      int roundedX = (int)(rotatedX+0.1f);
426
      int roundedY = (int)(rotatedY+0.1f);
427
      int roundedZ = (int)(rotatedZ+0.1f);
428

    
429
      mCurrentPosition[x][y][z].set1(roundedX);
430
      mCurrentPosition[x][y][z].set2(roundedY);
431
      mCurrentPosition[x][y][z].set3(roundedZ);
432
      }
433

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435

    
436
    void createTexture()
437
      {
438
      Bitmap bitmap;
439

    
440
      final int S = 128;
441
      final int W = 3*S;
442
      final int H = 2*S;
443
      final int R = S/10;
444
      final int M = S/20;
445

    
446
      Paint paint = new Paint();
447
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
448
      Canvas canvas = new Canvas(bitmap);
449

    
450
      paint.setAntiAlias(true);
451
      paint.setTextAlign(Paint.Align.CENTER);
452
      paint.setStyle(Paint.Style.FILL);
453

    
454
      // 3x2 bitmap = 6 squares:
455
      //
456
      // RED     GREEN   BLUE
457
      // YELLOW  WHITE   BROWN
458

    
459
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
460
      canvas.drawRect(0, 0, W, H, paint);                          //
461

    
462
      paint.setColor(0xffff0000);                                  // RED
463
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
464
      paint.setColor(0xff00ff00);                                  // GREEN
465
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
466
      paint.setColor(0xff0000ff);                                  // BLUE
467
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
468
      paint.setColor(0xffffff00);                                  // YELLOW
469
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
470
      paint.setColor(0xffffffff);                                  // WHITE
471
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
472
      paint.setColor(0xffb5651d);                                  // BROWN
473
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
474

    
475
      mTexture.setTexture(bitmap);
476
      }
477

    
478
///////////////////////////////////////////////////////////////////////////////////////////////////
479

    
480
   void recomputeScaleFactor(int screenWidth, int screenHeight, float size)
481
     {
482
     int texW = mNodeTexture.getWidth();
483
     int texH = mNodeTexture.getHeight();
484

    
485
     if( (float)texH/texW > (float)screenHeight/screenWidth )
486
       {
487
       int w = (screenHeight*texW)/texH;
488
       float factor = (float)screenHeight/texH;
489
       mNodeMove.set((screenWidth-w)*0.5f ,0, 0);
490
       mNodeScale.set(factor,factor,factor);
491
       }
492
     else
493
       {
494
       int h = (screenWidth*texH)/texW;
495
       float factor = (float)screenWidth/texW;
496
       mNodeMove.set(0,(screenHeight-h)*0.5f,0);
497
       mNodeScale.set(factor,factor,factor);
498
       }
499

    
500
     float scaleFactor = (size/(TEXTURE_SIZE*mSize)) * (float)texW/(screenWidth>screenHeight ? screenHeight:screenWidth);
501

    
502
     mMove.set( (texW-scaleFactor*TEXTURE_SIZE)/2 , (texH-scaleFactor*TEXTURE_SIZE)/2 , -scaleFactor*TEXTURE_SIZE/2 );
503
     mScale.set(scaleFactor,scaleFactor,scaleFactor);
504
     }
505

    
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507

    
508
    int getSize()
509
      {
510
      return mSize;
511
      }
512
}
(3-3/6)