Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / RubikObject.java @ 39e74052

1 fdec60a3 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube 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
// Magic Cube 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 Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19
20 4f9f99a2 Leszek Koltunski
package org.distorted.object;
21 fdec60a3 Leszek Koltunski
22 27a70eae Leszek Koltunski
import android.content.SharedPreferences;
23 411c6285 Leszek Koltunski
import android.graphics.Bitmap;
24
import android.graphics.Canvas;
25
import android.graphics.Paint;
26 27a70eae Leszek Koltunski
27
import org.distorted.library.effect.Effect;
28
import org.distorted.library.effect.MatrixEffectQuaternion;
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.DistortedTexture;
34 b32444ee Leszek Koltunski
import org.distorted.library.mesh.MeshBase;
35 97c012ae Leszek Koltunski
import org.distorted.library.mesh.MeshRectangles;
36 27a70eae Leszek Koltunski
import org.distorted.library.message.EffectListener;
37 f16ff19d Leszek Koltunski
import org.distorted.library.type.Dynamic1D;
38 27a70eae Leszek Koltunski
import org.distorted.library.type.Static1D;
39
import org.distorted.library.type.Static3D;
40
import org.distorted.library.type.Static4D;
41 4f9f99a2 Leszek Koltunski
42 ba740a0c Leszek Koltunski
import static org.distorted.magic.RubikRenderer.NODE_FBO_SIZE;
43 d41742f7 Leszek Koltunski
44 0333d81e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
45
46 27a70eae Leszek Koltunski
public abstract class RubikObject extends DistortedNode
47 fdec60a3 Leszek Koltunski
  {
48 411c6285 Leszek Koltunski
  static final int TEXTURE_HEIGHT = 128;
49 a10ada2a Leszek Koltunski
  final float[] LEGAL_QUATS;
50 efef689c Leszek Koltunski
  final Static3D[] ROTATION_AXIS;
51 27a70eae Leszek Koltunski
52 f0fa83ae Leszek Koltunski
  static float OBJECT_SCREEN_RATIO;
53
54 f16ff19d Leszek Koltunski
  private static final int POST_ROTATION_MILLISEC = 500;
55 a10ada2a Leszek Koltunski
  private final int NUM_CUBITS;
56 3c4a326c Leszek Koltunski
  private int mRotRow;
57 efef689c Leszek Koltunski
  private int mRotAxis;
58 49f67f9b Leszek Koltunski
  private Static3D[] mOrigPos;
59 5ba13c05 Leszek Koltunski
  private Static3D mScale, mNodeScale;
60 27a70eae Leszek Koltunski
  private Static4D mQuatAccumulated;
61 b32444ee Leszek Koltunski
  private Cubit[] mCubits;
62 49f67f9b Leszek Koltunski
  private int mSize;
63 27a70eae Leszek Koltunski
64 e844c116 Leszek Koltunski
  float mStart, mStep;
65 fdec60a3 Leszek Koltunski
66 27a70eae Leszek Koltunski
  Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
67
  DistortedTexture mTexture;
68
69
  VertexEffectSink mSinkEffect;
70
  MatrixEffectScale mScaleEffect;
71
  MatrixEffectQuaternion mQuatCEffect;
72
  MatrixEffectQuaternion mQuatAEffect;
73
74
///////////////////////////////////////////////////////////////////////////////////////////////////
75 fdec60a3 Leszek Koltunski
76 411c6285 Leszek Koltunski
  RubikObject(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture, MeshRectangles nodeMesh, DistortedEffects nodeEffects)
77 fdec60a3 Leszek Koltunski
    {
78 411c6285 Leszek Koltunski
    super(nodeTexture,nodeEffects,nodeMesh);
79 fdec60a3 Leszek Koltunski
80 ba740a0c Leszek Koltunski
    resizeFBO(NODE_FBO_SIZE, NODE_FBO_SIZE);
81 d41742f7 Leszek Koltunski
82 49f67f9b Leszek Koltunski
    mOrigPos = getCubitPositions(size);
83 10a2e360 Leszek Koltunski
84 f16ff19d Leszek Koltunski
    LEGAL_QUATS = getLegalQuats();
85 49f67f9b Leszek Koltunski
    NUM_CUBITS  = mOrigPos.length;
86 efef689c Leszek Koltunski
    ROTATION_AXIS = getRotationAxis();
87 f0fa83ae Leszek Koltunski
    OBJECT_SCREEN_RATIO = getScreenRatio();
88 a10ada2a Leszek Koltunski
89 27a70eae Leszek Koltunski
    mSize = size;
90 49f67f9b Leszek Koltunski
    computeStartAndStep(mOrigPos);
91 e844c116 Leszek Koltunski
92 27a70eae Leszek Koltunski
    mRotationAngleStatic = new Static1D(0);
93
    mRotationAngleMiddle = new Static1D(0);
94
    mRotationAngleFinal  = new Static1D(0);
95
96
    mScale    = new Static3D(1,1,1);
97
    mNodeScale= new Static3D(1,1,1);
98
99
    mQuatAccumulated = quatAcc;
100
101 5ba13c05 Leszek Koltunski
    Static3D center = new Static3D(0,0,0);
102 27a70eae Leszek Koltunski
103 f0fa83ae Leszek Koltunski
    mSinkEffect  = getSink(mSize);
104 27a70eae Leszek Koltunski
    mScaleEffect = new MatrixEffectScale(mScale);
105 5ba13c05 Leszek Koltunski
    mQuatCEffect = new MatrixEffectQuaternion(quatCur, center);
106
    mQuatAEffect = new MatrixEffectQuaternion(quatAcc, center);
107 27a70eae Leszek Koltunski
108
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
109 411c6285 Leszek Koltunski
    nodeEffects.apply(nodeScaleEffect);
110 a10ada2a Leszek Koltunski
111
    mCubits = new Cubit[NUM_CUBITS];
112 5974d2ae Leszek Koltunski
    mTexture = new DistortedTexture();
113 a10ada2a Leszek Koltunski
114
    int vertices = (int)(24.0f/mSize + 2.0f);
115
116
    for(int i=0; i<NUM_CUBITS; i++)
117
      {
118 89a11f7b Leszek Koltunski
      MeshBase cubitMesh = createCubitMesh(i,vertices);
119 49f67f9b Leszek Koltunski
      mCubits[i] = new Cubit(this,cubitMesh,mOrigPos[i]);
120 efef689c Leszek Koltunski
      textureCubitMesh(cubitMesh,i);
121 a10ada2a Leszek Koltunski
122
      attach(mCubits[i].mNode);
123
      }
124 27a70eae Leszek Koltunski
    }
125
126 efef689c Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
127
128
  private void textureCubitMesh(MeshBase mesh, int cubit)
129
    {
130
    boolean belongs;
131
    final int numFaces = getNumFaces();
132
    final Static4D[] maps = new Static4D[numFaces];
133
    final float ratio = 1.0f/(numFaces+1);
134
135 97d2f701 Leszek Koltunski
    if( 2*ROTATION_AXIS.length == numFaces )  // i.e. there are faces on both ends of the axis (cube)
136 efef689c Leszek Koltunski
      {
137
      for(int i=0; i<numFaces; i++)
138
        {
139 66cbdd21 Leszek Koltunski
        belongs = isOnFace(cubit, i/2, i%2==0 ? mSize-1:0 );
140 efef689c Leszek Koltunski
        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
141
        }
142
      }
143 97d2f701 Leszek Koltunski
    else if( ROTATION_AXIS.length == numFaces )  // just a single face on the right end of an axis (pyraminx)
144 efef689c Leszek Koltunski
      {
145
      for(int i=0; i<numFaces; i++)
146
        {
147 66cbdd21 Leszek Koltunski
        belongs = isOnFace(cubit, i, 0 );
148 efef689c Leszek Koltunski
        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
149
        }
150
      }
151
152
    mesh.setTextureMap(maps);
153
    }
154
155 e844c116 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
156 97d2f701 Leszek Koltunski
// Cast centers of all Cubits on the first rotation Axis and compute the leftmost and rightmost
157
// one. From there compute the 'start' (i.e. the leftmost) and 'step' (i.e. distance between two
158
// consecutive).
159
// it is assumed that other rotation axis have the same 'start' and 'step' - this is the case with
160
// the Cube and the Pyraminx.
161
// Start and Step are then needed to compute which rotation row (with respect to a given axis) a
162
// given Cubit belongs to.
163 e844c116 Leszek Koltunski
164
  private void computeStartAndStep(Static3D[] pos)
165
    {
166
    float min = Float.MAX_VALUE;
167
    float max = Float.MIN_VALUE;
168
    float axisX = ROTATION_AXIS[0].get0();
169
    float axisY = ROTATION_AXIS[0].get1();
170
    float axisZ = ROTATION_AXIS[0].get2();
171
    float tmp;
172
173
    for(int i=0; i<NUM_CUBITS; i++)
174
      {
175
      tmp = pos[i].get0()*axisX + pos[i].get1()*axisY + pos[i].get2()*axisZ;
176
      if( tmp<min ) min=tmp;
177
      if( tmp>max ) max=tmp;
178
      }
179
180
    mStart = min;
181
    mStep  = (max-min+1.0f)/mSize;
182
    }
183
184 efef689c Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
185
186
  private boolean belongsToRotation( int cubit, int axis, int row)
187
    {
188 66cbdd21 Leszek Koltunski
    return ((int)(mCubits[cubit].mRotationRow[axis]+0.5f))==row;
189
    }
190
191
///////////////////////////////////////////////////////////////////////////////////////////////////
192
// we cannot use belongsToRotation for deciding if to texture a face. Counterexample: the 'rotated'
193
// tetrahedrons of Pyraminx nearby the edge: they belong to rotation but their face which is rotated
194
// away from the face of the Pyraminx shouldn't be textured.
195
196
  private boolean isOnFace( int cubit, int axis, int row)
197
    {
198
    final float MAX_ERROR = 0.0001f;
199
    float diff = mCubits[cubit].mRotationRow[axis] - row;
200
    return diff*diff < MAX_ERROR;
201 efef689c Leszek Koltunski
    }
202
203 f16ff19d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
204
205 a10ada2a Leszek Koltunski
  private void resetRotationAngle(Dynamic1D rotationAngle)
206 f16ff19d Leszek Koltunski
    {
207
    rotationAngle.setDuration(POST_ROTATION_MILLISEC);
208
    rotationAngle.resetToBeginning();
209
    rotationAngle.removeAll();
210
    rotationAngle.add(mRotationAngleStatic);
211
    rotationAngle.add(mRotationAngleMiddle);
212
    rotationAngle.add(mRotationAngleFinal);
213
    }
214
215 49f67f9b Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
216
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
217
218
  void clampPos(Static3D pos)
219
    {
220
    float currError, minError = Float.MAX_VALUE;
221
    int minErrorIndex= -1;
222
    float x = pos.get0();
223
    float y = pos.get1();
224
    float z = pos.get2();
225
    float xo,yo,zo;
226
227
    for(int i=0; i<NUM_CUBITS; i++)
228
      {
229
      xo = mOrigPos[i].get0();
230
      yo = mOrigPos[i].get1();
231
      zo = mOrigPos[i].get2();
232
233
      currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
234
235
      if( currError<minError )
236
        {
237
        minError = currError;
238
        minErrorIndex = i;
239
        }
240
      }
241
242
    pos.set( mOrigPos[minErrorIndex] );
243
    }
244
245 411c6285 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
246
// the getFaceColors + final black in a horizontal strip.
247
248
  public void createTexture()
249
    {
250
    Bitmap bitmap;
251
252
    final int numColors = getNumFaces();
253
254
    Paint paint = new Paint();
255
    bitmap = Bitmap.createBitmap( (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
256
    Canvas canvas = new Canvas(bitmap);
257
258
    paint.setAntiAlias(true);
259
    paint.setTextAlign(Paint.Align.CENTER);
260
    paint.setStyle(Paint.Style.FILL);
261
262
    paint.setColor(0xff000000);
263
    canvas.drawRect(0, 0, (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
264
265
    for(int i=0; i<numColors; i++)
266
      {
267
      createFaceTexture(canvas,paint,i);
268
      }
269
270
    mTexture.setTexture(bitmap);
271
    }
272
273 dd73fdab Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
274
275 27a70eae Leszek Koltunski
  public int getSize()
276 fdec60a3 Leszek Koltunski
    {
277 27a70eae Leszek Koltunski
    return mSize;
278 fdec60a3 Leszek Koltunski
    }
279
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
282 27a70eae Leszek Koltunski
  public void continueRotation(float angleInDegrees)
283 fdec60a3 Leszek Koltunski
    {
284 27a70eae Leszek Koltunski
    mRotationAngleStatic.set0(angleInDegrees);
285 fdec60a3 Leszek Koltunski
    }
286
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288
289 27a70eae Leszek Koltunski
  public Static4D getRotationQuat()
290
      {
291
      return mQuatAccumulated;
292
      }
293
294
///////////////////////////////////////////////////////////////////////////////////////////////////
295
296 5ba13c05 Leszek Koltunski
  public void recomputeScaleFactor(int scrWidth, int scrHeight)
297 fdec60a3 Leszek Koltunski
    {
298 f0fa83ae Leszek Koltunski
    float factor = Math.min(scrWidth,scrHeight);
299 ba740a0c Leszek Koltunski
    float scaleFactor = OBJECT_SCREEN_RATIO*NODE_FBO_SIZE/mSize;
300 27a70eae Leszek Koltunski
301 5ba13c05 Leszek Koltunski
    mNodeScale.set(factor,factor,factor);
302 27a70eae Leszek Koltunski
    mScale.set(scaleFactor,scaleFactor,scaleFactor);
303 fdec60a3 Leszek Koltunski
    }
304 27a70eae Leszek Koltunski
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306
307 a10ada2a Leszek Koltunski
  public void savePreferences(SharedPreferences.Editor editor)
308
    {
309
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].savePreferences(editor);
310
    }
311 f16ff19d Leszek Koltunski
312 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
313 27a70eae Leszek Koltunski
314 a10ada2a Leszek Koltunski
  public void restorePreferences(SharedPreferences preferences)
315
    {
316
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].restorePreferences(preferences);
317
    }
318 27a70eae Leszek Koltunski
319 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
320 74686c71 Leszek Koltunski
321 a10ada2a Leszek Koltunski
  public long finishRotationNow(EffectListener listener)
322
    {
323
    boolean first = true;
324
    long effectID=0;
325
326
    for(int i=0; i<NUM_CUBITS; i++)
327
      {
328 efef689c Leszek Koltunski
      if( belongsToRotation(i,mRotAxis,mRotRow) )
329 a10ada2a Leszek Koltunski
        {
330
        if( first )
331
          {
332
          first=false;
333
          effectID = mCubits[i].finishRotationNow(listener);
334
          }
335
        resetRotationAngle(mCubits[i].mRotationAngle);
336
        }
337
      }
338
339
    return effectID;
340
    }
341
342
///////////////////////////////////////////////////////////////////////////////////////////////////
343
344
  public void releaseResources()
345
    {
346
    mTexture.markForDeletion();
347
348
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].releaseResources();
349
    }
350
351
///////////////////////////////////////////////////////////////////////////////////////////////////
352
353
  public void apply(Effect effect, int position)
354
    {
355
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].mEffect.apply(effect, position);
356
    }
357
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359
360
  public void remove(long effectID)
361
    {
362
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].mEffect.abortById(effectID);
363
    }
364 74686c71 Leszek Koltunski
365 a10ada2a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
366
367
  public void solve()
368
    {
369
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].solve();
370
    }
371
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
374
  public boolean isSolved()
375
    {
376
    Static4D q = mCubits[0].mQuatScramble;
377
378
    float x = q.get0();
379
    float y = q.get1();
380
    float z = q.get2();
381
    float w = q.get3();
382
383
    for(int i=0; i<NUM_CUBITS; i++)
384
      {
385
      q = mCubits[i].mQuatScramble;
386
387
      if( q.get0()!=x || q.get1()!=y || q.get2()!=z || q.get3()!=w ) return false;
388
      }
389
390
    return true;
391
    }
392
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394
395 efef689c Leszek Koltunski
  public void beginNewRotation(int axis, int row )
396 a10ada2a Leszek Koltunski
    {
397 9cd7695f Leszek Koltunski
    if( axis<0 || axis>=ROTATION_AXIS.length )
398
      {
399
      android.util.Log.e("object", "invalid rotation axis: "+axis);
400
      return;
401
      }
402
    if( row<0 || row>=mSize )
403
      {
404
      android.util.Log.e("object", "invalid rotation row: "+row);
405
      return;
406
      }
407
408 39e74052 Leszek Koltunski
    //android.util.Log.e("object", "beginning new rotation: axis: "+axis+" row: "+row);
409
410 3c4a326c Leszek Koltunski
    mRotAxis = axis;
411 a10ada2a Leszek Koltunski
    mRotRow  = row;
412
413
    mRotationAngleStatic.set0(0.0f);
414
415
    for(int i=0; i<NUM_CUBITS; i++)
416 efef689c Leszek Koltunski
      if( belongsToRotation(i,mRotAxis,mRotRow) )
417 a10ada2a Leszek Koltunski
        {
418
        mCubits[i].beginNewRotation(axis);
419
        }
420
     }
421
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423
424 efef689c Leszek Koltunski
  public long addNewRotation( int axis, int row, int angle, long durationMillis, EffectListener listener )
425 a10ada2a Leszek Koltunski
     {
426
     long effectID=0;
427
     boolean first = true;
428
429 3c4a326c Leszek Koltunski
     mRotAxis = axis;
430 a10ada2a Leszek Koltunski
     mRotRow  = row;
431
432
     mRotationAngleStatic.set0(0.0f);
433
434
     for(int i=0; i<NUM_CUBITS; i++)
435 efef689c Leszek Koltunski
       if( belongsToRotation(i,mRotAxis,mRotRow) )
436 a10ada2a Leszek Koltunski
         {
437
         mCubits[i].addNewRotation(axis,durationMillis,angle);
438
439
         if( first )
440
           {
441
           first = false;
442
           effectID = mCubits[i].setUpCallback(listener);
443
           }
444
         }
445
446
     return effectID;
447
     }
448
449
///////////////////////////////////////////////////////////////////////////////////////////////////
450
451
  public void removeRotationNow()
452
     {
453
     boolean first = true;
454
     Static4D quat = null;
455
456
     for(int i=0; i<NUM_CUBITS; i++)
457 efef689c Leszek Koltunski
       if( belongsToRotation(i,mRotAxis,mRotRow) )
458 a10ada2a Leszek Koltunski
         {
459
         if( first )
460
           {
461
           first = false;
462 3c4a326c Leszek Koltunski
           quat = mCubits[i].returnRotationQuat(mRotAxis);
463 a10ada2a Leszek Koltunski
           }
464
465
         mCubits[i].removeRotationNow(quat);
466
         }
467
468
     mRotationAngleStatic.set0(0);
469
     }
470
471
///////////////////////////////////////////////////////////////////////////////////////////////////
472
473 e844c116 Leszek Koltunski
  public int getNumAxis()
474 10a2e360 Leszek Koltunski
    {
475
    return ROTATION_AXIS.length;
476
    }
477
478
///////////////////////////////////////////////////////////////////////////////////////////////////
479
480 f0fa83ae Leszek Koltunski
  abstract float getScreenRatio();
481
  abstract VertexEffectSink getSink(int size);
482 10a2e360 Leszek Koltunski
  abstract Static3D[] getCubitPositions(int size);
483 a10ada2a Leszek Koltunski
  abstract float[] getLegalQuats();
484 411c6285 Leszek Koltunski
  abstract int getNumFaces();
485
  abstract void createFaceTexture(Canvas canvas, Paint paint, int face);
486 89a11f7b Leszek Koltunski
  abstract MeshBase createCubitMesh(int cubit, int vertices);
487 efef689c Leszek Koltunski
  abstract Static3D[] getRotationAxis();
488 e844c116 Leszek Koltunski
  public abstract int getBasicAngle();
489 39e74052 Leszek Koltunski
  public abstract int returnRowFromOffset(float offset);
490 fdec60a3 Leszek Koltunski
  }