Project

General

Profile

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

library / src / main / java / org / distorted / library / main / DistortedEffects.java @ 9e999930

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.main;
21

    
22
import android.content.res.Resources;
23
import android.opengl.GLES31;
24
import android.util.Log;
25

    
26
import org.distorted.library.R;
27
import org.distorted.library.effect.Effect;
28
import org.distorted.library.effect.EffectName;
29
import org.distorted.library.effect.EffectType;
30
import org.distorted.library.effect.FragmentEffect;
31
import org.distorted.library.effect.VertexEffect;
32
import org.distorted.library.message.EffectListener;
33
import org.distorted.library.program.DistortedProgram;
34

    
35
import java.io.InputStream;
36
import java.nio.ByteBuffer;
37
import java.nio.ByteOrder;
38
import java.nio.FloatBuffer;
39

    
40
///////////////////////////////////////////////////////////////////////////////////////////////////
41
/**
42
 * Class containing Matrix, Vertex, Fragment and Postprocessing effect queues.
43
 * <p>
44
 * The queues hold actual effects to be applied to a given (InputSurface,MeshObject) combo.
45
 */
46
public class DistortedEffects
47
  {
48
  /// MAIN PROGRAM ///
49
  private static DistortedProgram mMainProgram;
50
  private static int mMainTextureH;
51

    
52
  /// BLIT PROGRAM ///
53
  private static DistortedProgram mBlitProgram;
54
  private static int mBlitTextureH;
55
  private static int mBlitDepthH;
56
  private static final FloatBuffer mQuadPositions;
57

    
58
  static
59
    {
60
    float[] positionData= { -0.5f, -0.5f,  -0.5f, 0.5f,  0.5f,-0.5f,  0.5f, 0.5f };
61
    mQuadPositions = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()).asFloatBuffer();
62
    mQuadPositions.put(positionData).position(0);
63
    }
64

    
65
  /// BLIT DEPTH PROGRAM ///
66
  private static DistortedProgram mBlitDepthProgram;
67
  private static int mBlitDepthTextureH;
68
  private static int mBlitDepthDepthTextureH;
69
  private static int mBlitDepthDepthH;
70
  private static int mBlitDepthTexCorrH;
71

    
72
  /// NORMAL PROGRAM /////
73
  private static DistortedProgram mNormalProgram;
74
  private static int mNormalMVPMatrixH;
75
  /// END PROGRAMS //////
76

    
77
  private static long mNextID =0;
78
  private long mID;
79

    
80
  private EffectQueueMatrix mM;
81
  private EffectQueueFragment mF;
82
  private EffectQueueVertex mV;
83
  private EffectQueuePostprocess mP;
84

    
85
  private boolean matrixCloned, vertexCloned, fragmentCloned, postprocessCloned;
86

    
87
///////////////////////////////////////////////////////////////////////////////////////////////////
88

    
89
  static void createProgram(Resources resources)
90
    {
91
    // MAIN PROGRAM ////////////////////////////////////
92
    final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
93
    final InputStream mainFragStream = resources.openRawResource(R.raw.main_fragment_shader);
94

    
95
    int numF = FragmentEffect.getNumEnabled();
96
    int numV = VertexEffect.getNumEnabled();
97

    
98
    String mainVertHeader= Distorted.GLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n");
99
    String mainFragHeader= Distorted.GLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n");
100
    String enabledEffectV= VertexEffect.getGLSL();
101
    String enabledEffectF= FragmentEffect.getGLSL();
102

    
103
    //android.util.Log.e("Effects", "vertHeader= "+mainVertHeader);
104
    //android.util.Log.e("Effects", "fragHeader= "+mainFragHeader);
105
    //android.util.Log.e("Effects", "enabledV= "+enabledEffectV);
106
    //android.util.Log.e("Effects", "enabledF= "+enabledEffectF);
107

    
108
    String[] feedback = { "v_Position", "v_endPosition" };
109

    
110
    try
111
      {
112
      mMainProgram = new DistortedProgram(mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
113
                                          enabledEffectV, enabledEffectF, Distorted.GLSL, feedback);
114
      }
115
    catch(Exception e)
116
      {
117
      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile MAIN program: "+e.getMessage());
118
      throw new RuntimeException(e.getMessage());
119
      }
120

    
121
    int mainProgramH = mMainProgram.getProgramHandle();
122
    EffectQueueFragment.getUniforms(mainProgramH);
123
    EffectQueueVertex.getUniforms(mainProgramH);
124
    EffectQueueMatrix.getUniforms(mainProgramH);
125
    mMainTextureH= GLES31.glGetUniformLocation( mainProgramH, "u_Texture");
126

    
127
    // BLIT PROGRAM ////////////////////////////////////
128
    final InputStream blitVertStream = resources.openRawResource(R.raw.blit_vertex_shader);
129
    final InputStream blitFragStream = resources.openRawResource(R.raw.blit_fragment_shader);
130

    
131
    try
132
      {
133
      mBlitProgram = new DistortedProgram(blitVertStream,blitFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
134
      }
135
    catch(Exception e)
136
      {
137
      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT program: "+e.getMessage());
138
      throw new RuntimeException(e.getMessage());
139
      }
140

    
141
    int blitProgramH = mBlitProgram.getProgramHandle();
142
    mBlitTextureH  = GLES31.glGetUniformLocation( blitProgramH, "u_Texture");
143
    mBlitDepthH    = GLES31.glGetUniformLocation( blitProgramH, "u_Depth");
144

    
145
    // BLIT DEPTH PROGRAM ////////////////////////////////////
146
    final InputStream blitDepthVertStream = resources.openRawResource(R.raw.blit_depth_vertex_shader);
147
    final InputStream blitDepthFragStream = resources.openRawResource(R.raw.blit_depth_fragment_shader);
148

    
149
    try
150
      {
151
      mBlitDepthProgram = new DistortedProgram(blitDepthVertStream,blitDepthFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
152
      }
153
    catch(Exception e)
154
      {
155
      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
156
      throw new RuntimeException(e.getMessage());
157
      }
158

    
159
    int blitDepthProgramH   = mBlitDepthProgram.getProgramHandle();
160
    mBlitDepthTextureH      = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Texture");
161
    mBlitDepthDepthTextureH = GLES31.glGetUniformLocation( blitDepthProgramH, "u_DepthTexture");
162
    mBlitDepthDepthH        = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Depth");
163
    mBlitDepthTexCorrH      = GLES31.glGetUniformLocation( blitDepthProgramH, "u_TexCorr");
164

    
165
    // NORMAL PROGRAM //////////////////////////////////////
166
    final InputStream normalVertexStream   = resources.openRawResource(R.raw.normal_vertex_shader);
167
    final InputStream normalFragmentStream = resources.openRawResource(R.raw.normal_fragment_shader);
168

    
169
    try
170
      {
171
      mNormalProgram = new DistortedProgram(normalVertexStream,normalFragmentStream, Distorted.GLSL_VERSION, Distorted.GLSL_VERSION, Distorted.GLSL);
172
      }
173
    catch(Exception e)
174
      {
175
      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile NORMAL program: "+e.getMessage());
176
      throw new RuntimeException(e.getMessage());
177
      }
178

    
179
    int normalProgramH = mNormalProgram.getProgramHandle();
180
    mNormalMVPMatrixH  = GLES31.glGetUniformLocation( normalProgramH, "u_MVPMatrix");
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

    
185
  private void initializeEffectLists(DistortedEffects d, int flags)
186
    {
187
    if( (flags & Distorted.CLONE_MATRIX) != 0 )
188
      {
189
      mM = d.mM;
190
      matrixCloned = true;
191
      }
192
    else
193
      {
194
      mM = new EffectQueueMatrix(mID);
195
      matrixCloned = false;
196
      }
197
    
198
    if( (flags & Distorted.CLONE_VERTEX) != 0 )
199
      {
200
      mV = d.mV;
201
      vertexCloned = true;
202
      }
203
    else
204
      {
205
      mV = new EffectQueueVertex(mID);
206
      vertexCloned = false;
207
      }
208
    
209
    if( (flags & Distorted.CLONE_FRAGMENT) != 0 )
210
      {
211
      mF = d.mF;
212
      fragmentCloned = true;
213
      }
214
    else
215
      {
216
      mF = new EffectQueueFragment(mID);
217
      fragmentCloned = false;
218
      }
219

    
220
    if( (flags & Distorted.CLONE_POSTPROCESS) != 0 )
221
      {
222
      mP = d.mP;
223
      postprocessCloned = true;
224
      }
225
    else
226
      {
227
      mP = new EffectQueuePostprocess(mID);
228
      postprocessCloned = false;
229
      }
230
    }
231

    
232
///////////////////////////////////////////////////////////////////////////////////////////////////
233

    
234
  EffectQueuePostprocess getPostprocess()
235
    {
236
    return mP;
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

    
241
  void newNode(DistortedNode node)
242
    {
243
    mM.newNode(node);
244
    mF.newNode(node);
245
    mV.newNode(node);
246
    mP.newNode(node);
247
    }
248

    
249
///////////////////////////////////////////////////////////////////////////////////////////////////
250

    
251
  private void displayNormals(MeshObject mesh)
252
    {
253
    GLES31.glBindBufferBase(GLES31.GL_TRANSFORM_FEEDBACK_BUFFER, 0, mesh.mAttTFO[0]);
254
    GLES31.glBeginTransformFeedback( GLES31.GL_POINTS);
255
    DistortedRenderState.switchOffDrawing();
256
    GLES31.glDrawArrays( GLES31.GL_POINTS, 0, mesh.numVertices);
257
    DistortedRenderState.restoreDrawing();
258
    GLES31.glEndTransformFeedback();
259
    GLES31.glBindBufferBase(GLES31.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
260

    
261
    mNormalProgram.useProgram();
262
    GLES31.glUniformMatrix4fv(mNormalMVPMatrixH, 1, false, mM.getMVP() , 0);
263
    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttTFO[0]);
264
    GLES31.glVertexAttribPointer(mNormalProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, 0, 0);
265
    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
266
    GLES31.glLineWidth(8.0f);
267
    GLES31.glDrawArrays(GLES31.GL_LINES, 0, 2*mesh.numVertices);
268
    }
269

    
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

    
272
  void drawPriv(float halfW, float halfH, MeshObject mesh, DistortedOutputSurface surface, long currTime, float marginInPixels)
273
    {
274
    float halfZ = halfW*mesh.zFactor;
275

    
276
    mM.compute(currTime);
277
    mV.compute(currTime,halfW,halfH,halfZ);
278
    mF.compute(currTime,halfW,halfH);
279
    mP.compute(currTime);
280

    
281
    GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
282

    
283
    mMainProgram.useProgram();
284
    GLES31.glUniform1i(mMainTextureH, 0);
285

    
286
    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
287
    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
288
    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
289
    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
290
    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
291

    
292
    mM.send(surface,halfW,halfH,halfZ,marginInPixels);
293
    mV.send();
294
    mF.send();
295

    
296
    GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, mesh.numVertices);
297

    
298
    if( mesh.mShowNormals ) displayNormals(mesh);
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302
/**
303
 * Only for use by the library itself.
304
 *
305
 * @y.exclude
306
 */
307
  public static void blitPriv(DistortedOutputSurface surface)
308
    {
309
    mBlitProgram.useProgram();
310

    
311
    GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
312
    GLES31.glUniform1i(mBlitTextureH, 0);
313
    GLES31.glUniform1f( mBlitDepthH , 1.0f-surface.mNear);
314
    GLES31.glVertexAttribPointer(mBlitProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
315
    GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  static void blitDepthPriv(DistortedOutputSurface surface, float corrW, float corrH)
321
    {
322
    mBlitDepthProgram.useProgram();
323

    
324
    GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
325
    GLES31.glUniform1i(mBlitDepthTextureH, 0);
326
    GLES31.glUniform1i(mBlitDepthDepthTextureH, 1);
327
    GLES31.glUniform2f(mBlitDepthTexCorrH, corrW, corrH );
328
    GLES31.glUniform1f( mBlitDepthDepthH , 1.0f-surface.mNear);
329
    GLES31.glVertexAttribPointer(mBlitDepthProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
330
    GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334

    
335
  private void releasePriv()
336
    {
337
    if( !matrixCloned      ) mM.abortAll(false);
338
    if( !vertexCloned      ) mV.abortAll(false);
339
    if( !fragmentCloned    ) mF.abortAll(false);
340
    if( !postprocessCloned ) mP.abortAll(false);
341

    
342
    mM = null;
343
    mV = null;
344
    mF = null;
345
    mP = null;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
  static void onDestroy()
351
    {
352
    mNextID =  0;
353
    }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356
// PUBLIC API
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358
/**
359
 * Create empty effect queue.
360
 */
361
  public DistortedEffects()
362
    {
363
    mID = ++mNextID;
364
    initializeEffectLists(this,0);
365
    }
366

    
367
///////////////////////////////////////////////////////////////////////////////////////////////////
368
/**
369
 * Copy constructor.
370
 * <p>
371
 * Whatever we do not clone gets created just like in the default constructor.
372
 *
373
 * @param dc    Source object to create our object from
374
 * @param flags A bitmask of values specifying what to copy.
375
 *              For example, CLONE_VERTEX | CLONE_MATRIX.
376
 */
377
  public DistortedEffects(DistortedEffects dc, int flags)
378
    {
379
    mID = ++mNextID;
380
    initializeEffectLists(dc,flags);
381
    }
382

    
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384
/**
385
 * Releases all resources. After this call, the queue should not be used anymore.
386
 */
387
  @SuppressWarnings("unused")
388
  public synchronized void delete()
389
    {
390
    releasePriv();
391
    }
392

    
393
///////////////////////////////////////////////////////////////////////////////////////////////////
394
/**
395
 * Returns unique ID of this instance.
396
 *
397
 * @return ID of the object.
398
 */
399
  public long getID()
400
      {
401
      return mID;
402
      }
403

    
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405
/**
406
 * Adds the calling class to the list of Listeners that get notified each time some event happens 
407
 * to one of the Effects in our queues. Nothing will happen if 'el' is already in the list.
408
 * 
409
 * @param el A class implementing the EffectListener interface that wants to get notifications.
410
 */
411
  @SuppressWarnings("unused")
412
  public void registerForMessages(EffectListener el)
413
    {
414
    mM.registerForMessages(el);
415
    mV.registerForMessages(el);
416
    mF.registerForMessages(el);
417
    mP.registerForMessages(el);
418
    }
419

    
420
///////////////////////////////////////////////////////////////////////////////////////////////////
421
/**
422
 * Removes the calling class from the list of Listeners that get notified if something happens to Effects in our queue.
423
 * 
424
 * @param el A class implementing the EffectListener interface that no longer wants to get notifications.
425
 */
426
  @SuppressWarnings("unused")
427
  public void deregisterForMessages(EffectListener el)
428
    {
429
    mM.deregisterForMessages(el);
430
    mV.deregisterForMessages(el);
431
    mF.deregisterForMessages(el);
432
    mP.deregisterForMessages(el);
433
    }
434

    
435
///////////////////////////////////////////////////////////////////////////////////////////////////
436
/**
437
 * Aborts all Effects.
438
 * @return Number of effects aborted.
439
 */
440
  public int abortAllEffects()
441
    {
442
    return mM.abortAll(true) + mV.abortAll(true) + mF.abortAll(true);
443
    }
444

    
445
///////////////////////////////////////////////////////////////////////////////////////////////////
446
/**
447
 * Aborts all Effects of a given type, for example all MATRIX Effects.
448
 * 
449
 * @param type one of the constants defined in {@link EffectType}
450
 * @return Number of effects aborted.
451
 */
452
  public int abortByType(EffectType type)
453
    {
454
    switch(type)
455
      {
456
      case MATRIX     : return mM.abortAll(true);
457
      case VERTEX     : return mV.abortAll(true);
458
      case FRAGMENT   : return mF.abortAll(true);
459
      case POSTPROCESS: return mP.abortAll(true);
460
      default         : return 0;
461
      }
462
    }
463

    
464
///////////////////////////////////////////////////////////////////////////////////////////////////
465
/**
466
 * Aborts an Effect by its ID.
467
 *
468
 * @param id the Id of the Effect to be removed, as returned by getID().
469
 * @return Number of effects aborted.
470
 */
471
  public int abortById(long id)
472
    {
473
    long type = id&EffectType.MASK;
474

    
475
    if( type == EffectType.MATRIX.ordinal()      ) return mM.removeById(id);
476
    if( type == EffectType.VERTEX.ordinal()      ) return mV.removeById(id);
477
    if( type == EffectType.FRAGMENT.ordinal()    ) return mF.removeById(id);
478
    if( type == EffectType.POSTPROCESS.ordinal() ) return mP.removeById(id);
479

    
480
    return 0;
481
    }
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484
/**
485
 * Aborts a single Effect.
486
 * 
487
 * @param effect the Effect we want to abort.
488
 * @return number of Effects aborted. Always either 0 or 1.
489
 */
490
  public int abortEffect(Effect effect)
491
    {
492
    switch(effect.getType())
493
      {
494
      case MATRIX     : return mM.removeEffect(effect);
495
      case VERTEX     : return mV.removeEffect(effect);
496
      case FRAGMENT   : return mF.removeEffect(effect);
497
      case POSTPROCESS: return mP.removeEffect(effect);
498
      default         : return 0;
499
      }
500
    }
501

    
502
///////////////////////////////////////////////////////////////////////////////////////////////////
503
/**
504
 * Abort all Effects of a given name, for example all rotations.
505
 * 
506
 * @param name one of the constants defined in {@link EffectName}
507
 * @return number of Effects aborted.
508
 */
509
  public int abortByName(EffectName name)
510
    {
511
    switch(name.getType())
512
      {
513
      case MATRIX     : return mM.removeByName(name);
514
      case VERTEX     : return mV.removeByName(name);
515
      case FRAGMENT   : return mF.removeByName(name);
516
      case POSTPROCESS: return mP.removeByName(name);
517
      default                : return 0;
518
      }
519
    }
520

    
521
///////////////////////////////////////////////////////////////////////////////////////////////////
522
/**
523
 * Returns the maximum number of effects of a given type that can be simultaneously applied to a
524
 * single (InputSurface,MeshObject) combo.
525
 *
526
 * @param type {@link EffectType}
527
 * @return The maximum number of effects of a given type.
528
 */
529
  @SuppressWarnings("unused")
530
  public static int getMax(EffectType type)
531
    {
532
    return EffectQueue.getMax(type.ordinal());
533
    }
534

    
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536
/**
537
 * Sets the maximum number of effects that can be stored in a single EffectQueue at one time.
538
 * This can fail if:
539
 * <ul>
540
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
541
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
542
 *     before the Vertex Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
543
 *     time only decreasing the value of 'max' is permitted.
544
 * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
545
 * </ul>
546
 *
547
 * @param type {@link EffectType}
548
 * @param max new maximum number of simultaneous effects. Has to be a non-negative number not greater
549
 *            than Byte.MAX_VALUE
550
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
551
 */
552
  @SuppressWarnings("unused")
553
  public static boolean setMax(EffectType type, int max)
554
    {
555
    return EffectQueue.setMax(type.ordinal(),max);
556
    }
557

    
558
///////////////////////////////////////////////////////////////////////////////////////////////////
559
/**
560
 * Add a new Effect to our queue.
561
 *
562
 * @param effect The Effect to add.
563
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
564
 */
565
  public boolean apply(Effect effect)
566
    {
567
    switch(effect.getType())
568
      {
569
      case MATRIX      : return mM.add(effect);
570
      case VERTEX      : return mV.add(effect);
571
      case FRAGMENT    : return mF.add(effect);
572
      case POSTPROCESS : return mP.add(effect);
573
      }
574

    
575
    return false;
576
    }
577
  }
(2-2/21)