Project

General

Profile

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

examples / src / main / java / org / distorted / examples / rubik / RubikCube.java @ e3a72781

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.examples.rubik;
21

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

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

    
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43

    
44
class RubikCube
45
{
46
    private static final int POST_ROTATION_MILLISEC = 500;
47
    private static final int TEXTURE_SIZE = 100;
48

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

    
53
    private DistortedNode[][][] mNodes;
54
    private MeshCubes[][][] mCubes;
55
    private DistortedEffects[][][] mEffects;
56
    private Static4D[][][] mQuatScramble;
57
    private Static3D[][][] mRotationAxis;
58
    private Dynamic1D[][][] mRotationAngle;
59
    private Static3D[][][] mCurrentPosition;
60
    private Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
61
    private DistortedTexture mTexture;
62
    private DistortedEffects mEffectsListeningForNow;
63

    
64
    private int mRotAxis, mRotRow;
65
    private int mSize;
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
    RubikCube(int size, Static3D move, Static3D scale, Static4D quatC, Static4D quatA)
70
      {
71
      mSize = size;
72

    
73
      mRotationAngleStatic = new Static1D(0);
74
      mRotationAngleMiddle = new Static1D(0);
75
      mRotationAngleFinal  = new Static1D(0);
76

    
77
      mRotAxis= RubikSurfaceView.VECTX;
78
      mTexture = new DistortedTexture(TEXTURE_SIZE,TEXTURE_SIZE);
79

    
80
      mNodes          = new DistortedNode[mSize][mSize][mSize];
81
      mCubes          = new MeshCubes[mSize][mSize][mSize];
82
      mEffects        = new DistortedEffects[mSize][mSize][mSize];
83
      mQuatScramble   = new Static4D[mSize][mSize][mSize];
84
      mRotationAxis   = new Static3D[mSize][mSize][mSize];
85
      mRotationAngle  = new Dynamic1D[mSize][mSize][mSize];
86
      mCurrentPosition= new Static3D[mSize][mSize][mSize];
87

    
88
      Static3D[][][] cubeVectors = new Static3D[mSize][mSize][mSize];
89

    
90
      Static3D center = new Static3D(TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f, TEXTURE_SIZE*0.5f);
91
      Static4D region = new Static4D(0,0,0, TEXTURE_SIZE*0.72f);
92

    
93
      VertexEffectSink        sinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
94
      MatrixEffectMove        moveEffect = new MatrixEffectMove(move);
95
      MatrixEffectScale      scaleEffect = new MatrixEffectScale(scale);
96
      MatrixEffectQuaternion quatCEffect = new MatrixEffectQuaternion(quatC, center);
97
      MatrixEffectQuaternion quatAEffect = new MatrixEffectQuaternion(quatA, center);
98

    
99
      // 3x2 bitmap = 6 squares:
100
      //
101
      // RED     GREEN   BLUE
102
      // YELLOW  WHITE   BROWN
103

    
104
      final float ze = 0.0f;
105
      final float ot = 1.0f/3.0f;
106
      final float tt = 2.0f/3.0f;
107
      final float oh = 1.0f/2.0f;
108
      final float of = 1.0f/40.0f;
109

    
110
      final Static4D mapFront = new Static4D(ze,oh, ze+ot,oh+oh);
111
      final Static4D mapBack  = new Static4D(tt,ze, tt+ot,ze+oh);
112
      final Static4D mapLeft  = new Static4D(ot,ze, ot+ot,ze+oh);
113
      final Static4D mapRight = new Static4D(ze,ze, ze+ot,ze+oh);
114
      final Static4D mapTop   = new Static4D(tt,oh, tt+ot,oh+oh);
115
      final Static4D mapBottom= new Static4D(ot,oh, ot+ot,oh+oh);
116

    
117
      final Static4D mapBlack = new Static4D(ze,ze, ze+of,ze+of);
118

    
119
      Static4D tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom;
120
      float nc = 0.5f*(mSize-1);
121
      int vertices = (int)(24.0f/mSize + 2.0f);
122

    
123
      for(int x = 0; x< mSize; x++)
124
        for(int y = 0; y< mSize; y++)
125
          for(int z = 0; z< mSize; z++)
126
            {
127
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 ) // only the external walls
128
              {
129
              tmpLeft  = (x==       0 ? mapLeft  :mapBlack);
130
              tmpRight = (x== mSize-1 ? mapRight :mapBlack);
131
              tmpFront = (z== mSize-1 ? mapFront :mapBlack);
132
              tmpBack  = (z==       0 ? mapBack  :mapBlack);
133
              tmpTop   = (y== mSize-1 ? mapTop   :mapBlack);
134
              tmpBottom= (y==       0 ? mapBottom:mapBlack);
135

    
136
              mCubes[x][y][z]           = new MeshCubes(vertices,vertices,vertices, tmpFront, tmpBack, tmpLeft, tmpRight, tmpTop, tmpBottom);
137
              cubeVectors[x][y][z]      = new Static3D( TEXTURE_SIZE*(x-nc), TEXTURE_SIZE*(y-nc), TEXTURE_SIZE*(z-nc) );
138
              mQuatScramble[x][y][z]    = new Static4D(0,0,0,1);
139
              mRotationAngle[x][y][z]   = new Dynamic1D();
140
              mRotationAxis[x][y][z]    = new Static3D(1,0,0);
141
              mCurrentPosition[x][y][z] = new Static3D(x,y,z);
142

    
143
              mEffects[x][y][z] = new DistortedEffects();
144
              mEffects[x][y][z].apply(sinkEffect);
145
              mEffects[x][y][z].apply(moveEffect);
146
              mEffects[x][y][z].apply(scaleEffect);
147
              mEffects[x][y][z].apply(quatCEffect);
148
              mEffects[x][y][z].apply(quatAEffect);
149
              mEffects[x][y][z].apply( new MatrixEffectRotate( mRotationAngle[x][y][z], mRotationAxis[x][y][z], center));
150
              mEffects[x][y][z].apply( new MatrixEffectQuaternion(mQuatScramble[x][y][z], center));
151
              mEffects[x][y][z].apply( new MatrixEffectMove(cubeVectors[x][y][z]) );
152
              }
153
            }
154
      }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157

    
158
    void attachToScreen(DistortedScreen screen)
159
      {
160
      for(int x=0; x<mSize; x++)
161
        for(int y=0; y<mSize; y++)
162
          for(int z=0; z<mSize; z++)
163
            {
164
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
165
              {
166
              mNodes[x][y][z] = new DistortedNode(mTexture,mEffects[x][y][z],mCubes[x][y][z]);
167
              screen.attach(mNodes[x][y][z]);
168
              }
169
            }
170
      }
171

    
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173
// all DistortedTextures, DistortedNodes, DistortedFramebuffers, DistortedScreens and all types of
174
// Meshes HAVE TO be markedForDeletion when they are no longer needed- otherwise we have a major
175
// memory leak.
176

    
177
    void releaseResources()
178
      {
179
      mTexture.markForDeletion();
180

    
181
      for(int x=0; x<mSize; x++)
182
        for(int y=0; y<mSize; y++)
183
          for(int z=0; z<mSize; z++)
184
            {
185
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
186
              {
187
              mCubes[x][y][z].markForDeletion();
188
              mNodes[x][y][z].markForDeletion();
189
              }
190
            }
191
      }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194

    
195
    void addNewRotation(int vector, float offset )
196
      {
197
      Static3D axis = VectX;
198

    
199
      switch(vector)
200
        {
201
        case RubikSurfaceView.VECTX: axis = VectX; break;
202
        case RubikSurfaceView.VECTY: axis = VectY; break;
203
        case RubikSurfaceView.VECTZ: axis = VectZ; break;
204
        }
205

    
206
      mRotAxis = vector;
207
      mRotRow  = (int)(mSize*offset);
208

    
209
      mRotationAngleStatic.set1(0.0f);
210

    
211
      for(int x=0; x<mSize; x++)
212
        for(int y=0; y<mSize; y++)
213
          for(int z=0; z<mSize; z++)
214
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
215
              {
216
              if( belongsToRotation(x,y,z,vector,mRotRow) )
217
                {
218
                mRotationAxis[x][y][z].set(axis);
219
                mRotationAngle[x][y][z].add(mRotationAngleStatic);
220
                }
221
              }
222
      }
223

    
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

    
226
    void continueRotation(float angleInDegrees)
227
      {
228
      mRotationAngleStatic.set1(angleInDegrees);
229
      }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232

    
233
    private int computeNearestAngle(float angle)
234
      {
235
      final int NEAREST = 90;
236

    
237
      int tmp = (int)((angle+NEAREST/2)/NEAREST);
238
      if( angle< -(NEAREST/2) ) tmp-=1;
239

    
240
      return NEAREST*tmp;
241
      }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

    
245
    void finishRotationNow(EffectListener listener)
246
      {
247
      boolean first = true;
248
      float startingAngle = mRotationAngleStatic.get1();
249
      int nearestAngleInDegrees = computeNearestAngle(startingAngle);
250

    
251
      mRotationAngleFinal.set1(nearestAngleInDegrees);
252
      mRotationAngleMiddle.set1( nearestAngleInDegrees + (nearestAngleInDegrees-startingAngle)*0.2f );
253

    
254
      for(int x=0; x<mSize; x++)
255
        for(int y=0; y<mSize; y++)
256
          for(int z=0; z<mSize; z++)
257
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
258
              {
259
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
260
                {
261
                mRotationAngle[x][y][z].makeRunNowFor(POST_ROTATION_MILLISEC);
262
                mRotationAngle[x][y][z].add(mRotationAngleMiddle);
263
                mRotationAngle[x][y][z].add(mRotationAngleFinal);
264

    
265
                if( first )
266
                  {
267
                  first = false;
268
                  mEffectsListeningForNow = mEffects[x][y][z];
269
                  mEffectsListeningForNow.registerForMessages(listener);
270
                  }
271
                }
272
              }
273
      }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276

    
277
    void removeRotationNow(EffectListener listener)
278
      {
279
      mEffectsListeningForNow.deregisterForMessages(listener);
280

    
281
      int nearestAngleInDegrees = computeNearestAngle(mRotationAngleStatic.get1());
282
      double nearestAngleInRadians = nearestAngleInDegrees*Math.PI/180;
283
      float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
284
      float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
285

    
286
      mRotationAngleStatic.set1(0);
287

    
288
      float qx=0,qy=0,qz=0;
289

    
290
      switch(mRotAxis)
291
        {
292
        case RubikSurfaceView.VECTX: qx=1; break;
293
        case RubikSurfaceView.VECTY: qy=1; break;
294
        case RubikSurfaceView.VECTZ: qz=1; break;
295
        }
296

    
297
      Static4D quat = new Static4D(qx*sinA, qy*sinA, qz*sinA, cosA);
298

    
299
      for(int x=0; x<mSize; x++)
300
        for(int y=0; y<mSize; y++)
301
          for(int z=0; z<mSize; z++)
302
            if( x==0 || x==mSize-1 || y==0 || y==mSize-1 || z==0 || z==mSize-1 )
303
              {
304
              if( belongsToRotation(x,y,z,mRotAxis,mRotRow) )
305
                {
306
                mRotationAngle[x][y][z].makeRunNowFor(0);
307
                mRotationAngle[x][y][z].removeAll();
308
                mQuatScramble[x][y][z].set(RubikSurfaceView.quatMultiply(quat,mQuatScramble[x][y][z]));
309
                modifyCurrentPosition(x,y,z,quat);
310
                }
311
              }
312
      }
313

    
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315

    
316
    private float getSinkStrength()
317
      {
318
      switch(mSize)
319
        {
320
        case 1 : return 1.1f;
321
        case 2 : return 1.5f;
322
        case 3 : return 1.8f;
323
        case 4 : return 2.0f;
324
        default: return 3.0f - 4.0f/mSize;
325
        }
326
      }
327

    
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329

    
330
    private boolean belongsToRotation(int x, int y, int z, int vector, int row)
331
      {
332
      switch(vector)
333
        {
334
        case RubikSurfaceView.VECTX: return mCurrentPosition[x][y][z].get1()==row;
335
        case RubikSurfaceView.VECTY: return mCurrentPosition[x][y][z].get2()==row;
336
        case RubikSurfaceView.VECTZ: return mCurrentPosition[x][y][z].get3()==row;
337
        }
338

    
339
      return false;
340
      }
341

    
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343

    
344
    private void modifyCurrentPosition(int x, int y, int z, Static4D quat)
345
      {
346
      Static3D current = mCurrentPosition[x][y][z];
347
      float diff = 0.5f*(mSize-1);
348
      float cubitCenterX = current.get1() - diff;
349
      float cubitCenterY = current.get2() - diff;
350
      float cubitCenterZ = current.get3() - diff;
351

    
352
      Static4D cubitCenter =  new Static4D(cubitCenterX, cubitCenterY, cubitCenterZ, 0);
353
      Static4D rotatedCenter = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat);
354

    
355
      float rotatedX = rotatedCenter.get1() + diff;
356
      float rotatedY = rotatedCenter.get2() + diff;
357
      float rotatedZ = rotatedCenter.get3() + diff;
358

    
359
      int roundedX = (int)(rotatedX+0.1f);
360
      int roundedY = (int)(rotatedY+0.1f);
361
      int roundedZ = (int)(rotatedZ+0.1f);
362

    
363
      mCurrentPosition[x][y][z].set1(roundedX);
364
      mCurrentPosition[x][y][z].set2(roundedY);
365
      mCurrentPosition[x][y][z].set3(roundedZ);
366
      }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369

    
370
    void createTexture()
371
      {
372
      Bitmap bitmap;
373

    
374
      final int S = 128;
375
      final int W = 3*S;
376
      final int H = 2*S;
377
      final int R = S/10;
378
      final int M = S/20;
379

    
380
      Paint paint = new Paint();
381
      bitmap = Bitmap.createBitmap(W,H, Bitmap.Config.ARGB_8888);
382
      Canvas canvas = new Canvas(bitmap);
383

    
384
      paint.setAntiAlias(true);
385
      paint.setTextAlign(Paint.Align.CENTER);
386
      paint.setStyle(Paint.Style.FILL);
387

    
388
      // 3x2 bitmap = 6 squares:
389
      //
390
      // RED     GREEN   BLUE
391
      // YELLOW  WHITE   BROWN
392

    
393
      paint.setColor(0xff000000);                                  // BLACK BACKGROUND
394
      canvas.drawRect(0, 0, W, H, paint);                          //
395

    
396
      paint.setColor(0xffff0000);                                  // RED
397
      canvas.drawRoundRect(    M,   M,   S-M,   S-M, R, R, paint); //
398
      paint.setColor(0xff00ff00);                                  // GREEN
399
      canvas.drawRoundRect(  S+M,   M, 2*S-M,   S-M, R, R, paint); //
400
      paint.setColor(0xff0000ff);                                  // BLUE
401
      canvas.drawRoundRect(2*S+M,   M, 3*S-M,   S-M, R, R, paint); //
402
      paint.setColor(0xffffff00);                                  // YELLOW
403
      canvas.drawRoundRect(    M, S+M,   S-M, 2*S-M, R, R, paint); //
404
      paint.setColor(0xffffffff);                                  // WHITE
405
      canvas.drawRoundRect(  S+M, S+M, 2*S-M, 2*S-M, R, R, paint); //
406
      paint.setColor(0xffb5651d);                                  // BROWN
407
      canvas.drawRoundRect(2*S+M, S+M, 3*S-M, 2*S-M, R, R, paint); //
408

    
409
      mTexture.setTexture(bitmap);
410
      }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413

    
414
    float getTextureSize()
415
      {
416
      return TEXTURE_SIZE;
417
      }
418

    
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420

    
421
    int getSize()
422
      {
423
      return mSize;
424
      }
425
}
(2-2/4)