Project

General

Profile

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

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

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
   static final float SCREEN_RATIO = 0.5f;
65

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

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

    
86
///////////////////////////////////////////////////////////////////////////////////////////////////
87

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

    
93
     mView = v;
94

    
95
     mResetQuats       = false;
96
     mSetQuatT         = false;
97
     mResettingObject  = false;
98
     mConnectingCubits = false;
99
     mCubitsCreated    = false;
100
     mCreatingCubits   = false;
101

    
102
     mSaveIcon = -1;
103
     mObjSize  = new int[3];
104

    
105
     mScreen = new DistortedScreen();
106
     mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
107
     mScale = new Static3D(1,1,1);
108
     }
109

    
110
///////////////////////////////////////////////////////////////////////////////////////////////////
111

    
112
   private boolean isAdjacent(float[] pos1, float[] pos2)
113
     {
114
     int len1 = pos1.length/3;
115
     int len2 = pos2.length/3;
116

    
117
     for(int i=0; i<len1; i++)
118
       for(int j=0; j<len2; j++)
119
         {
120
         float d0 = pos1[3*i  ] - pos2[3*j  ];
121
         float d1 = pos1[3*i+1] - pos2[3*j+1];
122
         float d2 = pos1[3*i+2] - pos2[3*j+2];
123

    
124
         if( d0*d0 + d1*d1 + d2*d2 == 1 ) return true;
125
         }
126

    
127
     return false;
128
     }
129

    
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131

    
132
   private int computeNumCubits(int x, int y, int z)
133
     {
134
     return ( x<=1 || y<=1 || z<=1 ) ? x*y*z : x*y*z-(x-2)*(y-2)*(z-2);
135
     }
136

    
137
///////////////////////////////////////////////////////////////////////////////////////////////////
138

    
139
   private void createCubits()
140
     {
141
     mCubits = new BandagedCubit[mNumCubits];
142
     int c=0;
143

    
144
     float begX = 0.5f*(1-mX);
145
     float begY = 0.5f*(1-mY);
146
     float begZ = 0.5f*(1-mZ);
147

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

    
158
     mView.setCubits(mCubits,mX,mY,mZ);
159
     }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
   private void resetObject()
164
     {
165
     mView.resetCubits();
166

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

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
   @Override
185
   public void onDrawFrame(GL10 glUnused)
186
     {
187
     long time = System.currentTimeMillis();
188
     mScreen.render(time);
189

    
190
     if( mSetQuatT )
191
       {
192
       mSetQuatT = false;
193
       mQuatT.set(mQuatX,mQuatY,mQuatZ,mQuatW);
194
       }
195

    
196
     if( mResetQuats )
197
       {
198
       mResetQuats = false;
199

    
200
       float qx = mQuatT.get0();
201
       float qy = mQuatT.get1();
202
       float qz = mQuatT.get2();
203
       float qw = mQuatT.get3();
204

    
205
       float rx = mQuatA.get0();
206
       float ry = mQuatA.get1();
207
       float rz = mQuatA.get2();
208
       float rw = mQuatA.get3();
209

    
210
       float tx = rw*qx - rz*qy + ry*qz + rx*qw;
211
       float ty = rw*qy + rz*qx + ry*qw - rx*qz;
212
       float tz = rw*qz + rz*qw - ry*qx + rx*qy;
213
       float tw = rw*qw - rz*qz - ry*qy - rx*qx;
214

    
215
       mQuatT.set(0f, 0f, 0f, 1f);
216
       mQuatA.set(tx, ty, tz, tw);
217
       }
218

    
219
     if( mResettingObject )
220
       {
221
       boolean done = continueResetting(time);
222
       if( done ) mResettingObject = false;
223
       }
224

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

    
236
     if( mConnectingCubits )
237
       {
238
       mConnectingCubits = false;
239
       tryConnectingCubits(mIndex1,mIndex2);
240
       }
241

    
242
     if( mCreatingCubits )
243
       {
244
       mCreatingCubits = false;
245

    
246
       rescaleObject();
247

    
248
       if( mCubitsCreated )
249
         {
250
         createCubits();
251

    
252
         mScreen.detachAll();
253
         mView.resetCubits();
254

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

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

    
267
   @Override
268
   public void onSurfaceChanged(GL10 glUnused, int width, int height)
269
      {
270
      mWidth = width;
271
      mHeight= height;
272
      rescaleObject();
273

    
274
      mScreen.detachAll();
275
      int touched = mView.getTouched();
276

    
277
      for(int i=0; i<mNumCubits; i++)
278
        if( mCubits[i].isAttached() )
279
          {
280
          mCubits[i].scaleMove(mScaleValue);
281
          if( touched==i ) mCubits[i].setMarked();
282
          else             mCubits[i].setUnmarked();
283
          DistortedNode node = mCubits[i].getNode();
284
          mScreen.attach(node);
285
          }
286

    
287
      mView.setScreenSize(width,height);
288
      mScreen.resize(width,height);
289
      }
290

    
291
///////////////////////////////////////////////////////////////////////////////////////////////////
292

    
293
   @Override
294
   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
295
      {
296
      DistortedLibrary.setMax(EffectType.VERTEX,25);
297
      MeshBase.setMaxEffComponents(120);
298

    
299
      VertexEffectDeform.enable();
300
      FragmentEffectBrightness.enable();
301

    
302
      DistortedLibrary.onSurfaceCreated(mView.getContext(),this,1);
303
      DistortedLibrary.setCull(true);
304

    
305
      createCubits();
306
      mCubitsCreated = true;
307
      }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
   public void distortedException(Exception ex)
312
     {
313
     android.util.Log.e("CREATOR", "unexpected exception: "+ex.getMessage() );
314
     }
315

    
316
///////////////////////////////////////////////////////////////////////////////////////////////////
317

    
318
   public BandagedCubit[] getCubits()
319
     {
320
     return mCubits;
321
     }
322

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324

    
325
   public DistortedScreen getScreen()
326
     {
327
     return mScreen;
328
     }
329

    
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331

    
332
   void setConnecting(int index1, int index2)
333
     {
334
     mIndex1 = index1;
335
     mIndex2 = index2;
336
     mConnectingCubits = true;
337
     }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340

    
341
   private void tryConnectingCubits(int index1, int index2)
342
     {
343
     if( index1!=index2 )
344
       {
345
       float[] pos1 = mCubits[index1].getPosition();
346
       float[] pos2 = mCubits[index2].getPosition();
347

    
348
       if( isAdjacent(pos1,pos2) )
349
         {
350
         mCubits[index2].join(pos1,mScaleValue);
351
         mCubits[index1].detach();
352
         mScreen.detach(mCubits[index1].getNode());
353
         }
354
       }
355
     }
356

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358

    
359
   public Static4D getQuatAccu()
360
     {
361
     return mQuatA;
362
     }
363

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

    
366
   public void setQuatTemp(float x, float y, float z, float w)
367
     {
368
     mQuatX = x;
369
     mQuatY = y;
370
     mQuatZ = z;
371
     mQuatW = w;
372

    
373
     mSetQuatT = true;
374
     }
375

    
376
///////////////////////////////////////////////////////////////////////////////////////////////////
377

    
378
   public void resetQuats()
379
     {
380
     mResetQuats = true;
381
     }
382

    
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384

    
385
   public boolean isBusy()
386
     {
387
     return mResettingObject;
388
     }
389

    
390
///////////////////////////////////////////////////////////////////////////////////////////////////
391

    
392
   public void saveObject()
393
     {
394
     int numAttached=0;
395

    
396
     for(int i=0; i<mNumCubits; i++)
397
       if( mCubits[i].isAttached() ) numAttached++;
398

    
399
     float[][] pos = new float[numAttached][];
400
     int attached=0;
401

    
402
     for(int i=0; i<mNumCubits; i++)
403
       if( mCubits[i].isAttached() )
404
         {
405
         pos[attached++] = mCubits[i].getPosition();
406
         }
407

    
408
     TwistyBandagedGeneric.setPositions(pos);
409
     TwistyObject obj = new TwistyBandagedGeneric( mObjSize, TwistyObject.MESH_NICE, TwistyObject.MODE_NORM,
410
                                                   new Static4D(0,0,0,1), new Static3D(0,0,0), 1.0f, null );
411
     String name = obj.getShortName();
412
     BandagedCreatorActivity act = (BandagedCreatorActivity) mView.getContext();
413

    
414
     if( act.objectDoesntExist(name) && createObjectJson(obj,act) )
415
       {
416
       setupIconCreation(act);
417
       act.addObject(obj.getShortName());
418
       }
419
     }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422

    
423
   private boolean createObjectJson(TwistyObject object, Activity act)
424
     {
425
     final String name = object.getShortName()+"_object.json";
426
     File file = new File(act.getFilesDir(), name);
427
     String filename = file.getAbsolutePath();
428

    
429
     try
430
       {
431
       JsonWriter writer = JsonWriter.getInstance();
432
       String json = writer.createObjectString(object,24);
433
       writer.write(filename,json);
434
       return true;
435
       }
436
     catch(JSONException ex)
437
       {
438
       act.runOnUiThread(new Runnable()
439
         {
440
         public void run()
441
           {
442
           String message = "JSON Exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
443
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
444
           }
445
         });
446

    
447
       return false;
448
       }
449
     catch(FileNotFoundException ex)
450
       {
451
       act.runOnUiThread(new Runnable()
452
         {
453
         public void run()
454
           {
455
           String message = "FileNotFound 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(IOException ex)
463
       {
464
       act.runOnUiThread(new Runnable()
465
         {
466
         public void run()
467
           {
468
           String message = "IO 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
     }
476

    
477
///////////////////////////////////////////////////////////////////////////////////////////////////
478

    
479
   private void setupIconCreation(Activity act)
480
     {
481
     final float R=1.0f;
482
     final int FBO_WIDTH  = (int)(R*240);
483
     final int FBO_HEIGHT = (int)(R*360);
484
     final float OBJECT_SIZE = R*0.38f;
485

    
486
     TwistyObject obj = new TwistyBandagedGeneric( mObjSize, TwistyObject.MESH_NICE, TwistyObject.MODE_ICON,
487
                                                   ShapeHexahedron.DEFAULT_ROT, new Static3D(0,0,0), OBJECT_SIZE, null );
488

    
489
     DistortedEffects effects = obj.getObjectEffects();
490
     DistortedNode node = obj.getNode();
491

    
492
     if( mFramebuffer==null )
493
       {
494
       mFramebuffer = new DistortedFramebuffer(FBO_WIDTH,FBO_HEIGHT,1, InternalOutputSurface.DEPTH_NO_STENCIL);
495
       mFramebuffer.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
496
       }
497

    
498
     mFramebuffer.detachAll();
499
     mFramebuffer.attach(node);
500

    
501
     Static1D halo = new Static1D(5);
502
     Static4D color = new Static4D(0,0,0,1);
503
     PostprocessEffectBorder border = new PostprocessEffectBorder(halo,color);
504
     border.setHaloDepth(false);
505
     effects.apply(border);
506

    
507
     final String name = obj.getShortName()+".png";
508
     File file = new File(act.getFilesDir(), name);
509
     String filename = file.getAbsolutePath();
510

    
511
     mSaveIcon = 0;
512
     mPath = filename;
513
     }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

    
517
   private void renderIcon(long time)
518
     {
519
     mFramebuffer.render(time);
520
     }
521

    
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523

    
524
   private void saveIcon()
525
     {
526
     int fW = mFramebuffer.getWidth();
527
     int fH = mFramebuffer.getHeight();
528

    
529
     ByteBuffer buf = ByteBuffer.allocateDirect(fW*fH*4);
530
     buf.order(ByteOrder.LITTLE_ENDIAN);
531

    
532
     mFramebuffer.setAsReadFramebuffer(0);
533
     GLES31.glReadBuffer(GLES31.GL_COLOR_ATTACHMENT0);
534
     GLES31.glReadPixels( 0, 0, fW, fH, GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, buf);
535
     BandagedCreatorWorkerThread.newBuffer(buf,fW,fH,6,mPath);
536
     GLES31.glBindFramebuffer(GLES31.GL_READ_FRAMEBUFFER, 0);
537

    
538
     mSaveIcon = -1;
539
     }
540

    
541
///////////////////////////////////////////////////////////////////////////////////////////////////
542

    
543
   private void rescaleObject()
544
     {
545
     //final int min = mX<mY ? Math.min(mX, mZ) : Math.min(mY, mZ);
546
     //final int size= (mX+mY+mZ-min)/2;
547
     final int size = mX>mY ? Math.max(mX, mZ) : Math.max(mY, mZ);
548
     final float Q = SCREEN_RATIO/size;
549
     mScaleValue = mWidth<mHeight ? Q*mWidth : Q*mHeight;
550
     mScale.set( mScaleValue,mScaleValue,mScaleValue );
551
     }
552

    
553
///////////////////////////////////////////////////////////////////////////////////////////////////
554

    
555
   public void changeObject(int x, int y, int z)
556
     {
557
     if( mX!=x || mY!=y || mZ!=z )
558
       {
559
       mX = x;
560
       mY = y;
561
       mZ = z;
562

    
563
       mObjSize[0] = mX;
564
       mObjSize[1] = mY;
565
       mObjSize[2] = mZ;
566

    
567
       mNumCubits = computeNumCubits(mX,mY,mZ);
568

    
569
       mCreatingCubits = true;
570
       }
571
     }
572

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

    
575
   public void displaySavingDialog()
576
     {
577
     BandagedCreatorActivity act = (BandagedCreatorActivity)mView.getContext();
578
     RubikDialogBandagedSave saveDiag = new RubikDialogBandagedSave();
579
     saveDiag.show(act.getSupportFragmentManager(), null);
580
     }
581

    
582
///////////////////////////////////////////////////////////////////////////////////////////////////
583

    
584
   public void setupReset()
585
     {
586
     mResettingObject = true;
587
     mInitialPhase    = true;
588
     mStartTime       = System.currentTimeMillis();
589
     }
590

    
591
///////////////////////////////////////////////////////////////////////////////////////////////////
592

    
593
   public boolean continueResetting(long time)
594
     {
595
     long diff = time-mStartTime;
596
     float quotient = ((float)diff)/RESET_DURATION;
597

    
598
     if( mInitialPhase && quotient>0.5f )
599
       {
600
       mInitialPhase=false;
601
       resetObject();
602
       }
603

    
604
     double angle = 2*Math.PI*quotient*quotient*(3-2*quotient);
605

    
606
     float sinA = (float)Math.sin(angle);
607
     float cosA = (float)Math.cos(angle);
608

    
609
     mQuatT.set(0, -sinA, 0, cosA);
610

    
611
     return quotient>1.0f;
612
     }
613

    
614
///////////////////////////////////////////////////////////////////////////////////////////////////
615

    
616
  public void touchCubit(int index)
617
    {
618
    mCubits[index].setMarked();
619
    }
620

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

    
623
  public void untouchCubit(int index)
624
    {
625
    mCubits[index].setUnmarked();
626
    }
627
}
(3-3/13)