Project

General

Profile

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

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

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 DistortedObject 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 Matrix 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_MATRIX = 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
   * When creating an instance of a DistortedNode from another instance, clone the children Nodes.
79
   * <p>
80
   * This is mainly useful for creating many similar sub-trees of Bitmaps and rendering then at different places
81
   * on the screen, with (optionally) different effects of the top-level root Bitmap.   
82
   */
83
  public static final int CLONE_CHILDREN= 0x10;
84

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

    
95
  static DistortedProjection mProjection = new DistortedProjection(false);
96
  static float mFOV = 60.0f;
97
  
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

    
100
  private Distorted()
101
    {
102
    
103
    }
104

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

    
138
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
139
    {
140
    int shaderHandle = GLES20.glCreateShader(shaderType);
141

    
142
    if (shaderHandle != 0) 
143
      {
144
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
145
      GLES20.glCompileShader(shaderHandle);
146
      final int[] compileStatus = new int[1];
147
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
148

    
149
      if (compileStatus[0] != GLES20.GL_TRUE ) 
150
        {
151
        GLES20.glDeleteShader(shaderHandle);
152
        shaderHandle = 0;
153
        }
154
      }
155

    
156
    if (shaderHandle == 0)
157
      {     
158
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
159
     
160
      switch(shaderType)
161
        {
162
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
163
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
164
        default                       : throw new RuntimeException(error);
165
        }
166
      }
167

    
168
    return shaderHandle;
169
    }
170

    
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172

    
173
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
174
    {
175
    int programHandle = GLES20.glCreateProgram();
176

    
177
    if (programHandle != 0) 
178
      {
179
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
180
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
181

    
182
      if (attributes != null)
183
        {
184
        final int size = attributes.length;
185

    
186
        for (int i = 0; i < size; i++)
187
          {
188
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
189
          }                
190
        }
191

    
192
      GLES20.glLinkProgram(programHandle);
193

    
194
      final int[] linkStatus = new int[1];
195
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
196

    
197
      if (linkStatus[0] != GLES20.GL_TRUE ) 
198
        {         
199
        String error = GLES20.glGetProgramInfoLog(programHandle);
200
        GLES20.glDeleteProgram(programHandle);
201
        throw new LinkingException(error);
202
        }
203
      
204
      final int[] numberOfUniforms = new int[1];
205
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
206

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

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286
// Public API
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288
/**
289
 * Sets Vertical Field of View angle. This changes the Projection Matrix.  
290
 *   
291
 * @param fov Vertical Field Of View angle, in degrees. If T is the middle of the top edge of the 
292
 *            screen, E is the eye point, and B is the middle of the bottom edge of the screen, then 
293
 *            fov = angle(TEB)
294
 */
295
  public static void setFov(float fov)
296
    {
297
    mFOV = fov;
298
   
299
    if( mProjection.width>0 && mProjection.height>0 )
300
      mProjection.onSurfaceChanged( mProjection.width, mProjection.height);
301
    }
302
  
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304
/**
305
 * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
306
 * I.e. best called from GLSurfaceView.onSurfaceCreated().
307
 * <p>
308
 * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms, and initialises all Bitmaps that have already
309
 * been created.
310
 *   
311
 * @param context Context of the App using the library - used to open up Resources and read Shader code.
312
 * @throws FragmentCompilationException
313
 * @throws VertexCompilationException
314
 * @throws VertexUniformsException
315
 * @throws FragmentUniformsException
316
 * @throws LinkingException
317
 */
318
  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
319
    { 
320
    mInitialized = true;  
321
     
322
// String ver;  
323
    
324
    final String vertexShader   = Distorted.getVertexShader(context);
325
    final String fragmentShader = Distorted.getFragmentShader(context);
326
/*
327
    ver = GLES20.glGetString(GLES20.GL_SHADING_LANGUAGE_VERSION);    
328
    Log.d(TAG, "GLSL version: "+(ver==null ? "null" : ver) );
329
    ver = GLES20.glGetString(GLES20.GL_VERSION);   
330
    Log.d(TAG, "GL version: "+(ver==null ? "null" : ver) );
331
    ver = GLES20.glGetString(GLES20.GL_VENDOR);    
332
    Log.d(TAG, "GL vendor: "+(ver==null ? "null" : ver) );
333
    ver = GLES20.glGetString(GLES20.GL_RENDERER);  
334
    Log.d(TAG, "GL renderer: "+(ver==null ? "null" : ver) );
335
*/
336
    //ver = GLES20.glGetString(GLES20.GL_EXTENSIONS);    
337
    //Log.d(TAG, "GL extensions: "+(ver==null ? "null" : ver) );
338
    
339
    sanitizeMaxValues();
340
    
341
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
342
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
343
      
344
    mProgramH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});                                                            
345
      
346
    GLES20.glUseProgram(mProgramH);
347
    GLES20.glEnable (GLES20.GL_DEPTH_TEST);
348
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
349
    GLES20.glEnable(GLES20.GL_BLEND);
350
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
351

    
352
    mTextureUniformH = GLES20.glGetUniformLocation(mProgramH, "u_Texture");
353
    
354
    mPositionH       = GLES20.glGetAttribLocation( mProgramH, "a_Position");
355
    mColorH          = GLES20.glGetAttribLocation( mProgramH, "a_Color");
356
    mNormalH         = GLES20.glGetAttribLocation( mProgramH, "a_Normal"); 
357
    mTextureCoordH   = GLES20.glGetAttribLocation( mProgramH, "a_TexCoordinate");
358
    
359
    EffectQueueFragment.getUniforms(mProgramH);
360
    EffectQueueVertex.getUniforms(mProgramH);
361
    EffectQueueMatrix.getUniforms(mProgramH);
362
    
363
    GLES20.glEnableVertexAttribArray(mPositionH);        
364
    GLES20.glEnableVertexAttribArray(mColorH);
365
    GLES20.glEnableVertexAttribArray(mNormalH);
366
    GLES20.glEnableVertexAttribArray(mTextureCoordH);
367
   
368
    DistortedObjectList.reset();
369
    DistortedNode.reset();
370
    EffectMessageSender.startSending();
371
    }
372
  
373
///////////////////////////////////////////////////////////////////////////////////////////////////
374
/**
375
 * Call this when the physical size of the surface we are rendering to changes.
376
 * I.e. must be called from GLSurfaceView.onSurfaceChanged()  
377
 *   
378
 * @param surfaceWidth  new width of the surface.
379
 * @param surfaceHeight new height of the surface.
380
 */
381
  public static void onSurfaceChanged(int surfaceWidth, int surfaceHeight)
382
    {
383
    mProjection.onSurfaceChanged(surfaceWidth, surfaceHeight);
384
    }
385

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

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

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

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

    
500
///////////////////////////////////////////////////////////////////////////////////////////////////
501
//end of file  
502
}
(1-1/17)