Project

General

Profile

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

library / src / main / java / org / distorted / library / DistortedEffectsPostprocess.java @ 310e14fb

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 org.distorted.library.effect.EffectQueue;
23
import org.distorted.library.effect.EffectQueuePostprocess;
24
import org.distorted.library.message.EffectListener;
25
import org.distorted.library.type.Data1D;
26
import org.distorted.library.type.Data4D;
27

    
28
import java.util.ArrayList;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31

    
32
/**
33
 * Class containing the queue of postprocessing effects.
34
 * <p>
35
 * This better be separate from the main DistortedEffects class, because we want to be able to apply
36
 * the same postprocessing effects (i.e. not only the same effects, but the very same instance of this
37
 * object) to several Children of a Node. Reason for that: if several children have the same Object,
38
 * it is easy to group them into 'Buckets' where each bucket has the same postprocessing effects and
39
 * is therefore rendered to the same buffer, which is then postprocessed in one go.
40
 * <p>
41
 * The queue holds actual effects to be applied to a given bucket of several (DistortedTexture,MeshObject) combos.
42
 */
43
public class DistortedEffectsPostprocess implements DistortedSlave
44
  {
45
  private static final int MIPMAP = 0;
46

    
47
  private static long mNextID =0;
48
  private long mID;
49

    
50
  private EffectQueuePostprocess mP;
51

    
52
  private boolean postprocessCloned;
53

    
54
  private class Job
55
    {
56
    int type;
57
    int level;
58

    
59
    Job(int t, int l)
60
      {
61
      type = t;
62
      level= l;
63
      }
64
    }
65

    
66
  private ArrayList<Job> mJobs = new ArrayList<>();
67

    
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69

    
70
  private void initializeEffectLists(DistortedEffectsPostprocess d, int flags)
71
    {
72
    if( (flags & Distorted.CLONE_POSTPROCESS) != 0 )
73
      {
74
      mP = d.mP;
75
      postprocessCloned = true;
76
      }
77
    else
78
      {
79
      mP = new EffectQueuePostprocess(mID);
80
      postprocessCloned = false;
81
      }
82
    }
83

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

    
86
  int postprocess(long time, DistortedOutputSurface surface)
87
    {
88
    return mP.postprocess(time,surface);
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92

    
93
  long getBucket()
94
    {
95
    return mP.mNumEffects==0 ? 0: mID;
96
    }
97

    
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

    
100
  private void releasePriv()
101
    {
102
    if( !postprocessCloned) mP.abortAll(false);
103

    
104
    mP = null;
105
    }
106

    
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108

    
109
  int getQuality()
110
    {
111
    return mP.mQualityLevel;
112
    }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
  int getHalo()
117
    {
118
    return mP.getHalo();
119
    }
120

    
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122

    
123
  static void onDestroy()
124
    {
125
    mNextID = 0;
126
    }
127

    
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129
// PUBLIC API
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131
/**
132
 * Create empty effect queue.
133
 */
134
  public DistortedEffectsPostprocess()
135
    {
136
    mID = ++mNextID;
137
    initializeEffectLists(this,0);
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141
/**
142
 * Copy constructor.
143
 * <p>
144
 * Whatever we do not clone gets created just like in the default constructor.
145
 *
146
 * @param dc    Source object to create our object from
147
 * @param flags A bitmask of values specifying what to copy.
148
 *              Currently the only values possible are CLONE_NOTHING or CLONE_POSTPROCESS.
149
 */
150
  public DistortedEffectsPostprocess(DistortedEffectsPostprocess dc, int flags)
151
    {
152
    mID = ++mNextID;
153
    initializeEffectLists(dc,flags);
154
    }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157
/**
158
 * This is not really part of the public API. Has to be public only because it is a part of the
159
 * DistortedSlave interface, which should really be a class that we extend here instead but
160
 * Java has no multiple inheritance.
161
 *
162
 * @y.exclude
163
 */
164
  public void doWork()
165
    {
166
    int num = mJobs.size();
167
    Job job;
168

    
169
    for(int i=0; i<num; i++)
170
      {
171
      job = mJobs.remove(0);
172

    
173
      switch(job.type)
174
        {
175
        case MIPMAP: int level = job.level;
176
                     mP.mQualityLevel = level;
177
                     mP.mQualityScale = 1.0f;
178
                     for(int j=0; j<level; j++) mP.mQualityScale*=EffectQuality.MULTIPLIER;
179
                     break;
180
        }
181
      }
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185
/**
186
 * Releases all resources. After this call, the queue should not be used anymore.
187
 */
188
  @SuppressWarnings("unused")
189
  public synchronized void delete()
190
    {
191
    releasePriv();
192
    }
193

    
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195
/**
196
 * Returns unique ID of this instance.
197
 *
198
 * @return ID of the object.
199
 */
200
  @SuppressWarnings("unused")
201
  public long getID()
202
      {
203
      return mID;
204
      }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207
/**
208
 * Adds the calling class to the list of Listeners that get notified each time some event happens 
209
 * to one of the Effects in the queues. Nothing will happen if 'el' is already in the list.
210
 * 
211
 * @param el A class implementing the EffectListener interface that wants to get notifications.
212
 */
213
  @SuppressWarnings("unused")
214
  public void registerForMessages(EffectListener el)
215
    {
216
    mP.registerForMessages(el);
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220
/**
221
 * Removes the calling class from the list of Listeners.
222
 * 
223
 * @param el A class implementing the EffectListener interface that no longer wants to get notifications.
224
 */
225
  @SuppressWarnings("unused")
226
  public void deregisterForMessages(EffectListener el)
227
    {
228
    mP.deregisterForMessages(el);
229
    }
230

    
231
///////////////////////////////////////////////////////////////////////////////////////////////////
232
/**
233
 * Aborts all Effects.
234
 * @return Number of effects aborted.
235
 */
236
  @SuppressWarnings("unused")
237
  public int abortAllEffects()
238
      {
239
      return mP.abortAll(true);
240
      }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243
/**
244
 * Aborts all Effects of a given type (currently only POSTPROCESSING Effects).
245
 * 
246
 * @param type one of the constants defined in {@link EffectTypes}
247
 * @return Number of effects aborted.
248
 */
249
  @SuppressWarnings("unused")
250
  public int abortEffects(EffectTypes type)
251
    {
252
    switch(type)
253
      {
254
      case POSTPROCESS: return mP.abortAll(true);
255
      default         : return 0;
256
      }
257
    }
258
    
259
///////////////////////////////////////////////////////////////////////////////////////////////////
260
/**
261
 * Aborts a single Effect.
262
 * 
263
 * @param id ID of the Effect we want to abort.
264
 * @return number of Effects aborted. Always either 0 or 1.
265
 */
266
  @SuppressWarnings("unused")
267
  public int abortEffect(long id)
268
    {
269
    int type = (int)(id&EffectTypes.MASK);
270

    
271
    if( type==EffectTypes.POSTPROCESS.type ) return mP.removeByID(id>>EffectTypes.LENGTH);
272

    
273
    return 0;
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277
/**
278
 * Abort all Effects of a given name, for example all blurs.
279
 * 
280
 * @param name one of the constants defined in {@link EffectNames}
281
 * @return number of Effects aborted.
282
 */
283
  @SuppressWarnings("unused")
284
  public int abortEffects(EffectNames name)
285
    {
286
    switch(name.getType())
287
      {
288
      case POSTPROCESS: return mP.removeByType(name);
289
      default         : return 0;
290
      }
291
    }
292
    
293
///////////////////////////////////////////////////////////////////////////////////////////////////
294
/**
295
 * Print some info about a given Effect to Android's standard out. Used for debugging only.
296
 * 
297
 * @param id Effect ID we want to print info about
298
 * @return <code>true</code> if a single Effect of type effectType has been found.
299
 */
300
  @SuppressWarnings("unused")
301
  public boolean printEffect(long id)
302
    {
303
    int type = (int)(id&EffectTypes.MASK);
304

    
305
    if( type==EffectTypes.POSTPROCESS.type )  return mP.printByID(id>>EffectTypes.LENGTH);
306

    
307
    return false;
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311
/**
312
 * Returns the maximum number of Postprocess effects.
313
 *
314
 * @return The maximum number of Postprocess effects
315
 */
316
  @SuppressWarnings("unused")
317
  public static int getMaxPostprocess()
318
    {
319
    return EffectQueue.getMax(EffectTypes.POSTPROCESS.ordinal());
320
    }
321

    
322
///////////////////////////////////////////////////////////////////////////////////////////////////
323
/**
324
 * Sets the maximum number of Postprocess effects that can be stored in a single EffectQueue at one time.
325
 * This can fail if:
326
 * <ul>
327
 * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
328
 * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
329
 *     before the Fragment Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
330
 *     time only decreasing the value of 'max' is permitted.
331
 * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
332
 * </ul>
333
 *
334
 * @param max new maximum number of simultaneous Postprocess Effects. Has to be a non-negative number not greater
335
 *            than Byte.MAX_VALUE
336
 * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
337
 */
338
  @SuppressWarnings("unused")
339
  public static boolean setMaxPostprocess(int max)
340
    {
341
    return EffectQueue.setMax(EffectTypes.POSTPROCESS.ordinal(),max);
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345
/**
346
 * The higher the quality, the better the effect will look like and the slower it will be.
347
 * <p>
348
 * This works by rendering into smaller and smaller intermediate buffers. Each step renders into a
349
 * buffer that's half the size of the previous one.
350
 * <p>
351
 * We cannot this during mid-render - thus, give it to the Master to assign us back this job on the
352
 * next render.
353
 */
354
  public void setQuality(EffectQuality quality)
355
    {
356
    mJobs.add(new Job(MIPMAP,quality.level));
357
    DistortedMaster.newSlave(this);
358
    }
359

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////   
361
///////////////////////////////////////////////////////////////////////////////////////////////////
362
// Individual effect functions.
363
///////////////////////////////////////////////////////////////////////////////////////////////////
364
// Postprocess-based effects
365
///////////////////////////////////////////////////////////////////////////////////////////////////
366
/**
367
 * Blur the object.
368
 *
369
 * @param radius The 'strength' if the effect, in pixels. 0 = no blur, 10 = when blurring a given pixel,
370
 *               take into account 10 pixels in each direction.
371
 * @return ID of the effect added, or -1 if we failed to add one.
372
 */
373
  public long blur(Data1D radius)
374
    {
375
    return mP.add(EffectNames.BLUR, radius);
376
    }
377

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379
/**
380
 * Make the object glow with a specific color and a halo of specific radius.
381
 *
382
 * @param radius The 'strength' if the effect, in pixels. 0 = no halo, 10 = halo of roughly 10 pixels
383
 *               around the whole object.
384
 * @param color  RGBA of the color with which to draw the glow.
385
 * @return ID of the effect added, or -1 if we failed to add one.
386
 */
387
  public long glow(Data1D radius, Data4D color)
388
    {
389
    return mP.add(EffectNames.GLOW, radius, color);
390
    }
391
  }
(3-3/18)