Project

General

Profile

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

magiccube / src / main / java / org / distorted / bandaged / BandagedCreatorRenderer.java @ f6bf4e77

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

    
22
import javax.microedition.khronos.egl.EGLConfig;
23
import javax.microedition.khronos.opengles.GL10;
24

    
25
import android.app.Activity;
26
import android.opengl.GLES31;
27
import android.opengl.GLSurfaceView;
28
import android.widget.Toast;
29

    
30
import org.distorted.dialogs.RubikDialogBandagedSave;
31
import org.distorted.library.effect.EffectType;
32
import org.distorted.library.effect.FragmentEffectBrightness;
33
import org.distorted.library.effect.PostprocessEffectBorder;
34
import org.distorted.library.effect.VertexEffectDeform;
35
import org.distorted.library.main.DistortedEffects;
36
import org.distorted.library.main.DistortedFramebuffer;
37
import org.distorted.library.main.DistortedLibrary;
38
import org.distorted.library.main.DistortedNode;
39
import org.distorted.library.main.DistortedScreen;
40

    
41
import org.distorted.library.main.InternalOutputSurface;
42
import org.distorted.library.mesh.MeshBase;
43
import org.distorted.library.type.Static1D;
44
import org.distorted.library.type.Static3D;
45
import org.distorted.library.type.Static4D;
46
import org.distorted.objectlib.json.JsonWriter;
47
import org.distorted.objectlib.main.ShapeHexahedron;
48
import org.distorted.objectlib.main.TwistyObject;
49
import org.distorted.objectlib.objects.TwistyBandagedGeneric;
50
import org.json.JSONException;
51

    
52
import java.io.File;
53
import java.io.FileNotFoundException;
54
import java.io.IOException;
55
import java.nio.ByteBuffer;
56
import java.nio.ByteOrder;
57

    
58
///////////////////////////////////////////////////////////////////////////////////////////////////
59

    
60
public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, DistortedLibrary.ExceptionListener
61
{
62
   public static final float BRIGHTNESS = 0.333f;
63
   private static final int RESET_DURATION = 1000;
64
   private static final float MAX_SIZE_CHANGE = 1.70f;
65
   private static final float MIN_SIZE_CHANGE = 0.50f;
66
   private static final float INIT_RATIO = 0.5f;
67

    
68
   private final BandagedCreatorView mView;
69
   private final DistortedScreen mScreen;
70
   private final Static3D mScale;
71
   private final Static4D mQuatT, mQuatA;
72
   private final int[] mObjSize;
73

    
74
   private BandagedCubit[] mCubits;
75
   private boolean mInitialPhase;
76
   private long mStartTime;
77
   private float mQuatX, mQuatY, mQuatZ, mQuatW;
78
   private int mX, mY, mZ, mNumCubits;
79
   private boolean mResetQuats, mSetQuatT, mResettingObject, mConnectingCubits, mCreatingCubits, mRescaling;
80
   private int mIndex1, mIndex2;
81
   private int mSaveIcon;
82
   private DistortedFramebuffer mFramebuffer;
83
   private String mPath;
84
   private boolean mCubitsCreated;
85
   private int mWidth, mHeight;
86
   private float mScaleValue, mObjectScreenRatio;
87

    
88
///////////////////////////////////////////////////////////////////////////////////////////////////
89

    
90
   BandagedCreatorRenderer(BandagedCreatorView v)
91
     {
92
     mQuatT = new Static4D(0,0,0,1);
93
     mQuatA = new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
94

    
95
     mView = v;
96

    
97
     mResetQuats       = false;
98
     mSetQuatT         = false;
99
     mResettingObject  = false;
100
     mConnectingCubits = false;
101
     mCubitsCreated    = false;
102
     mCreatingCubits   = false;
103
     mRescaling        = false;
104

    
105
     mObjectScreenRatio= INIT_RATIO;
106
     mSaveIcon = -1;
107
     mObjSize  = new int[3];
108

    
109
     mScreen = new DistortedScreen();
110
     mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
111
     mScale = new Static3D(1,1,1);
112
     }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
   private boolean isAdjacent(float[] pos1, float[] pos2)
117
     {
118
     int len1 = pos1.length/3;
119
     int len2 = pos2.length/3;
120

    
121
     for(int i=0; i<len1; i++)
122
       for(int j=0; j<len2; j++)
123
         {
124
         float d0 = pos1[3*i  ] - pos2[3*j  ];
125
         float d1 = pos1[3*i+1] - pos2[3*j+1];
126
         float d2 = pos1[3*i+2] - pos2[3*j+2];
127

    
128
         if( d0*d0 + d1*d1 + d2*d2 == 1 ) return true;
129
         }
130

    
131
     return false;
132
     }
133

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

    
136
   private int computeNumCubits(int x, int y, int z)
137
     {
138
     return ( x<=1 || y<=1 || z<=1 ) ? x*y*z : x*y*z-(x-2)*(y-2)*(z-2);
139
     }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
   private void createCubits()
144
     {
145
     mCubits = new BandagedCubit[mNumCubits];
146
     int c=0;
147

    
148
     float begX = 0.5f*(1-mX);
149
     float begY = 0.5f*(1-mY);
150
     float begZ = 0.5f*(1-mZ);
151

    
152
     for(int x=0; x<mX; x++)
153
       for(int y=0; y<mY; y++)
154
          for(int z=0; z<mZ; z++)
155
            if( x==0 || x==mX-1 || y==0 || y==mY-1 || z==0 || z==mZ-1 )
156
              {
157
              float[] pos = new float[] { begX+x,begY+y,begZ+z };
158
              mCubits[c] = new BandagedCubit(pos,mX,mY,mZ,mQuatT,mQuatA,mScale,false);
159
              c++;
160
              }
161

    
162
     mView.setCubits(mCubits,mX,mY,mZ);
163
     }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
   private void resetObject()
168
     {
169
     mView.resetCubits();
170

    
171
     for(int c=0; c<mNumCubits; c++)
172
       {
173
       if( !mCubits[c].isAttached() )
174
         {
175
         mCubits[c].attach();
176
         mCubits[c].setUnmarked();
177
         mScreen.attach(mCubits[c].getNode());
178
         }
179
       if( mCubits[c].getPosition().length>3 )
180
         {
181
         mCubits[c].reset(mScaleValue);
182
         }
183
       }
184
     }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187

    
188
   @Override
189
   public void onDrawFrame(GL10 glUnused)
190
     {
191
     long time = System.currentTimeMillis();
192
     mScreen.render(time);
193

    
194
     if( mSetQuatT )
195
       {
196
       mSetQuatT = false;
197
       mQuatT.set(mQuatX,mQuatY,mQuatZ,mQuatW);
198
       }
199

    
200
     if( mResetQuats )
201
       {
202
       mResetQuats = false;
203

    
204
       float qx = mQuatT.get0();
205
       float qy = mQuatT.get1();
206
       float qz = mQuatT.get2();
207
       float qw = mQuatT.get3();
208

    
209
       float rx = mQuatA.get0();
210
       float ry = mQuatA.get1();
211
       float rz = mQuatA.get2();
212
       float rw = mQuatA.get3();
213

    
214
       float tx = rw*qx - rz*qy + ry*qz + rx*qw;
215
       float ty = rw*qy + rz*qx + ry*qw - rx*qz;
216
       float tz = rw*qz + rz*qw - ry*qx + rx*qy;
217
       float tw = rw*qw - rz*qz - ry*qy - rx*qx;
218

    
219
       mQuatT.set(0f, 0f, 0f, 1f);
220
       mQuatA.set(tx, ty, tz, tw);
221
       }
222

    
223
     if( mResettingObject )
224
       {
225
       boolean done = continueResetting(time);
226
       if( done ) mResettingObject = false;
227
       }
228

    
229
     if( mSaveIcon>=0 )
230
       {
231
       renderIcon(time); // for some reason we need to call render() twice here, otherwise the
232
       mSaveIcon++;      // icon turns out black. Probably some problem with binding the texture.
233
       }
234
     if( mSaveIcon>=2 )
235
       {
236
       saveIcon();
237
       mSaveIcon = -1;
238
       }
239

    
240
     if( mConnectingCubits )
241
       {
242
       mConnectingCubits = false;
243
       tryConnectingCubits(mIndex1,mIndex2);
244
       }
245

    
246
     if( mCreatingCubits )
247
       {
248
       mCreatingCubits = false;
249

    
250
       rescaleObject();
251

    
252
       if( mCubitsCreated )
253
         {
254
         createCubits();
255

    
256
         mScreen.detachAll();
257
         mView.resetCubits();
258

    
259
         for(int i=0; i<mNumCubits; i++)
260
           {
261
           mCubits[i].scaleMove(mScaleValue);
262
           DistortedNode node = mCubits[i].getNode();
263
           mScreen.attach(node);
264
           }
265
         }
266
       }
267

    
268
     if( mRescaling )
269
       {
270
       mRescaling = false;
271
       rescaleObject();
272
       for(int i=0; i<mNumCubits; i++) mCubits[i].scaleMove(mScaleValue);
273
       BandagedCreatorTouchControl control = mView.getTouchControl();
274
       control.setObjectRatio(mObjectScreenRatio);
275
       }
276
     }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279

    
280
   @Override
281
   public void onSurfaceChanged(GL10 glUnused, int width, int height)
282
      {
283
      mWidth = width;
284
      mHeight= height;
285
      rescaleObject();
286

    
287
      mScreen.detachAll();
288
      int touched = mView.getTouched();
289

    
290
      for(int i=0; i<mNumCubits; i++)
291
        if( mCubits[i].isAttached() )
292
          {
293
          mCubits[i].scaleMove(mScaleValue);
294
          if( touched==i ) mCubits[i].setMarked();
295
          else             mCubits[i].setUnmarked();
296
          DistortedNode node = mCubits[i].getNode();
297
          mScreen.attach(node);
298
          }
299

    
300
      mView.setScreenSize(width,height);
301
      mScreen.resize(width,height);
302
      }
303

    
304
///////////////////////////////////////////////////////////////////////////////////////////////////
305

    
306
   @Override
307
   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
308
      {
309
      DistortedLibrary.setMax(EffectType.VERTEX,25);
310
      MeshBase.setMaxEffComponents(120);
311

    
312
      VertexEffectDeform.enable();
313
      FragmentEffectBrightness.enable();
314

    
315
      DistortedLibrary.onSurfaceCreated(mView.getContext(),this,1);
316
      DistortedLibrary.setCull(true);
317

    
318
      createCubits();
319
      mCubitsCreated = true;
320
      }
321

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

    
324
   public void distortedException(Exception ex)
325
     {
326
     android.util.Log.e("CREATOR", "unexpected exception: "+ex.getMessage() );
327
     }
328

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

    
331
   public BandagedCubit[] getCubits()
332
     {
333
     return mCubits;
334
     }
335

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

    
338
   public DistortedScreen getScreen()
339
     {
340
     return mScreen;
341
     }
342

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

    
345
   void setConnecting(int index1, int index2)
346
     {
347
     mIndex1 = index1;
348
     mIndex2 = index2;
349
     mConnectingCubits = true;
350
     }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

    
354
   private void tryConnectingCubits(int index1, int index2)
355
     {
356
     if( index1!=index2 )
357
       {
358
       float[] pos1 = mCubits[index1].getPosition();
359
       float[] pos2 = mCubits[index2].getPosition();
360

    
361
       if( isAdjacent(pos1,pos2) )
362
         {
363
         mCubits[index2].join(pos1,mScaleValue);
364
         mCubits[index1].detach();
365
         mScreen.detach(mCubits[index1].getNode());
366
         }
367
       }
368
     }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371

    
372
   public Static4D getQuatAccu()
373
     {
374
     return mQuatA;
375
     }
376

    
377
///////////////////////////////////////////////////////////////////////////////////////////////////
378

    
379
   public void setQuatTemp(float x, float y, float z, float w)
380
     {
381
     mQuatX = x;
382
     mQuatY = y;
383
     mQuatZ = z;
384
     mQuatW = w;
385

    
386
     mSetQuatT = true;
387
     }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390

    
391
   public void resetQuats()
392
     {
393
     mResetQuats = true;
394
     }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
   public boolean isBusy()
399
     {
400
     return mResettingObject;
401
     }
402

    
403
///////////////////////////////////////////////////////////////////////////////////////////////////
404

    
405
   public void saveObject()
406
     {
407
     int numAttached=0;
408

    
409
     for(int i=0; i<mNumCubits; i++)
410
       if( mCubits[i].isAttached() ) numAttached++;
411

    
412
     float[][] pos = new float[numAttached][];
413
     int attached=0;
414

    
415
     for(int i=0; i<mNumCubits; i++)
416
       if( mCubits[i].isAttached() )
417
         {
418
         pos[attached++] = mCubits[i].getPosition();
419
         }
420

    
421
     TwistyBandagedGeneric.setPositions(pos);
422
     TwistyObject obj = new TwistyBandagedGeneric( mObjSize, TwistyObject.MESH_NICE, TwistyObject.MODE_NORM,
423
                                                   new Static4D(0,0,0,1), new Static3D(0,0,0), 1.0f, null );
424
     String name = obj.getShortName();
425
     BandagedCreatorActivity act = (BandagedCreatorActivity) mView.getContext();
426

    
427
     if( act.objectDoesntExist(name) && createObjectJson(obj,act) )
428
       {
429
       setupIconCreation(act);
430
       act.addObject(obj.getShortName());
431
       }
432
     }
433

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

    
436
   private boolean createObjectJson(TwistyObject object, Activity act)
437
     {
438
     final String name = object.getShortName()+"_object.json";
439
     File file = new File(act.getFilesDir(), name);
440
     String filename = file.getAbsolutePath();
441

    
442
     try
443
       {
444
       JsonWriter writer = JsonWriter.getInstance();
445
       String json = writer.createObjectString(object,24);
446
       writer.write(filename,json);
447
       return true;
448
       }
449
     catch(JSONException ex)
450
       {
451
       act.runOnUiThread(new Runnable()
452
         {
453
         public void run()
454
           {
455
           String message = "JSON Exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
456
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
457
           }
458
         });
459

    
460
       return false;
461
       }
462
     catch(FileNotFoundException ex)
463
       {
464
       act.runOnUiThread(new Runnable()
465
         {
466
         public void run()
467
           {
468
           String message = "FileNotFound exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
469
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
470
           }
471
         });
472

    
473
       return false;
474
       }
475
     catch(IOException ex)
476
       {
477
       act.runOnUiThread(new Runnable()
478
         {
479
         public void run()
480
           {
481
           String message = "IO exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
482
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
483
           }
484
         });
485

    
486
       return false;
487
       }
488
     }
489

    
490
///////////////////////////////////////////////////////////////////////////////////////////////////
491

    
492
   private void setupIconCreation(Activity act)
493
     {
494
     final float R=1.0f;
495
     final int FBO_WIDTH  = (int)(R*240);
496
     final int FBO_HEIGHT = (int)(R*360);
497
     final float OBJECT_SIZE = R*0.38f;
498

    
499
     TwistyObject obj = new TwistyBandagedGeneric( mObjSize, TwistyObject.MESH_NICE, TwistyObject.MODE_ICON,
500
                                                   ShapeHexahedron.DEFAULT_ROT, new Static3D(0,0,0), OBJECT_SIZE, null );
501

    
502
     DistortedEffects effects = obj.getObjectEffects();
503
     DistortedNode node = obj.getNode();
504

    
505
     if( mFramebuffer==null )
506
       {
507
       mFramebuffer = new DistortedFramebuffer(FBO_WIDTH,FBO_HEIGHT,1, InternalOutputSurface.DEPTH_NO_STENCIL);
508
       mFramebuffer.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
509
       }
510

    
511
     mFramebuffer.detachAll();
512
     mFramebuffer.attach(node);
513

    
514
     Static1D halo = new Static1D(5);
515
     Static4D color = new Static4D(0,0,0,1);
516
     PostprocessEffectBorder border = new PostprocessEffectBorder(halo,color);
517
     border.setHaloDepth(false);
518
     effects.apply(border);
519

    
520
     final String name = obj.getShortName()+".png";
521
     File file = new File(act.getFilesDir(), name);
522
     String filename = file.getAbsolutePath();
523

    
524
     mSaveIcon = 0;
525
     mPath = filename;
526
     }
527

    
528
///////////////////////////////////////////////////////////////////////////////////////////////////
529

    
530
   private void renderIcon(long time)
531
     {
532
     mFramebuffer.render(time);
533
     }
534

    
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536

    
537
   private void saveIcon()
538
     {
539
     int fW = mFramebuffer.getWidth();
540
     int fH = mFramebuffer.getHeight();
541

    
542
     ByteBuffer buf = ByteBuffer.allocateDirect(fW*fH*4);
543
     buf.order(ByteOrder.LITTLE_ENDIAN);
544

    
545
     mFramebuffer.setAsReadFramebuffer(0);
546
     GLES31.glReadBuffer(GLES31.GL_COLOR_ATTACHMENT0);
547
     GLES31.glReadPixels( 0, 0, fW, fH, GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, buf);
548
     BandagedCreatorWorkerThread.newBuffer(buf,fW,fH,6,mPath);
549
     GLES31.glBindFramebuffer(GLES31.GL_READ_FRAMEBUFFER, 0);
550

    
551
     mSaveIcon = -1;
552
     }
553

    
554
///////////////////////////////////////////////////////////////////////////////////////////////////
555

    
556
   void mulObjectRatio(float ratio)
557
     {
558
     mObjectScreenRatio *= ratio;
559

    
560
     if( mObjectScreenRatio>MAX_SIZE_CHANGE*INIT_RATIO) mObjectScreenRatio = MAX_SIZE_CHANGE*INIT_RATIO;
561
     if( mObjectScreenRatio<MIN_SIZE_CHANGE*INIT_RATIO) mObjectScreenRatio = MIN_SIZE_CHANGE*INIT_RATIO;
562

    
563
     mRescaling = true;
564
     }
565

    
566
///////////////////////////////////////////////////////////////////////////////////////////////////
567

    
568
   float getObjectRatio()
569
     {
570
     return mObjectScreenRatio;
571
     }
572

    
573
///////////////////////////////////////////////////////////////////////////////////////////////////
574

    
575
   private void rescaleObject()
576
     {
577
     final int size = mX>mY ? Math.max(mX, mZ) : Math.max(mY, mZ);
578
     final float Q = mObjectScreenRatio/size;
579
     mScaleValue = mWidth<mHeight ? Q*mWidth : Q*mHeight;
580
     mScale.set( mScaleValue,mScaleValue,mScaleValue );
581
     }
582

    
583
///////////////////////////////////////////////////////////////////////////////////////////////////
584

    
585
   public void changeObject(int x, int y, int z)
586
     {
587
     if( mX!=x || mY!=y || mZ!=z )
588
       {
589
       mX = x;
590
       mY = y;
591
       mZ = z;
592

    
593
       mObjSize[0] = mX;
594
       mObjSize[1] = mY;
595
       mObjSize[2] = mZ;
596

    
597
       mNumCubits = computeNumCubits(mX,mY,mZ);
598

    
599
       mCreatingCubits = true;
600
       }
601
     }
602

    
603
///////////////////////////////////////////////////////////////////////////////////////////////////
604

    
605
   public void displaySavingDialog()
606
     {
607
     BandagedCreatorActivity act = (BandagedCreatorActivity)mView.getContext();
608
     RubikDialogBandagedSave saveDiag = new RubikDialogBandagedSave();
609
     saveDiag.show(act.getSupportFragmentManager(), null);
610
     }
611

    
612
///////////////////////////////////////////////////////////////////////////////////////////////////
613

    
614
   public void setupReset()
615
     {
616
     mResettingObject = true;
617
     mInitialPhase    = true;
618
     mStartTime       = System.currentTimeMillis();
619
     }
620

    
621
///////////////////////////////////////////////////////////////////////////////////////////////////
622

    
623
   public boolean continueResetting(long time)
624
     {
625
     long diff = time-mStartTime;
626
     float quotient = ((float)diff)/RESET_DURATION;
627

    
628
     if( mInitialPhase && quotient>0.5f )
629
       {
630
       mInitialPhase=false;
631
       resetObject();
632
       }
633

    
634
     double angle = 2*Math.PI*quotient*quotient*(3-2*quotient);
635

    
636
     float sinA = (float)Math.sin(angle);
637
     float cosA = (float)Math.cos(angle);
638

    
639
     mQuatT.set(0, -sinA, 0, cosA);
640

    
641
     return quotient>1.0f;
642
     }
643

    
644
///////////////////////////////////////////////////////////////////////////////////////////////////
645

    
646
  public void touchCubit(int index)
647
    {
648
    mCubits[index].setMarked();
649
    }
650

    
651
///////////////////////////////////////////////////////////////////////////////////////////////////
652

    
653
  public void untouchCubit(int index)
654
    {
655
    mCubits[index].setUnmarked();
656
    }
657
}
(3-3/13)