Project

General

Profile

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

library / src / main / java / org / distorted / library / Distorted.java @ 98455aa2

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 mTextureUniformH;  // pass in the texture.
90
  static int mNormalH;          // pass in model normal information.
91
  static int mTextureCoordH;    // pass in model texture coordinate information.
92
  static int mProgramH;         // This is a handle to our shading program.
93

    
94
  static DistortedFramebuffer mFramebuffer = new DistortedFramebuffer(0); // output to the screen
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

    
98
  private Distorted()
99
    {
100
    
101
    }
102

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

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

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

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

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

    
166
    return shaderHandle;
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

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

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

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

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

    
190
      GLES20.glLinkProgram(programHandle);
191

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

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

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

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284
// Public API
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286
/**
287
 * Sets Vertical Field of View angle and the point the camera is looking at. This changes the Projection Matrix.
288
 *   
289
 * @param fov Vertical Field Of View angle, in degrees. If T is the middle of the top edge of the 
290
 *            screen, E is the eye point, and B is the middle of the bottom edge of the screen, then 
291
 *            fov = angle(TEB)
292
 * @param x   X-coordinate of the point the camera is looking at. -scrWidth/2 &lt; x &lt; scrWidth/2
293
 * @param y   Y-coordinate of the point the camera is looking at. -scrHeight/2 &lt; y &lt; scrHeight/2
294
 */
295
  public static void setProjection(float fov, float x, float y)
296
    {
297
    mFramebuffer.setProjection(fov,x,y);
298
    }
299
  
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301
/**
302
 * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
303
 * I.e. best called from GLSurfaceView.onSurfaceCreated().
304
 * <p>
305
 * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms, and initialises all Bitmaps that have already
306
 * been created.
307
 *   
308
 * @param context Context of the App using the library - used to open up Resources and read Shader code.
309
 * @throws FragmentCompilationException
310
 * @throws VertexCompilationException
311
 * @throws VertexUniformsException
312
 * @throws FragmentUniformsException
313
 * @throws LinkingException
314
 */
315
  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
316
    { 
317
    mInitialized = true;  
318

    
319
    final String vertexShader   = Distorted.getVertexShader(context);
320
    final String fragmentShader = Distorted.getFragmentShader(context);
321

    
322
    sanitizeMaxValues();
323
    
324
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
325
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
326
      
327
    mProgramH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});                                                            
328
      
329
    GLES20.glUseProgram(mProgramH);
330
    GLES20.glEnable (GLES20.GL_DEPTH_TEST);
331
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
332
    GLES20.glEnable(GLES20.GL_BLEND);
333
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
334
    GLES20.glEnable(GLES20.GL_CULL_FACE);
335
    GLES20.glCullFace(GLES20.GL_BACK);
336
    GLES20.glFrontFace(GLES20.GL_CW);
337

    
338
    mTextureUniformH = GLES20.glGetUniformLocation(mProgramH, "u_Texture");
339
    
340
    mPositionH       = GLES20.glGetAttribLocation( mProgramH, "a_Position");
341
    mNormalH         = GLES20.glGetAttribLocation( mProgramH, "a_Normal");
342
    mTextureCoordH   = GLES20.glGetAttribLocation( mProgramH, "a_TexCoordinate");
343
    
344
    EffectQueueFragment.getUniforms(mProgramH);
345
    EffectQueueVertex.getUniforms(mProgramH);
346
    EffectQueueMatrix.getUniforms(mProgramH);
347
    
348
    GLES20.glEnableVertexAttribArray(mPositionH);        
349
    GLES20.glEnableVertexAttribArray(mNormalH);
350
    GLES20.glEnableVertexAttribArray(mTextureCoordH);
351
   
352
    DistortedObject.reset();
353
    DistortedNode.reset();
354
    EffectMessageSender.startSending();
355
    }
356
  
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358
/**
359
 * Call this when the physical size of the surface we are rendering to changes.
360
 * I.e. must be called from GLSurfaceView.onSurfaceChanged()  
361
 *   
362
 * @param surfaceWidth  new width of the surface.
363
 * @param surfaceHeight new height of the surface.
364
 */
365
  public static void onSurfaceChanged(int surfaceWidth, int surfaceHeight)
366
    {
367
    mFramebuffer.resize(surfaceWidth,surfaceHeight);
368
    }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371
/**
372
 * Call this so that the Library can release its internal data structures.
373
 * Must be called from Activity.onDestroy(). 
374
 */
375
  public static void onDestroy()
376
    {
377
    DistortedGridFactory.release();
378
    DistortedObject.release();
379
    DistortedFramebuffer.release();
380
    DistortedNode.release();
381
    EffectQueue.release();
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
 * Sets the maximum number of Matrix effects that can be applied to a single DistortedObject at one time.
435
 * This can fail if:
436
 * <ul>
437
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
438
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
439
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
440
 *     time only decreasing the value of 'max' is permitted.
441
 * <li>Furthermore, this needs to be called before any DistortedObject gets created.
442
 * </ul>
443
 * 
444
 * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
445
 *            than Byte.MAX_VALUE 
446
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
447
 */
448
  public static boolean setMaxMatrix(int max)
449
    {
450
    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
451
    }
452
  
453
///////////////////////////////////////////////////////////////////////////////////////////////////
454
/**
455
 * Sets the maximum number of Vertex effects that can be applied to a single DistortedObject at one time.
456
 * This can fail if:
457
 * <ul>
458
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
459
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
460
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
461
 *     time only decreasing the value of 'max' is permitted.
462
* <li>Furthermore, this needs to be called before any DistortedObject gets created.
463
 * </ul>
464
 * 
465
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
466
 *            than Byte.MAX_VALUE 
467
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
468
 */
469
  public static boolean setMaxVertex(int max)
470
    {
471
    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475
/**
476
 * Sets the maximum number of Fragment effects that can be applied to a single DistortedObject at one time.
477
 * This can fail if:
478
 * <ul>
479
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
480
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
481
 *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
482
 *     time only decreasing the value of 'max' is permitted.
483
 * <li>Furthermore, this needs to be called before any DistortedObject gets created.
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 EffectQueue.setMax(EffectTypes.FRAGMENT.ordinal(),max);
493
    }
494
  }
(1-1/17)