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