Project

General

Profile

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

library / src / main / java / org / distorted / library / Distorted.java @ 7845dc66

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 Matrix 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_MATRIX  = 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
   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
60
   * <p>
61
   * This is mainly useful for creating many similar sub-trees of Bitmaps and rendering then at different places
62
   * on the screen, with (optionally) different effects of the top-level root Bitmap.   
63
   */
64
  public static final int CLONE_CHILDREN= 0x10;
65
  /**
66
  * Constant used to represent a Matrix-based effect.
67
  */
68
  public static final int TYPE_MATR = 0x1;
69
  /**
70
   * Constant used to represent a Vertex-based effect.
71
   */
72
  public static final int TYPE_VERT = 0x2;
73
  /**
74
   * Constant used to represent a Fragment-based effect.
75
   */
76
  public static final int TYPE_FRAG = 0x4;
77
   
78
  private static final String TAG = Distorted.class.getSimpleName();
79
  private static boolean mInitialized = false;
80
  
81
  static int mPositionH;      // pass in model position information
82
  static int mColorH;         // pass in model color information
83
  static int mTextureUniformH;// pass in the texture.
84
  static int mNormalH;        // pass in model normal information.
85
  static int mTextureCoordH;  // pass in model texture coordinate information.
86
  static int mProgramH;       // This is a handle to our shading program.  
87

    
88
  static DistortedProjection mProjection = new DistortedProjection(false);
89
  static float mFOV = 60.0f;
90
  
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92

    
93
  private Distorted()
94
    {
95
    
96
    }
97

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

    
131
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
132
    {
133
    int shaderHandle = GLES20.glCreateShader(shaderType);
134

    
135
    if (shaderHandle != 0) 
136
      {
137
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
138
      GLES20.glCompileShader(shaderHandle);
139
      final int[] compileStatus = new int[1];
140
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
141

    
142
      if (compileStatus[0] != GLES20.GL_TRUE ) 
143
        {
144
        GLES20.glDeleteShader(shaderHandle);
145
        shaderHandle = 0;
146
        }
147
      }
148

    
149
    if (shaderHandle == 0)
150
      {     
151
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
152
     
153
      switch(shaderType)
154
        {
155
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
156
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
157
        default                       : throw new RuntimeException(error);
158
        }
159
      }
160

    
161
    return shaderHandle;
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165

    
166
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
167
    {
168
    int programHandle = GLES20.glCreateProgram();
169

    
170
    if (programHandle != 0) 
171
      {
172
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
173
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
174

    
175
      if (attributes != null)
176
        {
177
        final int size = attributes.length;
178

    
179
        for (int i = 0; i < size; i++)
180
          {
181
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
182
          }                
183
        }
184

    
185
      GLES20.glLinkProgram(programHandle);
186

    
187
      final int[] linkStatus = new int[1];
188
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
189

    
190
      if (linkStatus[0] != GLES20.GL_TRUE ) 
191
        {         
192
        String error = GLES20.glGetProgramInfoLog(programHandle);
193
        GLES20.glDeleteProgram(programHandle);
194
        throw new LinkingException(error);
195
        }
196
      
197
      final int[] numberOfUniforms = new int[1];
198
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
199

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

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

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

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409
/**
410
 * Returns the maximum number of Matrix effects.
411
 *    
412
 * @return The maximum number of Matrix effects
413
 */
414
  public static int getMaxMatrix()
415
    {
416
    return EffectListMatrix.getMax();  
417
    }
418
 
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420
/**
421
 * Returns the maximum number of Vertex effects.
422
 *    
423
 * @return The maximum number of Vertex effects
424
 */  
425
  public static int getMaxVertex()
426
    {
427
    return EffectListVertex.getMax();  
428
    }
429
  
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
/**
432
 * Returns the maximum number of Fragment effects.
433
 *    
434
 * @return The maximum number of Fragment effects
435
 */  
436
  public static int getMaxFragment()
437
    {
438
    return EffectListFragment.getMax();  
439
    }
440
  
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442
/**
443
 * Sets the maximum number of Matrix effects that can be applied to a single DistortedBitmap at one time.
444
 * This can fail if the value of 'max' is outside permitted range. 
445
 * 
446
 * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
447
 *            than Byte.MAX_VALUE 
448
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
449
 */
450
  public static boolean setMaxMatrix(int max)
451
    {
452
    return EffectListMatrix.setMax(max);  
453
    }
454
  
455
///////////////////////////////////////////////////////////////////////////////////////////////////
456
/**
457
 * Sets the maximum number of Vertex effects that can be applied to a single DistortedBitmap at one time.
458
 * This can fail if:
459
 * <ul>
460
 * <li>the value of 'max' is outside permitted range
461
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
462
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
463
 *     time only decreasing the value of 'max' is permitted.  
464
 * </ul>
465
 * 
466
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
467
 *            than Byte.MAX_VALUE 
468
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
469
 */
470
  public static boolean setMaxVertex(int max)
471
    {
472
    return EffectListVertex.setMax(max);  
473
    }
474

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