Project

General

Profile

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

library / src / main / java / org / distorted / library / Distorted.java @ 6a06a912

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.opengl.GLSurfaceView;
9
import android.opengl.GLES20;
10
import android.os.Build;
11
import android.util.Log;
12

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

    
16
///////////////////////////////////////////////////////////////////////////////////////////////////
17
/**
18
 * A singleton class used to control various global settings.
19
 */
20
public class Distorted 
21
{
22
  /**
23
   * When creating an instance of a DistortedBitmap from another instance, do not clone anything.
24
   * Used in the copy constructor.
25
   */
26
  public static final int CLONE_NOTHING = 0x0;
27
  /**
28
   * When creating an instance of a DistortedObject from another instance, clone the Bitmap that's
29
   * backing up our DistortedObject. 
30
   * <p>
31
   * This way we can have two DistortedObjects, both backed up by the same Bitmap, to which we can 
32
   * apply different effects. Used in the copy constructor.
33
   */
34
  public static final int CLONE_BITMAP  = 0x1;
35
  /**
36
   * When creating an instance of a DistortedObject from another instance, clone the Matrix Effects.
37
   * <p>
38
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
39
   * always displayed in exactly the same place on the screen. Applying any matrix-based effect to 
40
   * one of them automatically applies the effect to the other. Used in the copy constructor.
41
   */
42
  public static final int CLONE_MATRIX  = 0x2;
43
  /**
44
   * When creating an instance of a DistortedObject from another instance, clone the Vertex Effects.
45
   * <p>
46
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
47
   * always with the same Vertex effects. Applying any vertex-based effect to one of them automatically 
48
   * applies the effect to the other. Used in the copy constructor.
49
   */
50
  public static final int CLONE_VERTEX  = 0x4;
51
  /**
52
   * When creating an instance of a DistortedObject from another instance, clone the Fragment Effects.
53
   * <p>
54
   * This way we can have two different DistortedObjects with different Bitmaps behind them, both 
55
   * always with the same Fragment effects. Applying any fragment-based effect to one of them automatically 
56
   * applies the effect to the other. Used in the copy constructor.
57
   */
58
  public static final int CLONE_FRAGMENT= 0x8;
59
  /**
60
   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
61
   * <p>
62
   * This is mainly useful for creating many similar sub-trees of Bitmaps and rendering then at different places
63
   * on the screen, with (optionally) different effects of the top-level root Bitmap.   
64
   */
65
  public static final int CLONE_CHILDREN= 0x10;
66
  /**
67
  * Constant used to represent a Matrix-based effect.
68
  */
69
  public static final int TYPE_MATR = 0x1;
70
  /**
71
   * Constant used to represent a Vertex-based effect.
72
   */
73
  public static final int TYPE_VERT = 0x2;
74
  /**
75
   * Constant used to represent a Fragment-based effect.
76
   */
77
  public static final int TYPE_FRAG = 0x4;
78
   
79
  private static final String TAG = Distorted.class.getSimpleName();
80
  private static boolean mInitialized = false;
81
  
82
  static int mPositionH;      // pass in model position information
83
  static int mColorH;         // pass in model color information
84
  static int mTextureUniformH;// pass in the texture.
85
  static int mNormalH;        // pass in model normal information.
86
  static int mTextureCoordH;  // pass in model texture coordinate information.
87
  static int mProgramH;       // This is a handle to our shading program.  
88

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

    
94
  private Distorted()
95
    {
96
    
97
    }
98

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

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

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

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

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

    
162
    return shaderHandle;
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

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

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

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

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

    
186
      GLES20.glLinkProgram(programHandle);
187

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

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

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

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

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

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

    
476
///////////////////////////////////////////////////////////////////////////////////////////////////
477
/**
478
 * Sets the maximum number of Fragment effects that can be applied to a single DistortedBitmap 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 Fragment 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 Fragment 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 setMaxFragment(int max)
492
    {
493
    return EffectListFragment.setMax(max);  
494
    }
495
    
496
///////////////////////////////////////////////////////////////////////////////////////////////////
497
//end of file  
498
}
(1-1/28)