Project

General

Profile

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

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

1 6a06a912 Leszek Koltunski
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 7845dc66 Leszek Koltunski
import android.content.Context;
9 6a06a912 Leszek Koltunski
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 b3618cb5 Leszek Koltunski
   * When creating an instance of a DistortedObject from another instance, clone the PreShader Effects.
36 6a06a912 Leszek Koltunski
   * <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 b3618cb5 Leszek Koltunski
  public static final int CLONE_PRESHADER = 0x2;
42 6a06a912 Leszek Koltunski
  /**
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 b3618cb5 Leszek Koltunski
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 6a06a912 Leszek Koltunski
  /**
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 b3618cb5 Leszek Koltunski
   * Constant used to represent a PreShader-based effect.
72
   */
73
  public static final int TYPE_PRE  = 0x1;
74 6a06a912 Leszek Koltunski
  /**
75
   * Constant used to represent a Vertex-based effect.
76
   */
77
  public static final int TYPE_VERT = 0x2;
78
  /**
79
   * Constant used to represent a Fragment-based effect.
80
   */
81
  public static final int TYPE_FRAG = 0x4;
82 b3618cb5 Leszek Koltunski
  /**
83
   * Constant used to represent a PostShader-based effect.
84
   */
85
  public static final int TYPE_POST = 0x8;
86
87 6a06a912 Leszek Koltunski
  private static final String TAG = Distorted.class.getSimpleName();
88
  private static boolean mInitialized = false;
89
  
90
  static int mPositionH;      // pass in model position information
91
  static int mColorH;         // pass in model color information
92
  static int mTextureUniformH;// pass in the texture.
93
  static int mNormalH;        // pass in model normal information.
94
  static int mTextureCoordH;  // pass in model texture coordinate information.
95
  static int mProgramH;       // This is a handle to our shading program.  
96
97
  static DistortedProjection mProjection = new DistortedProjection(false);
98
  static float mFOV = 60.0f;
99
  
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101
102
  private Distorted()
103
    {
104
    
105
    }
106
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108
  
109
  private static void sanitizeMaxValues() throws VertexUniformsException,FragmentUniformsException
110
    {
111
    int maxV,maxF;  
112
    int[] param = new int[1];
113
    
114
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
115
    maxV = param[0];
116
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
117
    maxF = param[0];
118
    
119
    Log.d(TAG, "Max vectors in vertex shader: "+maxV);
120
    Log.d(TAG, "Max vectors in fragment shader: "+maxF);
121
    
122
    if( Build.FINGERPRINT.startsWith("generic") == false )
123
      {
124
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
125
      int realMaxF = (maxF- 2)/4;   //
126
    
127
      if( EffectListVertex.getMax() > realMaxV )
128
        {
129
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
130
        }
131
      if( EffectListFragment.getMax() > realMaxF )
132
        {
133
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
134
        }
135
      }
136
    }
137
  
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139
140
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
141
    {
142
    int shaderHandle = GLES20.glCreateShader(shaderType);
143
144
    if (shaderHandle != 0) 
145
      {
146
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
147
      GLES20.glCompileShader(shaderHandle);
148
      final int[] compileStatus = new int[1];
149
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
150
151
      if (compileStatus[0] != GLES20.GL_TRUE ) 
152
        {
153
        GLES20.glDeleteShader(shaderHandle);
154
        shaderHandle = 0;
155
        }
156
      }
157
158
    if (shaderHandle == 0)
159
      {     
160
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
161
     
162
      switch(shaderType)
163
        {
164
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
165
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
166
        default                       : throw new RuntimeException(error);
167
        }
168
      }
169
170
    return shaderHandle;
171
    }
172
173
///////////////////////////////////////////////////////////////////////////////////////////////////
174
175
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
176
    {
177
    int programHandle = GLES20.glCreateProgram();
178
179
    if (programHandle != 0) 
180
      {
181
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
182
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
183
184
      if (attributes != null)
185
        {
186
        final int size = attributes.length;
187
188
        for (int i = 0; i < size; i++)
189
          {
190
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
191
          }                
192
        }
193
194
      GLES20.glLinkProgram(programHandle);
195
196
      final int[] linkStatus = new int[1];
197
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
198
199
      if (linkStatus[0] != GLES20.GL_TRUE ) 
200
        {         
201
        String error = GLES20.glGetProgramInfoLog(programHandle);
202
        GLES20.glDeleteProgram(programHandle);
203
        throw new LinkingException(error);
204
        }
205
      
206
      final int[] numberOfUniforms = new int[1];
207
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
208
209
      android.util.Log.d(TAG, "number of active uniforms="+numberOfUniforms[0]);
210
      }
211
 
212
    return programHandle;
213
    }
214
 
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216
  
217
  private static String generateShaderHeader(final int type)
218
    {
219
    String header="";
220
   
221
    switch(type)
222
      {
223
      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  +EffectListVertex.getMax()+"\n");
224
     
225
                                      for(EffectNames name: EffectNames.values() )
226
                                        {
227
                                        if( name.getType()==TYPE_VERT )  
228
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
229
                                        }
230
                                      break;
231
      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+EffectListFragment.getMax()+"\n");
232
     
233
                                      for(EffectNames name: EffectNames.values() )
234
                                        {
235
                                        if( name.getType()==TYPE_FRAG )  
236
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
237
                                        }
238
                                      break;
239
     }
240
   
241
    //Log.d(TAG,""+header);
242
    
243
    return header;
244
    }
245
  
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247
  
248 7845dc66 Leszek Koltunski
  private static String readTextFileFromRawResource(final Context c, final int resourceId)
249 6a06a912 Leszek Koltunski
    {
250 7845dc66 Leszek Koltunski
    final InputStream inputStream = c.getResources().openRawResource(resourceId);
251 6a06a912 Leszek Koltunski
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
252
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
253
 
254
    String nextLine;
255
    final StringBuilder body = new StringBuilder();
256
 
257
    try
258
      {
259
      while ((nextLine = bufferedReader.readLine()) != null)
260
        {
261
        body.append(nextLine);
262
        body.append('\n');
263
        }
264
      }
265
    catch (IOException e)
266
      {
267
      return null;
268
      }
269
 
270
    return body.toString();
271
    }
272
 
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274
 
275 7845dc66 Leszek Koltunski
  private static String getVertexShader(final Context c)
276 6a06a912 Leszek Koltunski
    {
277 7845dc66 Leszek Koltunski
    return readTextFileFromRawResource( c, R.raw.main_vertex_shader);
278 6a06a912 Leszek Koltunski
    }
279
 
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281
 
282 7845dc66 Leszek Koltunski
  private static String getFragmentShader(final Context c)
283 6a06a912 Leszek Koltunski
    {
284 7845dc66 Leszek Koltunski
    return readTextFileFromRawResource( c, R.raw.main_fragment_shader);
285 6a06a912 Leszek Koltunski
    }
286
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288
// Public API
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290
/**
291
 * Sets Vertical Field of View angle. This changes the Projection Matrix.  
292
 *   
293
 * @param fov Vertical Field Of View angle, in degrees. If T is the middle of the top edge of the 
294
 *            screen, E is the eye point, and B is the middle of the bottom edge of the screen, then 
295
 *            fov = angle(TEB)
296
 */
297
  public static void setFov(float fov)
298
    {
299
    mFOV = fov;
300
   
301
    if( mProjection.width>0 && mProjection.height>0 )
302
      mProjection.onSurfaceChanged( (int)mProjection.width, (int)mProjection.height);
303
    }
304
  
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306
/**
307
 * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
308
 * I.e. best called from GLSurfaceView.onSurfaceCreated().
309
 * <p>
310
 * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms, and initialises all Bitmaps that have already
311
 * been created.
312
 *   
313 7845dc66 Leszek Koltunski
 * @param context Context of the APpp using the library - used to open up Resources and read Shader code.
314 6a06a912 Leszek Koltunski
 * @throws FragmentCompilationException
315
 * @throws VertexCompilationException
316
 * @throws VertexUniformsException
317
 * @throws FragmentUniformsException
318
 * @throws LinkingException
319
 */
320 7845dc66 Leszek Koltunski
  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
321 6a06a912 Leszek Koltunski
    { 
322
    mInitialized = true;  
323
     
324
// String ver;  
325
    
326 7845dc66 Leszek Koltunski
    final String vertexShader   = Distorted.getVertexShader(context);
327
    final String fragmentShader = Distorted.getFragmentShader(context);
328 6a06a912 Leszek Koltunski
/*
329
    ver = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);    
330
    Log.d(TAG, "GLSL version: "+(ver==null ? "null" : ver) );
331
    ver = GLES20.glGetString(GLES20.GL_VERSION);   
332
    Log.d(TAG, "GL version: "+(ver==null ? "null" : ver) );
333
    ver = GLES20.glGetString(GLES20.GL_VENDOR);    
334
    Log.d(TAG, "GL vendor: "+(ver==null ? "null" : ver) );
335
    ver = GLES20.glGetString(GLES20.GL_RENDERER);  
336
    Log.d(TAG, "GL renderer: "+(ver==null ? "null" : ver) );
337
*/
338
    //ver = GLES20.glGetString(GLES20.GL_EXTENSIONS);    
339
    //Log.d(TAG, "GL extensions: "+(ver==null ? "null" : ver) );
340
    
341
    sanitizeMaxValues();
342
    
343
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
344
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
345
      
346
    mProgramH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});                                                            
347
      
348
    GLES20.glUseProgram(mProgramH);
349
    GLES20.glEnable (GLES20.GL_DEPTH_TEST);
350
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
351
    GLES20.glEnable(GLES20.GL_BLEND);
352
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
353
    
354
    mTextureUniformH = GLES20.glGetUniformLocation(mProgramH, "u_Texture");
355
    
356
    mPositionH       = GLES20.glGetAttribLocation( mProgramH, "a_Position");
357
    mColorH          = GLES20.glGetAttribLocation( mProgramH, "a_Color");
358
    mNormalH         = GLES20.glGetAttribLocation( mProgramH, "a_Normal"); 
359
    mTextureCoordH   = GLES20.glGetAttribLocation( mProgramH, "a_TexCoordinate");
360
    
361
    EffectListFragment.getUniforms(mProgramH);
362
    EffectListVertex.getUniforms(mProgramH);
363 b3618cb5 Leszek Koltunski
    EffectListPreShader.getUniforms(mProgramH);
364 6a06a912 Leszek Koltunski
    
365
    GLES20.glEnableVertexAttribArray(mPositionH);        
366
    GLES20.glEnableVertexAttribArray(mColorH);
367
    GLES20.glEnableVertexAttribArray(mNormalH);
368
    GLES20.glEnableVertexAttribArray(mTextureCoordH);
369
   
370
    DistortedObjectList.reset();
371
    DistortedNode.reset();
372
    EffectMessageSender.startSending();
373
    }
374
  
375
///////////////////////////////////////////////////////////////////////////////////////////////////
376
/**
377
 * Call this when the physical size of the surface we are rendering to changes.
378
 * I.e. must be called from GLSurfaceView.onSurfaceChanged()  
379
 *   
380
 * @param surfaceWidth  new width of the surface.
381
 * @param surfaceHeight new height of the surface.
382
 */
383
  public static void onSurfaceChanged(int surfaceWidth, int surfaceHeight)
384
    {
385
    mProjection.onSurfaceChanged(surfaceWidth, surfaceHeight);
386
    }
387
388
///////////////////////////////////////////////////////////////////////////////////////////////////
389
/**
390
 * Call this so that the Library can release its internal data structures.
391
 * Must be called from Activity.onDestroy(). 
392
 */
393
  public static void onDestroy()
394
    {
395
    DistortedObjectList.release();
396 436899f2 Leszek Koltunski
    DistortedNode.release();
397
398 6a06a912 Leszek Koltunski
    EffectListVertex.reset();
399
    EffectListFragment.reset();
400 b3618cb5 Leszek Koltunski
    EffectListPreShader.reset();  // no need to reset PostShader stuff
401
402 6a06a912 Leszek Koltunski
    EffectMessageSender.stopSending();
403
   
404
    mInitialized = false;
405
    }
406
  
407
///////////////////////////////////////////////////////////////////////////////////////////////////
408
/**
409
 * Returns the true if onSurfaceCreated has been called already, and thus if the Library's is ready
410
 * to accept effect requests.
411
 * 
412
 * @return <code>true</code> if the Library is ready for action, <code>false</code> otherwise.
413
 */
414
  public static boolean isInitialized()
415
    {
416
    return mInitialized;  
417
    }
418
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420
/**
421 b3618cb5 Leszek Koltunski
 * Returns the maximum number of PreShader effects.
422 6a06a912 Leszek Koltunski
 *    
423 b3618cb5 Leszek Koltunski
 * @return The maximum number of PreShader effects
424 6a06a912 Leszek Koltunski
 */
425 b3618cb5 Leszek Koltunski
  public static int getMaxPreShader()
426 6a06a912 Leszek Koltunski
    {
427 b3618cb5 Leszek Koltunski
    return EffectListPreShader.getMax();
428 6a06a912 Leszek Koltunski
    }
429
 
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
/**
432
 * Returns the maximum number of Vertex effects.
433
 *    
434
 * @return The maximum number of Vertex effects
435
 */  
436
  public static int getMaxVertex()
437
    {
438
    return EffectListVertex.getMax();  
439
    }
440
  
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442
/**
443
 * Returns the maximum number of Fragment effects.
444
 *    
445
 * @return The maximum number of Fragment effects
446
 */  
447
  public static int getMaxFragment()
448
    {
449
    return EffectListFragment.getMax();  
450
    }
451
  
452
///////////////////////////////////////////////////////////////////////////////////////////////////
453
/**
454 b3618cb5 Leszek Koltunski
 * Sets the maximum number of PreShader effects that can be applied to a single DistortedBitmap at one time.
455 6a06a912 Leszek Koltunski
 * This can fail if the value of 'max' is outside permitted range. 
456
 * 
457 b3618cb5 Leszek Koltunski
 * @param max new maximum number of simultaneous PreShader Effects. Has to be a non-negative number not greater
458 6a06a912 Leszek Koltunski
 *            than Byte.MAX_VALUE 
459
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
460
 */
461 b3618cb5 Leszek Koltunski
  public static boolean setMaxPreShader(int max)
462 6a06a912 Leszek Koltunski
    {
463 b3618cb5 Leszek Koltunski
    return EffectListPreShader.setMax(max);
464 6a06a912 Leszek Koltunski
    }
465
  
466
///////////////////////////////////////////////////////////////////////////////////////////////////
467
/**
468
 * Sets the maximum number of Vertex effects that can be applied to a single DistortedBitmap at one time.
469
 * This can fail if:
470
 * <ul>
471
 * <li>the value of 'max' is outside permitted range
472
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
473
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
474
 *     time only decreasing the value of 'max' is permitted.  
475
 * </ul>
476
 * 
477
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
478
 *            than Byte.MAX_VALUE 
479
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
480
 */
481
  public static boolean setMaxVertex(int max)
482
    {
483
    return EffectListVertex.setMax(max);  
484
    }
485
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487
/**
488
 * Sets the maximum number of Fragment effects that can be applied to a single DistortedBitmap at one time.
489
 * This can fail if:
490
 * <ul>
491
 * <li>the value of 'max' is outside permitted range
492
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
493
 *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
494
 *     time only decreasing the value of 'max' is permitted.  
495
 * </ul>
496
 * 
497
 * @param max new maximum number of simultaneous Fragment Effects. Has to be a non-negative number not greater
498
 *            than Byte.MAX_VALUE 
499
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
500
 */
501
  public static boolean setMaxFragment(int max)
502
    {
503
    return EffectListFragment.setMax(max);  
504
    }
505
    
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507
//end of file  
508
}