Project

General

Profile

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

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

1
package org.distorted.library;
2

    
3
import java.io.BufferedReader;
4
import java.io.IOException;
5
import java.io.InputStream;
6
import java.io.InputStreamReader;
7

    
8
import android.content.Context;
9
import android.opengl.GLES20;
10
import android.os.Build;
11
import android.util.Log;
12

    
13
import org.distorted.library.exception.*;
14

    
15
///////////////////////////////////////////////////////////////////////////////////////////////////
16
/**
17
 * A singleton class used to control various global settings.
18
 */
19
public class Distorted 
20
{
21
  /**
22
   * When creating an instance of a DistortedBitmap from another instance, do not clone anything.
23
   * Used in the copy constructor.
24
   */
25
  public static final int CLONE_NOTHING = 0x0;
26
  /**
27
   * When creating an instance of a DistortedObject from another instance, clone the Bitmap that's
28
   * backing up our DistortedObject. 
29
   * <p>
30
   * This way we can have two DistortedObjects, both backed up by the same Bitmap, to which we can 
31
   * apply different effects. Used in the copy constructor.
32
   */
33
  public static final int CLONE_BITMAP  = 0x1;
34
  /**
35
   * When creating an instance of a DistortedObject from another instance, clone the PreShader Effects.
36
   * <p>
37
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
38
   * always displayed in exactly the same place on the screen. Applying any matrix-based effect to 
39
   * one of them automatically applies the effect to the other. Used in the copy constructor.
40
   */
41
  public static final int CLONE_PRESHADER = 0x2;
42
  /**
43
   * When creating an instance of a DistortedObject from another instance, clone the Vertex Effects.
44
   * <p>
45
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
46
   * always with the same Vertex effects. Applying any vertex-based effect to one of them automatically 
47
   * applies the effect to the other. Used in the copy constructor.
48
   */
49
  public static final int CLONE_VERTEX  = 0x4;
50
  /**
51
   * When creating an instance of a DistortedObject from another instance, clone the Fragment Effects.
52
   * <p>
53
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
54
   * always with the same Fragment effects. Applying any fragment-based effect to one of them automatically 
55
   * applies the effect to the other. Used in the copy constructor.
56
   */
57
  public static final int CLONE_FRAGMENT= 0x8;
58

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

    
63
  /**
64
   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
65
   * <p>
66
   * This is mainly useful for creating many similar sub-trees of Bitmaps and rendering then at different places
67
   * on the screen, with (optionally) different effects of the top-level root Bitmap.   
68
   */
69
  public static final int CLONE_CHILDREN= 0x10;
70

    
71
  private static final String TAG = Distorted.class.getSimpleName();
72
  private static boolean mInitialized = false;
73
  
74
  static int mPositionH;      // pass in model position information
75
  static int mColorH;         // pass in model color information
76
  static int mTextureUniformH;// pass in the texture.
77
  static int mNormalH;        // pass in model normal information.
78
  static int mTextureCoordH;  // pass in model texture coordinate information.
79
  static int mProgramH;       // This is a handle to our shading program.  
80

    
81
  static DistortedProjection mProjection = new DistortedProjection(false);
82
  static float mFOV = 60.0f;
83
  
84
///////////////////////////////////////////////////////////////////////////////////////////////////
85

    
86
  private Distorted()
87
    {
88
    
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92
  
93
  private static void sanitizeMaxValues() throws VertexUniformsException,FragmentUniformsException
94
    {
95
    int maxV,maxF;  
96
    int[] param = new int[1];
97
    
98
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
99
    maxV = param[0];
100
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
101
    maxF = param[0];
102
    
103
    Log.d(TAG, "Max vectors in vertex shader: "+maxV);
104
    Log.d(TAG, "Max vectors in fragment shader: "+maxF);
105
    
106
    if( Build.FINGERPRINT.startsWith("generic") == false )
107
      {
108
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
109
      int realMaxF = (maxF- 2)/4;   //
110
    
111
      if( getMaxVertex()   > realMaxV )
112
        {
113
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
114
        }
115
      if( getMaxFragment() > realMaxF )
116
        {
117
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
118
        }
119
      }
120
    }
121
  
122
///////////////////////////////////////////////////////////////////////////////////////////////////
123

    
124
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
125
    {
126
    int shaderHandle = GLES20.glCreateShader(shaderType);
127

    
128
    if (shaderHandle != 0) 
129
      {
130
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
131
      GLES20.glCompileShader(shaderHandle);
132
      final int[] compileStatus = new int[1];
133
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
134

    
135
      if (compileStatus[0] != GLES20.GL_TRUE ) 
136
        {
137
        GLES20.glDeleteShader(shaderHandle);
138
        shaderHandle = 0;
139
        }
140
      }
141

    
142
    if (shaderHandle == 0)
143
      {     
144
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
145
     
146
      switch(shaderType)
147
        {
148
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
149
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
150
        default                       : throw new RuntimeException(error);
151
        }
152
      }
153

    
154
    return shaderHandle;
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
160
    {
161
    int programHandle = GLES20.glCreateProgram();
162

    
163
    if (programHandle != 0) 
164
      {
165
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
166
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
167

    
168
      if (attributes != null)
169
        {
170
        final int size = attributes.length;
171

    
172
        for (int i = 0; i < size; i++)
173
          {
174
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
175
          }                
176
        }
177

    
178
      GLES20.glLinkProgram(programHandle);
179

    
180
      final int[] linkStatus = new int[1];
181
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
182

    
183
      if (linkStatus[0] != GLES20.GL_TRUE ) 
184
        {         
185
        String error = GLES20.glGetProgramInfoLog(programHandle);
186
        GLES20.glDeleteProgram(programHandle);
187
        throw new LinkingException(error);
188
        }
189
      
190
      final int[] numberOfUniforms = new int[1];
191
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
192

    
193
      android.util.Log.d(TAG, "number of active uniforms="+numberOfUniforms[0]);
194
      }
195
 
196
    return programHandle;
197
    }
198
 
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200
  
201
  private static String generateShaderHeader(final int type)
202
    {
203
    String header="";
204
   
205
    switch(type)
206
      {
207
      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + getMaxVertex()+"\n");
208
     
209
                                      for(EffectNames name: EffectNames.values() )
210
                                        {
211
                                        if( name.getType()==EffectTypes.VERTEX)
212
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
213
                                        }
214
                                      break;
215
      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ getMaxFragment()+"\n");
216
     
217
                                      for(EffectNames name: EffectNames.values() )
218
                                        {
219
                                        if( name.getType()==EffectTypes.FRAGMENT)
220
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
221
                                        }
222
                                      break;
223
     }
224
   
225
    //Log.d(TAG,""+header);
226
    
227
    return header;
228
    }
229
  
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231
  
232
  private static String readTextFileFromRawResource(final Context c, final int resourceId)
233
    {
234
    final InputStream inputStream = c.getResources().openRawResource(resourceId);
235
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
236
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
237
 
238
    String nextLine;
239
    final StringBuilder body = new StringBuilder();
240
 
241
    try
242
      {
243
      while ((nextLine = bufferedReader.readLine()) != null)
244
        {
245
        body.append(nextLine);
246
        body.append('\n');
247
        }
248
      }
249
    catch (IOException e)
250
      {
251
      return null;
252
      }
253
 
254
    return body.toString();
255
    }
256
 
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258
 
259
  private static String getVertexShader(final Context c)
260
    {
261
    return readTextFileFromRawResource( c, R.raw.main_vertex_shader);
262
    }
263
 
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265
 
266
  private static String getFragmentShader(final Context c)
267
    {
268
    return readTextFileFromRawResource( c, R.raw.main_fragment_shader);
269
    }
270

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

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
/**
374
 * Call this so that the Library can release its internal data structures.
375
 * Must be called from Activity.onDestroy(). 
376
 */
377
  public static void onDestroy()
378
    {
379
    DistortedObjectList.release();
380
    DistortedNode.release();
381
    EffectQueue.reset();
382
    EffectMessageSender.stopSending();
383
   
384
    mInitialized = false;
385
    }
386
  
387
///////////////////////////////////////////////////////////////////////////////////////////////////
388
/**
389
 * Returns the true if onSurfaceCreated has been called already, and thus if the Library's is ready
390
 * to accept effect requests.
391
 * 
392
 * @return <code>true</code> if the Library is ready for action, <code>false</code> otherwise.
393
 */
394
  public static boolean isInitialized()
395
    {
396
    return mInitialized;  
397
    }
398

    
399
///////////////////////////////////////////////////////////////////////////////////////////////////
400
/**
401
 * Returns the maximum number of Matrix effects.
402
 *    
403
 * @return The maximum number of Matrix effects
404
 */
405
  public static int getMaxMatrix()
406
    {
407
    return EffectQueue.getMax(EffectTypes.MATRIX.ordinal());
408
    }
409
 
410
///////////////////////////////////////////////////////////////////////////////////////////////////
411
/**
412
 * Returns the maximum number of Vertex effects.
413
 *    
414
 * @return The maximum number of Vertex effects
415
 */  
416
  public static int getMaxVertex()
417
    {
418
    return EffectQueue.getMax(EffectTypes.VERTEX.ordinal());
419
    }
420
  
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422
/**
423
 * Returns the maximum number of Fragment effects.
424
 *    
425
 * @return The maximum number of Fragment effects
426
 */  
427
  public static int getMaxFragment()
428
    {
429
    return EffectQueue.getMax(EffectTypes.FRAGMENT.ordinal());
430
    }
431

    
432
///////////////////////////////////////////////////////////////////////////////////////////////////
433
/**
434
 * Returns the maximum number of Other effects.
435
 *
436
 * @return The maximum number of Other effects
437
 */
438
  public static int getMaxOther()
439
    {
440
    return EffectQueue.getMax(EffectTypes.OTHER.ordinal());
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444
/**
445
 * Sets the maximum number of Matrix effects that can be applied to a single DistortedObject at one time.
446
 * This can fail if the value of 'max' is outside permitted range. 
447
 * 
448
 * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
449
 *            than Byte.MAX_VALUE 
450
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
451
 */
452
  public static boolean setMaxMatrix(int max)
453
    {
454
    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
455
    }
456
  
457
///////////////////////////////////////////////////////////////////////////////////////////////////
458
/**
459
 * Sets the maximum number of Vertex effects that can be applied to a single DistortedObject at one time.
460
 * This can fail if:
461
 * <ul>
462
 * <li>the value of 'max' is outside permitted range
463
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
464
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
465
 *     time only decreasing the value of 'max' is permitted.  
466
 * </ul>
467
 * 
468
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
469
 *            than Byte.MAX_VALUE 
470
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
471
 */
472
  public static boolean setMaxVertex(int max)
473
    {
474
    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
475
    }
476

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

    
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498
/**
499
 * Sets the maximum number of Other effects that can be applied to a single DistortedObject at one time.
500
 * This can fail if the value of 'max' is outside permitted range.
501
 *
502
 * @param max new maximum number of simultaneous Other Effects. Has to be a non-negative number not greater
503
 *            than Byte.MAX_VALUE
504
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
505
 */
506
  public static boolean setMaxOther(int max)
507
    {
508
    return EffectQueue.setMax(EffectTypes.OTHER.ordinal(),max);
509
    }
510

    
511
///////////////////////////////////////////////////////////////////////////////////////////////////
512
//end of file  
513
}
(1-1/30)