Project

General

Profile

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

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

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

    
31
import org.distorted.library.exception.*;
32

    
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
/**
35
 * A singleton class used to control various global settings.
36
 */
37
public class Distorted 
38
  {
39
  /**
40
   * When creating an instance of a DistortedTexture (or Tree) from another instance, do not clone anything.
41
   * Used in the copy constructor.
42
   */
43
  public static final int CLONE_NOTHING = 0x0;
44
  /**
45
   * When creating an instance of a DistortedTexture from another instance, clone the Bitmap that's
46
   * backing up our DistortedTexture.
47
   * <p>
48
   * This way we can have two DistortedTextures, both backed up by the same Bitmap, to which we can
49
   * apply different effects. Used in the copy constructor.
50
   */
51
  public static final int CLONE_BITMAP  = 0x1;
52
  /**
53
   * When creating an instance of a DistortedEffects from another instance, clone the Matrix Effects.
54
   * <p>
55
   * This way we can have two different DistortedEffects sharing the MATRIX queue.
56
   */
57
  public static final int CLONE_MATRIX = 0x2;
58
  /**
59
   * When creating an instance of a DistortedEffects from another instance, clone the Vertex Effects.
60
   * <p>
61
   * This way we can have two different DistortedEffects sharing the VERTEX queue.
62
   */
63
  public static final int CLONE_VERTEX  = 0x4;
64
  /**
65
   * When creating an instance of a DistortedEffects from another instance, clone the Fragment Effects.
66
   * <p>
67
   * This way we can have two different DistortedEffects sharing the FRAGMENT queue.
68
   */
69
  public static final int CLONE_FRAGMENT= 0x8;
70
  /**
71
   * When creating an instance of a DistortedTree from another instance, clone the children Nodes.
72
   * <p>
73
   * This is mainly useful for creating many similar sub-trees and rendering then at different places
74
   * on the screen with (optionally) different Effects.
75
   */
76
  public static final int CLONE_CHILDREN= 0x10;
77

    
78
  private static boolean mInitialized = false;
79

    
80
  static int mPositionH;       // model position information
81
  static int mNormalH;         // model normal information.
82
  static int mTextureCoordH;   // model texture coordinate information.
83

    
84
///////////////////////////////////////////////////////////////////////////////////////////////////
85

    
86
  private Distorted()
87
    {
88
    
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92
  
93
  private static void sanitizeMaxValues() throws VertexUniformsException,FragmentUniformsException
94
    {
95
    int maxV,maxF;  
96
    int[] param = new int[1];
97
    
98
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
99
    maxV = param[0];
100
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
101
    maxF = param[0];
102
    
103
    //Log.d("Distorted", "Max vectors in vertex shader: "+maxV);
104
    //Log.d("Distorted", "Max vectors in fragment shader: "+maxF);
105
    
106
    if( !Build.FINGERPRINT.contains("generic") )
107
      {
108
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
109
      int realMaxF = (maxF- 2)/4;   //
110
    
111
      if( getMaxVertex()   > realMaxV )
112
        {
113
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
114
        }
115
      if( getMaxFragment() > realMaxF )
116
        {
117
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
118
        }
119
      }
120
    }
121
  
122
///////////////////////////////////////////////////////////////////////////////////////////////////
123

    
124
  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
125
    {
126
    int shaderHandle = GLES20.glCreateShader(shaderType);
127

    
128
    if (shaderHandle != 0) 
129
      {
130
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
131
      GLES20.glCompileShader(shaderHandle);
132
      final int[] compileStatus = new int[1];
133
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
134

    
135
      if (compileStatus[0] != GLES20.GL_TRUE ) 
136
        {
137
        GLES20.glDeleteShader(shaderHandle);
138
        shaderHandle = 0;
139
        }
140
      }
141

    
142
    if (shaderHandle == 0)
143
      {     
144
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
145
     
146
      switch(shaderType)
147
        {
148
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
149
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
150
        default                       : throw new RuntimeException(error);
151
        }
152
      }
153

    
154
    return shaderHandle;
155
    }
156

    
157
///////////////////////////////////////////////////////////////////////////////////////////////////
158

    
159
  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
160
    {
161
    int programHandle = GLES20.glCreateProgram();
162

    
163
    if (programHandle != 0) 
164
      {
165
      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
166
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
167

    
168
      if (attributes != null)
169
        {
170
        final int size = attributes.length;
171

    
172
        for (int i = 0; i < size; i++)
173
          {
174
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
175
          }                
176
        }
177

    
178
      GLES20.glLinkProgram(programHandle);
179

    
180
      final int[] linkStatus = new int[1];
181
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
182

    
183
      if (linkStatus[0] != GLES20.GL_TRUE ) 
184
        {         
185
        String error = GLES20.glGetProgramInfoLog(programHandle);
186
        GLES20.glDeleteProgram(programHandle);
187
        throw new LinkingException(error);
188
        }
189
      
190
      final int[] numberOfUniforms = new int[1];
191
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
192

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

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  static boolean isInitialized()
274
    {
275
    return mInitialized;
276
    }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279
/**
280
 * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
281
 * I.e. best called from GLSurfaceView.onSurfaceCreated().
282
 * <p>
283
 * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms.
284
 *   
285
 * @param context Context of the App using the library - used to open up Resources and read Shader code.
286
 * @throws FragmentCompilationException
287
 * @throws VertexCompilationException
288
 * @throws VertexUniformsException
289
 * @throws FragmentUniformsException
290
 * @throws LinkingException
291
 */
292
  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
293
    { 
294
    mInitialized = true;  
295

    
296
    final String vertexShader   = Distorted.getVertexShader(context);
297
    final String fragmentShader = Distorted.getFragmentShader(context);
298

    
299
    sanitizeMaxValues();
300
    
301
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
302
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
303
      
304
    int programH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});
305

    
306
    GLES20.glUseProgram(programH);
307

    
308
    int textureUniformH = GLES20.glGetUniformLocation(programH, "u_Texture");
309
    mPositionH          = GLES20.glGetAttribLocation( programH, "a_Position");
310
    mNormalH            = GLES20.glGetAttribLocation( programH, "a_Normal");
311
    mTextureCoordH      = GLES20.glGetAttribLocation( programH, "a_TexCoordinate");
312

    
313
    GLES20.glEnable (GLES20.GL_DEPTH_TEST);
314
    GLES20.glDepthFunc(GLES20.GL_LEQUAL);
315
    GLES20.glEnable(GLES20.GL_BLEND);
316
    GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
317
    GLES20.glEnable(GLES20.GL_CULL_FACE);
318
    GLES20.glCullFace(GLES20.GL_BACK);
319
    GLES20.glFrontFace(GLES20.GL_CW);
320
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
321
    GLES20.glUniform1i(textureUniformH, 0);
322

    
323
    EffectQueueFragment.getUniforms(programH);
324
    EffectQueueVertex.getUniforms(programH);
325
    EffectQueueMatrix.getUniforms(programH);
326
    
327
    GLES20.glEnableVertexAttribArray(mPositionH);        
328
    GLES20.glEnableVertexAttribArray(mNormalH);
329
    GLES20.glEnableVertexAttribArray(mTextureCoordH);
330
   
331
    DistortedTree.reset();
332
    EffectMessageSender.startSending();
333
    }
334

    
335
///////////////////////////////////////////////////////////////////////////////////////////////////
336
/**
337
 * Call this so that the Library can release its internal data structures.
338
 * Must be called from Activity.onDestroy(). 
339
 */
340
  public static void onDestroy()
341
    {
342
    DistortedTexture.onDestroy();
343
    DistortedFramebuffer.onDestroy();
344
    DistortedTree.onDestroy();
345
    EffectQueue.onDestroy();
346
    DistortedEffects.onDestroy();
347
    EffectMessageSender.stopSending();
348
   
349
    mInitialized = false;
350
    }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353
/**
354
 * Returns the maximum number of Matrix effects.
355
 *    
356
 * @return The maximum number of Matrix effects
357
 */
358
  public static int getMaxMatrix()
359
    {
360
    return EffectQueue.getMax(EffectTypes.MATRIX.ordinal());
361
    }
362
 
363
///////////////////////////////////////////////////////////////////////////////////////////////////
364
/**
365
 * Returns the maximum number of Vertex effects.
366
 *    
367
 * @return The maximum number of Vertex effects
368
 */  
369
  public static int getMaxVertex()
370
    {
371
    return EffectQueue.getMax(EffectTypes.VERTEX.ordinal());
372
    }
373
  
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375
/**
376
 * Returns the maximum number of Fragment effects.
377
 *    
378
 * @return The maximum number of Fragment effects
379
 */  
380
  public static int getMaxFragment()
381
    {
382
    return EffectQueue.getMax(EffectTypes.FRAGMENT.ordinal());
383
    }
384

    
385
///////////////////////////////////////////////////////////////////////////////////////////////////
386
/**
387
 * Sets the maximum number of Matrix effects that can be stored in a single EffectQueue at one time.
388
 * This can fail if:
389
 * <ul>
390
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
391
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
392
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
393
 *     time only decreasing the value of 'max' is permitted.
394
 * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
395
 * </ul>
396
 * 
397
 * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
398
 *            than Byte.MAX_VALUE 
399
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
400
 */
401
  public static boolean setMaxMatrix(int max)
402
    {
403
    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
404
    }
405
  
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407
/**
408
 * Sets the maximum number of Vertex effects that can be stored in a single EffectQueue at one time.
409
 * This can fail if:
410
 * <ul>
411
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
412
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
413
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
414
 *     time only decreasing the value of 'max' is permitted.
415
* <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
416
 * </ul>
417
 * 
418
 * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
419
 *            than Byte.MAX_VALUE 
420
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
421
 */
422
  public static boolean setMaxVertex(int max)
423
    {
424
    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428
/**
429
 * Sets the maximum number of Fragment effects that can be stored in a single EffectQueue at one time.
430
 * This can fail if:
431
 * <ul>
432
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
433
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
434
 *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
435
 *     time only decreasing the value of 'max' is permitted.
436
 * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
437
 * </ul>
438
 * 
439
 * @param max new maximum number of simultaneous Fragment Effects. Has to be a non-negative number not greater
440
 *            than Byte.MAX_VALUE 
441
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
442
 */
443
  public static boolean setMaxFragment(int max)
444
    {
445
    return EffectQueue.setMax(EffectTypes.FRAGMENT.ordinal(),max);
446
    }
447
  }
(1-1/15)