Project

General

Profile

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

library / src / main / java / org / distorted / library / effect / PostprocessEffect.java @ f90fe926

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2017 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.effect;
21

    
22
import org.distorted.library.effectqueue.EffectQueue;
23
import org.distorted.library.main.DistortedFramebuffer;
24
import org.distorted.library.main.InternalMaster;
25
import org.distorted.library.program.DistortedProgram;
26

    
27
import java.lang.reflect.Method;
28
import java.nio.ByteBuffer;
29
import java.nio.ByteOrder;
30
import java.nio.FloatBuffer;
31
import java.util.ArrayList;
32

    
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
/**
35
 * Abstract class that represents an Effect that works by running a certain Shader Program(s) on a Framebuffer.
36
 */
37
public abstract class PostprocessEffect extends Effect implements InternalMaster.Slave
38
  {
39
  private static final int MIPMAP = 0;
40
  /**
41
   * 5 per-effect interpolated values.
42
   */
43
  public static final int NUM_FLOAT_UNIFORMS = 6;
44
  /**
45
   * 1: the name of the effect
46
   */
47
  public static final int NUM_INT_UNIFORMS = 1;
48

    
49
  static final int POS_DATA_SIZE= 2;
50
  static final int TEX_DATA_SIZE= 2;
51

    
52
  static final FloatBuffer mQuadPositions, mQuadTexture, mQuadTextureInv;
53

    
54
  static
55
    {
56
    int dataLength      = 4;
57
    int bytes_per_float = 4;
58

    
59
    float[] position  = { -0.5f, -0.5f,  -0.5f, 0.5f,  0.5f,-0.5f,  0.5f, 0.5f };
60
    float[] textureNor= {  0.0f,  0.0f,   0.0f, 1.0f,  1.0f, 0.0f,  1.0f, 1.0f };
61
    float[] textureInv= {  0.0f,  0.0f,   1.0f, 0.0f,  0.0f, 1.0f,  1.0f, 1.0f };
62

    
63
    mQuadPositions = ByteBuffer.allocateDirect(POS_DATA_SIZE*dataLength*bytes_per_float).order(ByteOrder.nativeOrder()).asFloatBuffer();
64
    mQuadPositions.put(position).position(0);
65
    mQuadTexture   = ByteBuffer.allocateDirect(TEX_DATA_SIZE*dataLength*bytes_per_float).order(ByteOrder.nativeOrder()).asFloatBuffer();
66
    mQuadTexture.put(textureNor).position(0);
67
    mQuadTextureInv= ByteBuffer.allocateDirect(TEX_DATA_SIZE*dataLength*bytes_per_float).order(ByteOrder.nativeOrder()).asFloatBuffer();
68
    mQuadTextureInv.put(textureInv).position(0);
69
    }
70

    
71
  private static class Source
72
    {
73
    private final String mName, mVertexShader, mFragmentShader;
74

    
75
    Source(String name, String vertex, String fragment)
76
      {
77
      mName           = name;
78
      mVertexShader   = vertex;
79
      mFragmentShader = fragment;
80
      }
81
    }
82

    
83
  static ArrayList<DistortedProgram> mPrograms = new ArrayList<>();
84
  private final static ArrayList<Source> mSources = new ArrayList<>();
85
  private static int mNumSources = 0;
86

    
87
  private static class Job
88
    {
89
    int type;
90
    int level;
91

    
92
    Job(int t, int l)
93
      {
94
      type = t;
95
      level= l;
96
      }
97
    }
98

    
99
  private final ArrayList<Job> mJobs = new ArrayList<>();
100
  private int mQualityLevel;
101

    
102
  float mQualityScale;
103

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

    
106
  static int register(final String name, final String vertexShader, final String fragmentShader)
107
    {
108
    mSources.add(new Source(name,vertexShader,fragmentShader));
109

    
110
    return mNumSources++;
111
    }
112

    
113
///////////////////////////////////////////////////////////////////////////////////////////////////
114
/**
115
 * Only for use by the library itself.
116
 *
117
 * @y.exclude
118
 */
119
  public static void createPrograms(int GLSL)
120
    {
121
    Source source;
122
    int len = mSources.size();
123

    
124
    String version = "#version "+GLSL+" es\n";
125

    
126
    for(int i=0; i<len; i++)
127
      {
128
      source = mSources.remove(0);
129

    
130
      try
131
        {
132
        mPrograms.add (new DistortedProgram(version+source.mVertexShader,version+source.mFragmentShader));
133
        }
134
      catch(Exception e)
135
        {
136
        android.util.Log.e("Effects", "exception trying to compile "+source.mName+" program: "+e.getMessage());
137
        throw new RuntimeException(e.getMessage());
138
        }
139
      }
140
    }
141

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
/**
144
 * At this moment the 'buffer' contains a) preprocessed object b) real object rendered 'on top' of
145
 * the preprocessed one.
146
 * Postprocess buffer. What this means exactly depends on the effect -
147
 *
148
 * Only for use by the library itself.
149
 *
150
 * @y.exclude
151
 */
152
  public abstract int postprocess(float[] uniforms, int index, DistortedFramebuffer buffer);
153

    
154
///////////////////////////////////////////////////////////////////////////////////////////////////
155
  /**
156
   * Do we render the object directly to the final surface, and only postprocess and then blit its
157
   * 'halo', or do we render the object along with its halo to the intermediate framebuffer and
158
   * postprocess it as well?
159
   *
160
   * Only for use by the library itself.
161
   *
162
   * @y.exclude
163
   */
164
  public abstract boolean getRenderDirectly();
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167
  /**
168
   * Only for use by the library itself.
169
   *
170
   * @y.exclude
171
   */
172
  public int getQuality()
173
    {
174
    return mQualityLevel;
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
/**
179
 * Only for use by the library itself.
180
 *
181
 * @y.exclude
182
 */
183
  public void addQueue(EffectQueue queue)
184
    {
185
    // NO OP
186
    }
187

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189
/**
190
 * Only for use by the library itself.
191
 *
192
 * @y.exclude
193
 */
194
  public void remQueue(EffectQueue queue)
195
    {
196
    // NO OP
197
    }
198

    
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200
/**
201
 * This is not really part of the public API. Has to be public only because it is a part of the
202
 * DistortedSlave interface, which should really be a class that we extend here instead but
203
 * Java has no multiple inheritance.
204
 *
205
 * @y.exclude
206
 */
207
  public void doWork()
208
    {
209
    int num = mJobs.size();
210
    Job job;
211

    
212
    for(int i=0; i<num; i++)
213
      {
214
      job = mJobs.remove(0);
215

    
216
      switch(job.type)
217
        {
218
        case MIPMAP: int level = job.level;
219
                     mQualityLevel = level;
220
                     mQualityScale = 1.0f;
221
                     for(int j=0; j<level; j++) mQualityScale*= EffectQuality.MULTIPLIER;
222
                     break;
223
        }
224
      }
225
    }
226

    
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

    
229
  static void destroyStatics()
230
    {
231
    mPrograms.clear();
232
    mSources.clear();
233
    mNumSources = 0;
234

    
235
    Method method;
236

    
237
    for(EffectName name: EffectName.values())
238
      {
239
      if( name.getType() == EffectType.POSTPROCESS )
240
        {
241
        Class<? extends Effect> cls = name.getEffectClass();
242

    
243
        try
244
          {
245
          method = cls.getDeclaredMethod("destroyStatics");  // destroyStatics not public, thus getDeclaredMethod
246
          }
247
        catch(NoSuchMethodException ex)
248
          {
249
          android.util.Log.e("postprocess", cls.getSimpleName()+": exception getting method: "+ex.getMessage());
250
          method = null;
251
          }
252

    
253
        try
254
          {
255
          if( method!=null ) method.invoke(null);
256
          }
257
        catch(Exception ex)
258
          {
259
          android.util.Log.e("postprocess", "exception invoking method: "+ex.getMessage());
260
          }
261
        }
262
      }
263
    }
264

    
265
///////////////////////////////////////////////////////////////////////////////////////////////////
266

    
267
  PostprocessEffect(EffectName name)
268
    {
269
    super(name);
270

    
271
    mQualityLevel = 0;
272
    mQualityScale = 1.0f;
273
    }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
// PUBLIC API
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
/**
279
 * The higher the quality, the better the effect will look like and the slower it will be.
280
 * <p>
281
 * This works by rendering into smaller and smaller intermediate buffers. Each step renders into a
282
 * buffer that's half the size of the previous one.
283
 * <p>
284
 * We cannot this during mid-render - thus, give it to the Master to assign us back this job on the
285
 * next render.
286
 */
287
  public void setQuality(EffectQuality quality)
288
    {
289
    mJobs.add(new Job(MIPMAP,quality.getLevel()));
290
    InternalMaster.newSlave(this);
291
    }
292
  }
(17-17/34)