Project

General

Profile

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

magiccube / src / main / java / org / distorted / object / RubikObject.java @ 97d2f701

1
///////////////////////////////////////////////////////////////////////////////////////////////////
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
package org.distorted.object;
21

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

    
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
import org.distorted.library.mesh.MeshBase;
35
import org.distorted.library.mesh.MeshRectangles;
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
import static org.distorted.magic.RubikRenderer.NODE_FBO_SIZE;
43

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

    
46
public abstract class RubikObject extends DistortedNode
47
  {
48
  static final int TEXTURE_HEIGHT = 128;
49
  static final float OBJECT_SCREEN_RATIO = 0.5f;
50
  final float[] LEGAL_QUATS;
51
  final Static3D[] ROTATION_AXIS;
52

    
53
  private static final int POST_ROTATION_MILLISEC = 500;
54
  private final int NUM_CUBITS;
55
  private int mRotRow;
56
  private int mRotAxis;
57
  private Static3D mScale, mNodeScale;
58
  private Static4D mQuatAccumulated;
59
  private Cubit[] mCubits;
60

    
61
  float mStart, mStep;
62
  int mSize;
63

    
64
  Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
65
  DistortedTexture mTexture;
66

    
67
  VertexEffectSink mSinkEffect;
68
  MatrixEffectScale mScaleEffect;
69
  MatrixEffectQuaternion mQuatCEffect;
70
  MatrixEffectQuaternion mQuatAEffect;
71

    
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73

    
74
  RubikObject(int size, Static4D quatCur, Static4D quatAcc, DistortedTexture nodeTexture, MeshRectangles nodeMesh, DistortedEffects nodeEffects)
75
    {
76
    super(nodeTexture,nodeEffects,nodeMesh);
77

    
78
    resizeFBO(NODE_FBO_SIZE, NODE_FBO_SIZE);
79

    
80
    Static3D[] positions = getCubitPositions(size);
81

    
82
    LEGAL_QUATS = getLegalQuats();
83
    NUM_CUBITS  = positions.length;
84
    ROTATION_AXIS = getRotationAxis();
85

    
86
    mSize = size;
87

    
88
    computeStartAndStep(positions);
89

    
90
    mRotationAngleStatic = new Static1D(0);
91
    mRotationAngleMiddle = new Static1D(0);
92
    mRotationAngleFinal  = new Static1D(0);
93

    
94
    mScale    = new Static3D(1,1,1);
95
    mNodeScale= new Static3D(1,1,1);
96

    
97
    mQuatAccumulated = quatAcc;
98

    
99
    Static3D center = new Static3D(0,0,0);
100
    Static4D region = new Static4D(0,0,0,0.72f);
101

    
102
    mSinkEffect = new VertexEffectSink( new Static1D(getSinkStrength()), center, region );
103
    mScaleEffect = new MatrixEffectScale(mScale);
104
    mQuatCEffect = new MatrixEffectQuaternion(quatCur, center);
105
    mQuatAEffect = new MatrixEffectQuaternion(quatAcc, center);
106

    
107
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
108
    nodeEffects.apply(nodeScaleEffect);
109

    
110
    mCubits = new Cubit[NUM_CUBITS];
111
    mTexture = new DistortedTexture();
112

    
113
    int vertices = (int)(24.0f/mSize + 2.0f);
114

    
115
    for(int i=0; i<NUM_CUBITS; i++)
116
      {
117
      MeshBase cubitMesh = createCubitMesh(vertices);
118
      mCubits[i] = new Cubit(this,cubitMesh,positions[i]);
119
      textureCubitMesh(cubitMesh,i);
120

    
121
      attach(mCubits[i].mNode);
122
      }
123
    }
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  private void textureCubitMesh(MeshBase mesh, int cubit)
128
    {
129
    boolean belongs;
130
    final int numFaces = getNumFaces();
131
    final Static4D[] maps = new Static4D[numFaces];
132
    final float ratio = 1.0f/(numFaces+1);
133

    
134
    if( 2*ROTATION_AXIS.length == numFaces )  // i.e. there are faces on both ends of the axis (cube)
135
      {
136
      for(int i=0; i<numFaces; i++)
137
        {
138
        belongs = belongsToRotation(cubit, i/2, i%2==0 ? mSize-1:0 );
139
        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
140
        }
141
      }
142
    else if( ROTATION_AXIS.length == numFaces )  // just a single face on the right end of an axis (pyraminx)
143
      {
144
      for(int i=0; i<numFaces; i++)
145
        {
146
        belongs = belongsToRotation(cubit, i, mSize-1 );
147
        maps[i] = new Static4D( (belongs?i:6)*ratio, 0.0f, ratio, 1.0f);
148
        }
149
      }
150

    
151
    mesh.setTextureMap(maps);
152
    }
153

    
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155
// Cast centers of all Cubits on the first rotation Axis and compute the leftmost and rightmost
156
// one. From there compute the 'start' (i.e. the leftmost) and 'step' (i.e. distance between two
157
// consecutive).
158
// it is assumed that other rotation axis have the same 'start' and 'step' - this is the case with
159
// the Cube and the Pyraminx.
160
// Start and Step are then needed to compute which rotation row (with respect to a given axis) a
161
// given Cubit belongs to.
162

    
163
  private void computeStartAndStep(Static3D[] pos)
164
    {
165
    float min = Float.MAX_VALUE;
166
    float max = Float.MIN_VALUE;
167
    float axisX = ROTATION_AXIS[0].get0();
168
    float axisY = ROTATION_AXIS[0].get1();
169
    float axisZ = ROTATION_AXIS[0].get2();
170
    float tmp;
171

    
172
    for(int i=0; i<NUM_CUBITS; i++)
173
      {
174
      tmp = pos[i].get0()*axisX + pos[i].get1()*axisY + pos[i].get2()*axisZ;
175
      if( tmp<min ) min=tmp;
176
      if( tmp>max ) max=tmp;
177
      }
178

    
179
    mStart = min;
180
    mStep  = (max-min+1.0f)/mSize;
181
    }
182

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

    
185
  private boolean belongsToRotation( int cubit, int axis, int row)
186
    {
187
    return mCubits[cubit].mRotationRow[axis]==row;
188
    }
189

    
190
///////////////////////////////////////////////////////////////////////////////////////////////////
191

    
192
  private void resetRotationAngle(Dynamic1D rotationAngle)
193
    {
194
    rotationAngle.setDuration(POST_ROTATION_MILLISEC);
195
    rotationAngle.resetToBeginning();
196
    rotationAngle.removeAll();
197
    rotationAngle.add(mRotationAngleStatic);
198
    rotationAngle.add(mRotationAngleMiddle);
199
    rotationAngle.add(mRotationAngleFinal);
200
    }
201

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

    
204
  private float getSinkStrength()
205
    {
206
    switch(mSize)
207
      {
208
      case 1 : return 1.1f;
209
      case 2 : return 1.5f;
210
      case 3 : return 1.8f;
211
      case 4 : return 2.0f;
212
      default: return 3.0f - 4.0f/mSize;
213
      }
214
    }
215

    
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217
// the getFaceColors + final black in a horizontal strip.
218

    
219
  public void createTexture()
220
    {
221
    Bitmap bitmap;
222

    
223
    final int numColors = getNumFaces();
224

    
225
    Paint paint = new Paint();
226
    bitmap = Bitmap.createBitmap( (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
227
    Canvas canvas = new Canvas(bitmap);
228

    
229
    paint.setAntiAlias(true);
230
    paint.setTextAlign(Paint.Align.CENTER);
231
    paint.setStyle(Paint.Style.FILL);
232

    
233
    paint.setColor(0xff000000);
234
    canvas.drawRect(0, 0, (numColors+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
235

    
236
    for(int i=0; i<numColors; i++)
237
      {
238
      createFaceTexture(canvas,paint,i);
239
      }
240

    
241
    mTexture.setTexture(bitmap);
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  public int getSize()
247
    {
248
    return mSize;
249
    }
250

    
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252

    
253
  public void continueRotation(float angleInDegrees)
254
    {
255
    mRotationAngleStatic.set0(angleInDegrees);
256
    }
257

    
258
///////////////////////////////////////////////////////////////////////////////////////////////////
259

    
260
  public Static4D getRotationQuat()
261
      {
262
      return mQuatAccumulated;
263
      }
264

    
265
///////////////////////////////////////////////////////////////////////////////////////////////////
266

    
267
  public void recomputeScaleFactor(int scrWidth, int scrHeight)
268
    {
269
    float factor = scrWidth>scrHeight ? scrHeight : scrWidth;
270
    float scaleFactor = OBJECT_SCREEN_RATIO*NODE_FBO_SIZE/mSize;
271

    
272
    mNodeScale.set(factor,factor,factor);
273
    mScale.set(scaleFactor,scaleFactor,scaleFactor);
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

    
278
  public void savePreferences(SharedPreferences.Editor editor)
279
    {
280
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].savePreferences(editor);
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  public void restorePreferences(SharedPreferences preferences)
286
    {
287
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].restorePreferences(preferences);
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291

    
292
  public long finishRotationNow(EffectListener listener)
293
    {
294
    boolean first = true;
295
    long effectID=0;
296

    
297
    for(int i=0; i<NUM_CUBITS; i++)
298
      {
299
      if( belongsToRotation(i,mRotAxis,mRotRow) )
300
        {
301
        if( first )
302
          {
303
          first=false;
304
          effectID = mCubits[i].finishRotationNow(listener);
305
          }
306
        resetRotationAngle(mCubits[i].mRotationAngle);
307
        }
308
      }
309

    
310
    return effectID;
311
    }
312

    
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314

    
315
  public void releaseResources()
316
    {
317
    mTexture.markForDeletion();
318

    
319
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].releaseResources();
320
    }
321

    
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323

    
324
  public void apply(Effect effect, int position)
325
    {
326
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].mEffect.apply(effect, position);
327
    }
328

    
329
///////////////////////////////////////////////////////////////////////////////////////////////////
330

    
331
  public void remove(long effectID)
332
    {
333
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].mEffect.abortById(effectID);
334
    }
335

    
336
///////////////////////////////////////////////////////////////////////////////////////////////////
337

    
338
  public void solve()
339
    {
340
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].solve();
341
    }
342

    
343
///////////////////////////////////////////////////////////////////////////////////////////////////
344

    
345
  public boolean isSolved()
346
    {
347
    Static4D q = mCubits[0].mQuatScramble;
348

    
349
    float x = q.get0();
350
    float y = q.get1();
351
    float z = q.get2();
352
    float w = q.get3();
353

    
354
    for(int i=0; i<NUM_CUBITS; i++)
355
      {
356
      q = mCubits[i].mQuatScramble;
357

    
358
      if( q.get0()!=x || q.get1()!=y || q.get2()!=z || q.get3()!=w ) return false;
359
      }
360

    
361
    return true;
362
    }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365

    
366
  public void beginNewRotation(int axis, int row )
367
    {
368
    mRotAxis = axis;
369
    mRotRow  = row;
370

    
371
    mRotationAngleStatic.set0(0.0f);
372

    
373
    for(int i=0; i<NUM_CUBITS; i++)
374
      if( belongsToRotation(i,mRotAxis,mRotRow) )
375
        {
376
        mCubits[i].beginNewRotation(axis);
377
        }
378
     }
379

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

    
382
  public long addNewRotation( int axis, int row, int angle, long durationMillis, EffectListener listener )
383
     {
384
     long effectID=0;
385
     boolean first = true;
386

    
387
     mRotAxis = axis;
388
     mRotRow  = row;
389

    
390
     mRotationAngleStatic.set0(0.0f);
391

    
392
     for(int i=0; i<NUM_CUBITS; i++)
393
       if( belongsToRotation(i,mRotAxis,mRotRow) )
394
         {
395
         mCubits[i].addNewRotation(axis,durationMillis,angle);
396

    
397
         if( first )
398
           {
399
           first = false;
400
           effectID = mCubits[i].setUpCallback(listener);
401
           }
402
         }
403

    
404
     return effectID;
405
     }
406

    
407
///////////////////////////////////////////////////////////////////////////////////////////////////
408

    
409
  public void removeRotationNow()
410
     {
411
     boolean first = true;
412
     Static4D quat = null;
413

    
414
     for(int i=0; i<NUM_CUBITS; i++)
415
       if( belongsToRotation(i,mRotAxis,mRotRow) )
416
         {
417
         if( first )
418
           {
419
           first = false;
420
           quat = mCubits[i].returnRotationQuat(mRotAxis);
421
           }
422

    
423
         mCubits[i].removeRotationNow(quat);
424
         }
425

    
426
     mRotationAngleStatic.set0(0);
427
     }
428

    
429
///////////////////////////////////////////////////////////////////////////////////////////////////
430

    
431
  public int getNumAxis()
432
    {
433
    return ROTATION_AXIS.length;
434
    }
435

    
436
///////////////////////////////////////////////////////////////////////////////////////////////////
437

    
438
  abstract Static3D[] getCubitPositions(int size);
439
  abstract float[] getLegalQuats();
440
  abstract int getNumFaces();
441
  abstract void createFaceTexture(Canvas canvas, Paint paint, int face);
442
  abstract MeshBase createCubitMesh(int vertices);
443
  abstract Static3D[] getRotationAxis();
444
  public abstract int getBasicAngle();
445
  }
(4-4/7)