Project

General

Profile

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

library / src / main / java / org / distorted / library / Distorted.java @ d333eb6b

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 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.library;
21

    
22
import java.io.BufferedReader;
23
import java.io.IOException;
24
import java.io.InputStream;
25
import java.io.InputStreamReader;
26

    
27
import android.content.Context;
28
import android.opengl.GLES20;
29
import android.os.Build;
30
import android.util.Log;
31

    
32
import org.distorted.library.exception.*;
33

    
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35
/**
36
 * A singleton class used to control various global settings.
37
 */
38
public class Distorted 
39
{
40
  /**
41
   * When creating an instance of a DistortedBitmap from another instance, do not clone anything.
42
   * Used in the copy constructor.
43
   */
44
  public static final int CLONE_NOTHING = 0x0;
45
  /**
46
   * When creating an instance of a DistortedObject from another instance, clone the Bitmap that's
47
   * backing up our DistortedObject. 
48
   * <p>
49
   * This way we can have two DistortedObjects, both backed up by the same Bitmap, to which we can 
50
   * apply different effects. Used in the copy constructor.
51
   */
52
  public static final int CLONE_BITMAP  = 0x1;
53
  /**
54
   * When creating an instance of a DistortedObject from another instance, clone the PreShader Effects.
55
   * <p>
56
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
57
   * always displayed in exactly the same place on the screen. Applying any matrix-based effect to 
58
   * one of them automatically applies the effect to the other. Used in the copy constructor.
59
   */
60
  public static final int CLONE_PRESHADER = 0x2;
61
  /**
62
   * When creating an instance of a DistortedObject from another instance, clone the Vertex Effects.
63
   * <p>
64
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
65
   * always with the same Vertex effects. Applying any vertex-based effect to one of them automatically 
66
   * applies the effect to the other. Used in the copy constructor.
67
   */
68
  public static final int CLONE_VERTEX  = 0x4;
69
  /**
70
   * When creating an instance of a DistortedObject from another instance, clone the Fragment Effects.
71
   * <p>
72
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
73
   * always with the same Fragment effects. Applying any fragment-based effect to one of them automatically 
74
   * applies the effect to the other. Used in the copy constructor.
75
   */
76
  public static final int CLONE_FRAGMENT= 0x8;
77

    
78
  // Note: why no 'CLONE_POSTSHADER' constant? The only Postshader effects are the 'save to PNG file'
79
  // 'save to MP4 file'. Sharing those effects across multiple DistortedObject objects does not make any
80
  // sense - multiple Objects would then fight to get saved all to the same file.
81

    
82
  /**
83
   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
84
   * <p>
85
   * This is mainly useful for creating many similar sub-trees of Bitmaps and rendering then at different places
86
   * on the screen, with (optionally) different effects of the top-level root Bitmap.   
87
   */
88
  public static final int CLONE_CHILDREN= 0x10;
89

    
90
  private static final String TAG = Distorted.class.getSimpleName();
91
  private static boolean mInitialized = false;
92
  
93
  static int mPositionH;      // pass in model position information
94
  static int mColorH;         // pass in model color information
95
  static int mTextureUniformH;// pass in the texture.
96
  static int mNormalH;        // pass in model normal information.
97
  static int mTextureCoordH;  // pass in model texture coordinate information.
98
  static int mProgramH;       // This is a handle to our shading program.  
99

    
100
  static DistortedProjection mProjection = new DistortedProjection(false);
101
  static float mFOV = 60.0f;
102
  
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104

    
105
  private Distorted()
106
    {
107
    
108
    }
109

    
110
///////////////////////////////////////////////////////////////////////////////////////////////////
111
  
112
  private static void sanitizeMaxValues() throws VertexUniformsException,FragmentUniformsException
113
    {
114
    int maxV,maxF;  
115
    int[] param = new int[1];
116
    
117
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
118
    maxV = param[0];
119
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
120
    maxF = param[0];
121
    
122
    Log.d(TAG, "Max vectors in vertex shader: "+maxV);
123
    Log.d(TAG, "Max vectors in fragment shader: "+maxF);
124
    
125
    if( Build.FINGERPRINT.startsWith("generic") == false )
126
      {
127
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
128
      int realMaxF = (maxF- 2)/4;   //
129
    
130
      if( getMaxVertex()   > realMaxV )
131
        {
132
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
133
        }
134
      if( getMaxFragment() > realMaxF )
135
        {
136
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
137
        }
138
      }
139
    }
140
  
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
144
    {
145
    int shaderHandle = GLES20.glCreateShader(shaderType);
146

    
147
    if (shaderHandle != 0) 
148
      {
149
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
150
      GLES20.glCompileShader(shaderHandle);
151
      final int[] compileStatus = new int[1];
152
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
153

    
154
      if (compileStatus[0] != GLES20.GL_TRUE ) 
155
        {
156
        GLES20.glDeleteShader(shaderHandle);
157
        shaderHandle = 0;
158
        }
159
      }
160

    
161
    if (shaderHandle == 0)
162
      {     
163
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
164
     
165
      switch(shaderType)
166
        {
167
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
168
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
169
        default                       : throw new RuntimeException(error);
170
        }
171
      }
172

    
173
    return shaderHandle;
174
    }
175

    
176
///////////////////////////////////////////////////////////////////////////////////////////////////
177

    
178
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
179
    {
180
    int programHandle = GLES20.glCreateProgram();
181

    
182
    if (programHandle != 0) 
183
      {
184
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
185
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
186

    
187
      if (attributes != null)
188
        {
189
        final int size = attributes.length;
190

    
191
        for (int i = 0; i < size; i++)
192
          {
193
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
194
          }                
195
        }
196

    
197
      GLES20.glLinkProgram(programHandle);
198

    
199
      final int[] linkStatus = new int[1];
200
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
201

    
202
      if (linkStatus[0] != GLES20.GL_TRUE ) 
203
        {         
204
        String error = GLES20.glGetProgramInfoLog(programHandle);
205
        GLES20.glDeleteProgram(programHandle);
206
        throw new LinkingException(error);
207
        }
208
      
209
      final int[] numberOfUniforms = new int[1];
210
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
211

    
212
      android.util.Log.d(TAG, "number of active uniforms="+numberOfUniforms[0]);
213
      }
214
 
215
    return programHandle;
216
    }
217
 
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219
  
220
  private static String generateShaderHeader(final int type)
221
    {
222
    String header="";
223
   
224
    switch(type)
225
      {
226
      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + getMaxVertex()+"\n");
227
     
228
                                      for(EffectNames name: EffectNames.values() )
229
                                        {
230
                                        if( name.getType()==EffectTypes.VERTEX)
231
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
232
                                        }
233
                                      break;
234
      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ getMaxFragment()+"\n");
235
     
236
                                      for(EffectNames name: EffectNames.values() )
237
                                        {
238
                                        if( name.getType()==EffectTypes.FRAGMENT)
239
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
240
                                        }
241
                                      break;
242
     }
243
   
244
    //Log.d(TAG,""+header);
245
    
246
    return header;
247
    }
248
  
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250
  
251
  private static String readTextFileFromRawResource(final Context c, final int resourceId)
252
    {
253
    final InputStream inputStream = c.getResources().openRawResource(resourceId);
254
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
255
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
256
 
257
    String nextLine;
258
    final StringBuilder body = new StringBuilder();
259
 
260
    try
261
      {
262
      while ((nextLine = bufferedReader.readLine()) != null)
263
        {
264
        body.append(nextLine);
265
        body.append('\n');
266
        }
267
      }
268
    catch (IOException e)
269
      {
270
      return null;
271
      }
272
 
273
    return body.toString();
274
    }
275
 
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277
 
278
  private static String getVertexShader(final Context c)
279
    {
280
    return readTextFileFromRawResource( c, R.raw.main_vertex_shader);
281
    }
282
 
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284
 
285
  private static String getFragmentShader(final Context c)
286
    {
287
    return readTextFileFromRawResource( c, R.raw.main_fragment_shader);
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291
// Public API
292
///////////////////////////////////////////////////////////////////////////////////////////////////
293
/**
294
 * Sets Vertical Field of View angle. This changes the Projection Matrix.  
295
 *   
296
 * @param fov Vertical Field Of View angle, in degrees. If T is the middle of the top edge of the 
297
 *            screen, E is the eye point, and B is the middle of the bottom edge of the screen, then 
298
 *            fov = angle(TEB)
299
 */
300
  public static void setFov(float fov)
301
    {
302
    mFOV = fov;
303
   
304
    if( mProjection.width>0 && mProjection.height>0 )
305
      mProjection.onSurfaceChanged( (int)mProjection.width, (int)mProjection.height);
306
    }
307
  
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309
/**
310
 * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
311
 * I.e. best called from GLSurfaceView.onSurfaceCreated().
312
 * <p>
313
 * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms, and initialises all Bitmaps that have already
314
 * been created.
315
 *   
316
 * @param context Context of the APpp using the library - used to open up Resources and read Shader code.
317
 * @throws FragmentCompilationException
318
 * @throws VertexCompilationException
319
 * @throws VertexUniformsException
320
 * @throws FragmentUniformsException
321
 * @throws LinkingException
322
 */
323
  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
324
    { 
325
    mInitialized = true;  
326
     
327
// String ver;  
328
    
329
    final String vertexShader   = Distorted.getVertexShader(context);
330
    final String fragmentShader = Distorted.getFragmentShader(context);
331
/*
332
    ver = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);    
333
    Log.d(TAG, "GLSL version: "+(ver==null ? "null" : ver) );
334
    ver = GLES20.glGetString(GLES20.GL_VERSION);   
335
    Log.d(TAG, "GL version: "+(ver==null ? "null" : ver) );
336
    ver = GLES20.glGetString(GLES20.GL_VENDOR);    
337
    Log.d(TAG, "GL vendor: "+(ver==null ? "null" : ver) );
338
    ver = GLES20.glGetString(GLES20.GL_RENDERER);  
339
    Log.d(TAG, "GL renderer: "+(ver==null ? "null" : ver) );
340
*/
341
    //ver = GLES20.glGetString(GLES20.GL_EXTENSIONS);    
342
    //Log.d(TAG, "GL extensions: "+(ver==null ? "null" : ver) );
343
    
344
    sanitizeMaxValues();
345
    
346
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
347
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
348
      
349
    mProgramH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});                                                            
350
      
351
    GLES20.glUseProgram(mProgramH);
352
    GLES20.glEnable (GLES20.GL_DEPTH_TEST);
353
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
354
    GLES20.glEnable(GLES20.GL_BLEND);
355
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
356
    
357
    mTextureUniformH = GLES20.glGetUniformLocation(mProgramH, "u_Texture");
358
    
359
    mPositionH       = GLES20.glGetAttribLocation( mProgramH, "a_Position");
360
    mColorH          = GLES20.glGetAttribLocation( mProgramH, "a_Color");
361
    mNormalH         = GLES20.glGetAttribLocation( mProgramH, "a_Normal"); 
362
    mTextureCoordH   = GLES20.glGetAttribLocation( mProgramH, "a_TexCoordinate");
363
    
364
    EffectQueueFragment.getUniforms(mProgramH);
365
    EffectQueueVertex.getUniforms(mProgramH);
366
    EffectQueueMatrix.getUniforms(mProgramH);
367
    
368
    GLES20.glEnableVertexAttribArray(mPositionH);        
369
    GLES20.glEnableVertexAttribArray(mColorH);
370
    GLES20.glEnableVertexAttribArray(mNormalH);
371
    GLES20.glEnableVertexAttribArray(mTextureCoordH);
372
   
373
    DistortedObjectList.reset();
374
    DistortedNode.reset();
375
    EffectMessageSender.startSending();
376
    }
377
  
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
/**
380
 * Call this when the physical size of the surface we are rendering to changes.
381
 * I.e. must be called from GLSurfaceView.onSurfaceChanged()  
382
 *   
383
 * @param surfaceWidth  new width of the surface.
384
 * @param surfaceHeight new height of the surface.
385
 */
386
  public static void onSurfaceChanged(int surfaceWidth, int surfaceHeight)
387
    {
388
    mProjection.onSurfaceChanged(surfaceWidth, surfaceHeight);
389
    }
390

    
391
///////////////////////////////////////////////////////////////////////////////////////////////////
392
/**
393
 * Call this so that the Library can release its internal data structures.
394
 * Must be called from Activity.onDestroy(). 
395
 */
396
  public static void onDestroy()
397
    {
398
    DistortedObjectList.release();
399
    DistortedNode.release();
400
    EffectQueue.reset();
401
    EffectMessageSender.stopSending();
402
   
403
    mInitialized = false;
404
    }
405
  
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407
/**
408
 * Returns the true if onSurfaceCreated has been called already, and thus if the Library's is ready
409
 * to accept effect requests.
410
 * 
411
 * @return <code>true</code> if the Library is ready for action, <code>false</code> otherwise.
412
 */
413
  public static boolean isInitialized()
414
    {
415
    return mInitialized;  
416
    }
417

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419
/**
420
 * Returns the maximum number of Matrix effects.
421
 *    
422
 * @return The maximum number of Matrix effects
423
 */
424
  public static int getMaxMatrix()
425
    {
426
    return EffectQueue.getMax(EffectTypes.MATRIX.ordinal());
427
    }
428
 
429
///////////////////////////////////////////////////////////////////////////////////////////////////
430
/**
431
 * Returns the maximum number of Vertex effects.
432
 *    
433
 * @return The maximum number of Vertex effects
434
 */  
435
  public static int getMaxVertex()
436
    {
437
    return EffectQueue.getMax(EffectTypes.VERTEX.ordinal());
438
    }
439
  
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441
/**
442
 * Returns the maximum number of Fragment effects.
443
 *    
444
 * @return The maximum number of Fragment effects
445
 */  
446
  public static int getMaxFragment()
447
    {
448
    return EffectQueue.getMax(EffectTypes.FRAGMENT.ordinal());
449
    }
450

    
451
///////////////////////////////////////////////////////////////////////////////////////////////////
452
/**
453
 * Returns the maximum number of Other effects.
454
 *
455
 * @return The maximum number of Other effects
456
 */
457
  public static int getMaxOther()
458
    {
459
    return EffectQueue.getMax(EffectTypes.OTHER.ordinal());
460
    }
461

    
462
///////////////////////////////////////////////////////////////////////////////////////////////////
463
/**
464
 * Sets the maximum number of Matrix effects that can be applied to a single DistortedObject at one time.
465
 * This can fail if the value of 'max' is outside permitted range. 
466
 * 
467
 * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
468
 *            than Byte.MAX_VALUE 
469
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
470
 */
471
  public static boolean setMaxMatrix(int max)
472
    {
473
    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
474
    }
475
  
476
///////////////////////////////////////////////////////////////////////////////////////////////////
477
/**
478
 * Sets the maximum number of Vertex effects that can be applied to a single DistortedObject at one time.
479
 * This can fail if:
480
 * <ul>
481
 * <li>the value of 'max' is outside permitted range
482
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
483
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
484
 *     time only decreasing the value of 'max' is permitted.  
485
 * </ul>
486
 * 
487
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
488
 *            than Byte.MAX_VALUE 
489
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
490
 */
491
  public static boolean setMaxVertex(int max)
492
    {
493
    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
494
    }
495

    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497
/**
498
 * Sets the maximum number of Fragment effects that can be applied to a single DistortedObject at one time.
499
 * This can fail if:
500
 * <ul>
501
 * <li>the value of 'max' is outside permitted range
502
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
503
 *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
504
 *     time only decreasing the value of 'max' is permitted.  
505
 * </ul>
506
 * 
507
 * @param max new maximum number of simultaneous Fragment Effects. Has to be a non-negative number not greater
508
 *            than Byte.MAX_VALUE 
509
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
510
 */
511
  public static boolean setMaxFragment(int max)
512
    {
513
    return EffectQueue.setMax(EffectTypes.FRAGMENT.ordinal(),max);
514
    }
515

    
516
///////////////////////////////////////////////////////////////////////////////////////////////////
517
/**
518
 * Sets the maximum number of Other effects that can be applied to a single DistortedObject at one time.
519
 * This can fail if the value of 'max' is outside permitted range.
520
 *
521
 * @param max new maximum number of simultaneous Other Effects. Has to be a non-negative number not greater
522
 *            than Byte.MAX_VALUE
523
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
524
 */
525
  public static boolean setMaxOther(int max)
526
    {
527
    return EffectQueue.setMax(EffectTypes.OTHER.ordinal(),max);
528
    }
529

    
530
///////////////////////////////////////////////////////////////////////////////////////////////////
531
//end of file  
532
}
(1-1/30)