1 /**
2  * Copyright 2011 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 package com.jogamp.graph.curve.opengl;
29 
30 import java.nio.FloatBuffer;
31 
32 import com.jogamp.opengl.GL;
33 import com.jogamp.opengl.GL2ES2;
34 import com.jogamp.opengl.GLException;
35 import com.jogamp.opengl.GLUniformData;
36 
37 import jogamp.common.os.PlatformPropsImpl;
38 import jogamp.graph.curve.opengl.shader.UniformNames;
39 
40 import com.jogamp.common.os.Platform;
41 import com.jogamp.graph.curve.Region;
42 import com.jogamp.graph.geom.Vertex;
43 import com.jogamp.opengl.util.GLArrayDataServer;
44 import com.jogamp.opengl.util.PMVMatrix;
45 import com.jogamp.opengl.util.glsl.ShaderProgram;
46 
47 public class RenderState {
48     private static final String thisKey = "jogamp.graph.curve.RenderState" ;
49 
50     /**
51      * Bitfield hint, {@link #isHintMaskSet(int) if set}
52      * stating <i>enabled</i> {@link GL#GL_BLEND}, otherwise <i>disabled</i>.
53      * <p>
54      * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}.
55      * </p>
56      * <p>
57      * If set, {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method}
58      * will set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function}
59      * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering.
60      * </p>
61      * <p>
62      * Shall be set by custom code, e.g. via {@link RegionRenderer}'s
63      * enable and disable {@link RegionRenderer.GLCallback} as done in
64      * {@link RegionRenderer#defaultBlendEnable} and {@link RegionRenderer#defaultBlendDisable}.
65      * </p>
66      */
67     public static final int BITHINT_BLENDING_ENABLED = 1 << 0 ;
68 
69     /**
70      * Bitfield hint, {@link #isHintMaskSet(int) if set}
71      * stating globally <i>enabled</i> {@link GL#GL_DEPTH_TEST}, otherwise <i>disabled</i>.
72      * <p>
73      * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}.
74      * </p>
75      * <p>
76      * {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method}
77      * may toggle depth test, and reset it's state according to this hint.
78      * </p>
79      * <p>
80      * Shall be set by custom code, e.g. after {@link RenderState} or {@link RegionRenderer} construction.
81      * </p>
82      */
83     public static final int BITHINT_GLOBAL_DEPTH_TEST_ENABLED = 1 << 1 ;
84 
createRenderState(final Vertex.Factory<? extends Vertex> pointFactory)85     public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory) {
86         return new RenderState(pointFactory, null);
87     }
88 
createRenderState(final Vertex.Factory<? extends Vertex> pointFactory, final PMVMatrix pmvMatrix)89     public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory, final PMVMatrix pmvMatrix) {
90         return new RenderState(pointFactory, pmvMatrix);
91     }
92 
getRenderState(final GL2ES2 gl)93     public static final RenderState getRenderState(final GL2ES2 gl) {
94         return (RenderState) gl.getContext().getAttachedObject(thisKey);
95     }
96 
97     private final Vertex.Factory<? extends Vertex> vertexFactory;
98     private final PMVMatrix pmvMatrix;
99     private final float[] weight;
100     private final FloatBuffer weightBuffer;
101     private final float[] colorStatic;
102     private final FloatBuffer colorStaticBuffer;
103     private ShaderProgram sp;
104     private int hintBitfield;
105 
106     private final int id;
getNextID()107     private static synchronized int getNextID() {
108         return nextID++;
109     }
110     private static int nextID = 1;
111 
112     /**
113      * Representation of {@link RenderState} data for one {@link ShaderProgram}
114      * as {@link GLUniformData}.
115      * <p>
116      * FIXME: Utilize 'ARB_Uniform_Buffer_Object' where available!
117      * </p>
118      */
119     public static class ProgramLocal {
120         public final GLUniformData gcu_PMVMatrix01;
121         public final GLUniformData gcu_Weight;
122         public final GLUniformData gcu_ColorStatic;
123         private int rsId = -1;
124 
ProgramLocal()125         public ProgramLocal() {
126             gcu_PMVMatrix01 = GLUniformData.creatEmptyMatrix(UniformNames.gcu_PMVMatrix01, 4, 4);
127             gcu_Weight = GLUniformData.creatEmptyVector(UniformNames.gcu_Weight, 1);
128             gcu_ColorStatic = GLUniformData.creatEmptyVector(UniformNames.gcu_ColorStatic, 4);
129         }
130 
getRenderStateId()131         public final int getRenderStateId() { return rsId; }
132 
133         /**
134          * <p>
135          * Since {@link RenderState} data is being used in multiple
136          * {@link ShaderProgram}s the data must always be written.
137          * </p>
138          * @param gl
139          * @param updateLocation
140          * @param renderModes
141          * @param throwOnError TODO
142          * @return true if no error occurred, i.e. all locations found, otherwise false.
143          */
update(final GL2ES2 gl, final RenderState rs, final boolean updateLocation, final int renderModes, final boolean pass1, final boolean throwOnError)144         public final boolean update(final GL2ES2 gl, final RenderState rs, final boolean updateLocation, final int renderModes, final boolean pass1, final boolean throwOnError) {
145             if( rs.id() != rsId ) {
146                 gcu_PMVMatrix01.setData(rs.pmvMatrix.glGetPMvMatrixf());
147                 gcu_Weight.setData(rs.weightBuffer);
148                 gcu_ColorStatic.setData(rs.colorStaticBuffer);
149                 rsId = rs.id();
150             }
151             boolean res = true;
152             if( null != rs.sp && rs.sp.inUse() ) {
153                 if( !Region.isTwoPass(renderModes) || !pass1 ) {
154                     final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_PMVMatrix01, throwOnError);
155                     res = res && r0;
156                 }
157                 if( pass1 ) {
158                     if( Region.hasVariableWeight( renderModes ) ) {
159                         final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_Weight, throwOnError);
160                         res = res && r0;
161                     }
162                     {
163                         final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_ColorStatic, throwOnError);
164                         res = res && r0;
165                     }
166                 }
167             }
168             return res;
169         }
170 
toString(StringBuilder sb, final boolean alsoUnlocated)171         public StringBuilder toString(StringBuilder sb, final boolean alsoUnlocated) {
172             if(null==sb) {
173                 sb = new StringBuilder();
174             }
175             sb.append("ProgramLocal[rsID ").append(rsId).append(PlatformPropsImpl.NEWLINE);
176             // pmvMatrix.toString(sb, "%.2f");
177             sb.append(gcu_PMVMatrix01).append(", ").append(PlatformPropsImpl.NEWLINE);
178             sb.append(gcu_ColorStatic).append(", ");
179             sb.append(gcu_Weight).append("]");
180             return sb;
181         }
182 
183         @Override
toString()184         public String toString() {
185             return toString(null, false).toString();
186         }
187     }
188 
RenderState(final Vertex.Factory<? extends Vertex> vertexFactory, final PMVMatrix pmvMatrix)189     protected RenderState(final Vertex.Factory<? extends Vertex> vertexFactory, final PMVMatrix pmvMatrix) {
190         this.id = getNextID();
191         this.sp = null;
192         this.vertexFactory = vertexFactory;
193         this.pmvMatrix = null != pmvMatrix ? pmvMatrix : new PMVMatrix();
194         this.weight = new float[1];
195         this.weightBuffer = FloatBuffer.wrap(weight);
196         this.colorStatic = new float[4];
197         this.colorStaticBuffer = FloatBuffer.wrap(colorStatic);
198         this.hintBitfield = 0;
199     }
200 
id()201     public final int id() { return id; }
getShaderProgram()202     public final ShaderProgram getShaderProgram() { return sp; }
isShaderProgramInUse()203     public final boolean isShaderProgramInUse() { return null != sp ? sp.inUse() : false; }
204 
205     /**
206      * Set a {@link ShaderProgram} and enable it. If the given {@link ShaderProgram} is new,
207      * method returns true, otherwise false.
208      * @param gl
209      * @param spNext
210      * @return true if a new shader program is being used and hence external uniform-data and -location,
211      *         as well as the attribute-location must be updated, otherwise false.
212      */
setShaderProgram(final GL2ES2 gl, final ShaderProgram spNext)213     public final boolean setShaderProgram(final GL2ES2 gl, final ShaderProgram spNext) {
214         if( spNext.equals(this.sp) ) {
215             spNext.useProgram(gl, true);
216             return false;
217         }
218         if( null != this.sp ) {
219             this.sp.notifyNotInUse();
220         }
221         this.sp = spNext;
222         spNext.useProgram(gl, true);
223         return true;
224     }
225 
getVertexFactory()226     public final Vertex.Factory<? extends Vertex> getVertexFactory() { return vertexFactory; }
227 
getMatrix()228     public final PMVMatrix getMatrix() { return pmvMatrix; }
229 
isWeightValid(final float v)230     public static boolean isWeightValid(final float v) {
231         return 0.0f <= v && v <= 1.9f ;
232     }
getWeight()233     public final float getWeight() { return weight[0]; }
setWeight(final float v)234     public final void setWeight(final float v) {
235         if( !isWeightValid(v) ) {
236              throw new IllegalArgumentException("Weight out of range");
237         }
238         weight[0] = v;
239     }
240 
241 
getColorStatic(final float[] rgbaColor)242     public final float[] getColorStatic(final float[] rgbaColor) {
243         System.arraycopy(colorStatic, 0, rgbaColor, 0, 4);
244         return rgbaColor;
245     }
setColorStatic(final float r, final float g, final float b, final float a)246     public final void setColorStatic(final float r, final float g, final float b, final float a){
247         colorStatic[0] = r;
248         colorStatic[1] = g;
249         colorStatic[2] = b;
250         colorStatic[3] = a;
251     }
252 
253 
254     /**
255      *
256      * @param gl
257      * @param updateLocation
258      * @param data
259      * @param throwOnError TODO
260      * @return true if no error occured, i.e. all locations found, otherwise false.
261      */
updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data, final boolean throwOnError)262     public final boolean updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data, final boolean throwOnError) {
263         if( updateLocation || 0 > data.getLocation() ) {
264             final boolean ok = 0 <= data.setLocation(gl, sp.program());
265             if( throwOnError && !ok ) {
266                 throw new GLException("Could not locate "+data);
267             }
268             return ok;
269         } else {
270             return true;
271         }
272     }
273 
274     /**
275      *
276      * @param gl
277      * @param updateLocation
278      * @param updateData TODO
279      * @param data
280      * @param throwOnError TODO
281      * @return true if no error occured, i.e. all locations found, otherwise false.
282      */
updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data, final boolean throwOnError)283     public final boolean updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data, final boolean throwOnError) {
284         updateLocation = updateLocation || 0 > data.getLocation();
285         if( updateLocation ) {
286             updateData = 0 <= data.setLocation(gl, sp.program());
287             if( throwOnError && !updateData ) {
288                 throw new GLException("Could not locate "+data);
289             }
290         }
291         if( updateData ){
292             gl.glUniform(data);
293             return true;
294         } else {
295             return !updateLocation;
296         }
297     }
298 
299     /**
300      * @param gl
301      * @param data
302      * @param throwOnError TODO
303      * @return true if no error occured, i.e. all locations found, otherwise false.
304      */
updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data, final boolean throwOnError)305     public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data, final boolean throwOnError) {
306         if( updateLocation || 0 > data.getLocation() ) {
307             final boolean ok = 0 <= data.setLocation(gl, sp.program());
308             if( throwOnError && !ok ) {
309                 throw new GLException("Could not locate "+data);
310             }
311             return ok;
312         } else {
313             return true;
314         }
315     }
316 
317 
isHintMaskSet(final int mask)318     public final boolean isHintMaskSet(final int mask) {
319         return mask == ( hintBitfield & mask );
320     }
setHintMask(final int mask)321     public final void setHintMask(final int mask) {
322         hintBitfield |= mask;
323     }
clearHintMask(final int mask)324     public final void clearHintMask(final int mask) {
325         hintBitfield &= ~mask;
326     }
327 
destroy(final GL2ES2 gl)328     public void destroy(final GL2ES2 gl) {
329         if( null != sp ) {
330             sp.destroy(gl);
331             sp = null;
332         }
333     }
334 
attachTo(final GL2ES2 gl)335     public final RenderState attachTo(final GL2ES2 gl) {
336         return (RenderState) gl.getContext().attachObject(thisKey, this);
337     }
338 
detachFrom(final GL2ES2 gl)339     public final boolean detachFrom(final GL2ES2 gl) {
340         final RenderState _rs = (RenderState) gl.getContext().getAttachedObject(thisKey);
341         if(_rs == this) {
342             gl.getContext().detachObject(thisKey);
343             return true;
344         }
345         return false;
346     }
347 
348     @Override
toString()349     public String toString() {
350         return "RenderState["+sp+"]";
351     }
352 }
353