1 /** 2 * Copyright 2010 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; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 import jogamp.graph.geom.plane.AffineTransform; 34 import jogamp.opengl.Debug; 35 36 import com.jogamp.graph.geom.Triangle; 37 import com.jogamp.graph.geom.Vertex; 38 import com.jogamp.graph.curve.opengl.GLRegion; 39 import com.jogamp.opengl.math.geom.AABBox; 40 import com.jogamp.opengl.math.geom.Frustum; 41 import com.jogamp.opengl.util.texture.TextureSequence; 42 43 /** 44 * Abstract Outline shape representation define the method an OutlineShape(s) 45 * is bound and rendered. 46 * 47 * @see GLRegion 48 */ 49 public abstract class Region { 50 51 /** Debug flag for region impl (graph.curve) */ 52 public static final boolean DEBUG = Debug.debug("graph.curve"); 53 public static final boolean DEBUG_INSTANCE = Debug.debug("graph.curve.Instance"); 54 55 /** 56 * Rendering-Mode bit for {@link #getRenderModes() Region} 57 * <p> 58 * MSAA based Anti-Aliasing, a two pass region rendering, slower and more 59 * resource hungry (FBO), but providing fast MSAA in case 60 * the whole scene is not rendered with MSAA. 61 * </p> 62 */ 63 public static final int MSAA_RENDERING_BIT = 1 << 0; 64 65 /** 66 * Rendering-Mode bit for {@link #getRenderModes() Region} 67 * <p> 68 * View based Anti-Aliasing, a two pass region rendering, slower and more 69 * resource hungry (FBO), but AA is perfect. Otherwise the default fast one 70 * pass MSAA region rendering is being used. 71 * </p> 72 */ 73 public static final int VBAA_RENDERING_BIT = 1 << 1; 74 75 /** 76 * Rendering-Mode bit for {@link #getRenderModes() Region} 77 * <p> 78 * Use non uniform weights [0.0 .. 1.9] for curve region rendering. 79 * Otherwise the default weight 1.0 for uniform curve region rendering is 80 * being applied. 81 * </p> 82 */ 83 public static final int VARWEIGHT_RENDERING_BIT = 1 << 8; 84 85 /** 86 * Rendering-Mode bit for {@link #getRenderModes() Region} 87 * <p> 88 * If set, a color channel attribute per vertex is added to the stream, 89 * otherwise only the 90 * {@link com.jogamp.graph.curve.opengl.RegionRenderer#setColorStatic(com.jogamp.opengl.GL2ES2, float, float, float, float) static color} 91 * is being used. 92 * </p> 93 */ 94 public static final int COLORCHANNEL_RENDERING_BIT = 1 << 9; 95 96 /** 97 * Rendering-Mode bit for {@link #getRenderModes() Region} 98 * <p> 99 * If set, a color texture is used to determine the color. 100 * </p> 101 */ 102 public static final int COLORTEXTURE_RENDERING_BIT = 1 << 10; 103 104 /** Default maximum {@link #getQuality() quality}, {@value}. */ 105 public static final int MAX_QUALITY = 1; 106 107 public static final int DEFAULT_TWO_PASS_TEXTURE_UNIT = 0; 108 109 protected static final int DIRTY_SHAPE = 1 << 0 ; 110 protected static final int DIRTY_STATE = 1 << 1 ; 111 112 private final int renderModes; 113 private int quality; 114 private int dirty = DIRTY_SHAPE | DIRTY_STATE; 115 private int numVertices = 0; 116 protected final AABBox box = new AABBox(); 117 protected Frustum frustum = null; 118 isVBAA(final int renderModes)119 public static boolean isVBAA(final int renderModes) { 120 return 0 != (renderModes & Region.VBAA_RENDERING_BIT); 121 } 122 isMSAA(final int renderModes)123 public static boolean isMSAA(final int renderModes) { 124 return 0 != (renderModes & Region.MSAA_RENDERING_BIT); 125 } 126 isTwoPass(final int renderModes)127 public static boolean isTwoPass(final int renderModes) { 128 return 0 != ( renderModes & ( Region.VBAA_RENDERING_BIT | Region.MSAA_RENDERING_BIT) ); 129 } 130 131 /** 132 * Returns true if render mode capable of variable weights, 133 * i.e. the bit {@link #VARWEIGHT_RENDERING_BIT} is set, 134 * otherwise false. 135 */ hasVariableWeight(final int renderModes)136 public static boolean hasVariableWeight(final int renderModes) { 137 return 0 != (renderModes & Region.VARWEIGHT_RENDERING_BIT); 138 } 139 140 /** 141 * Returns true if render mode has a color channel, 142 * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, 143 * otherwise false. 144 */ hasColorChannel(final int renderModes)145 public static boolean hasColorChannel(final int renderModes) { 146 return 0 != (renderModes & Region.COLORCHANNEL_RENDERING_BIT); 147 } 148 149 /** 150 * Returns true if render mode has a color texture, 151 * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, 152 * otherwise false. 153 */ hasColorTexture(final int renderModes)154 public static boolean hasColorTexture(final int renderModes) { 155 return 0 != (renderModes & Region.COLORTEXTURE_RENDERING_BIT); 156 } 157 getRenderModeString(final int renderModes)158 public static String getRenderModeString(final int renderModes) { 159 final String curveS = hasVariableWeight(renderModes) ? "-curve" : ""; 160 final String cChanS = hasColorChannel(renderModes) ? "-cols" : ""; 161 final String cTexS = hasColorTexture(renderModes) ? "-ctex" : ""; 162 if( Region.isVBAA(renderModes) ) { 163 return "vbaa"+curveS+cChanS+cTexS; 164 } else if( Region.isMSAA(renderModes) ) { 165 return "msaa"+curveS+cChanS+cTexS; 166 } else { 167 return "norm"+curveS+cChanS+cTexS; 168 } 169 } 170 Region(final int regionRenderModes)171 protected Region(final int regionRenderModes) { 172 this.renderModes = regionRenderModes; 173 this.quality = MAX_QUALITY; 174 } 175 176 // FIXME: Better handling of impl. buffer growth .. ! 177 // protected abstract void setupInitialComponentCount(int attributeCount, int indexCount); 178 pushVertex(final float[] coords, final float[] texParams, float[] rgba)179 protected abstract void pushVertex(final float[] coords, final float[] texParams, float[] rgba); pushIndex(int idx)180 protected abstract void pushIndex(int idx); 181 182 /** 183 * Return bit-field of render modes, see {@link GLRegion#create(int, TextureSequence)}. 184 */ getRenderModes()185 public final int getRenderModes() { return renderModes; } 186 187 /** See {@link #MAX_QUALITY} */ getQuality()188 public final int getQuality() { return quality; } 189 190 /** See {@link #MAX_QUALITY} */ setQuality(final int q)191 public final void setQuality(final int q) { quality=q; } 192 clearImpl()193 protected void clearImpl() { 194 dirty = DIRTY_SHAPE | DIRTY_STATE; 195 numVertices = 0; 196 box.reset(); 197 } 198 199 /** 200 * Returns true if capable of two pass rendering - VBAA, otherwise false. 201 */ isVBAA()202 public final boolean isVBAA() { 203 return Region.isVBAA(renderModes); 204 } 205 206 /** 207 * Returns true if capable of two pass rendering - MSAA, otherwise false. 208 */ isMSAA()209 public final boolean isMSAA() { 210 return Region.isMSAA(renderModes); 211 } 212 213 /** 214 * Returns true if capable of variable weights, otherwise false. 215 */ hasVariableWeight()216 public final boolean hasVariableWeight() { 217 return Region.hasVariableWeight(renderModes); 218 } 219 220 /** 221 * Returns true if render mode has a color channel, 222 * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, 223 * otherwise false. 224 */ hasColorChannel()225 public boolean hasColorChannel() { 226 return Region.hasColorChannel(renderModes); 227 } 228 229 /** 230 * Returns true if render mode has a color texture, 231 * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, 232 * otherwise false. 233 */ hasColorTexture()234 public boolean hasColorTexture() { 235 return Region.hasColorTexture(renderModes); 236 } 237 238 239 /** See {@link #setFrustum(Frustum)} */ getFrustum()240 public final Frustum getFrustum() { return frustum; } 241 242 /** 243 * Set {@link Frustum} culling for {@link #addOutlineShape(OutlineShape, AffineTransform, float[])}. 244 */ setFrustum(final Frustum frustum)245 public final void setFrustum(final Frustum frustum) { 246 this.frustum = frustum; 247 } 248 249 final float[] coordsEx = new float[3]; 250 pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba)251 private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { 252 if( null != transform ) { 253 final float[] coordsIn = vertIn.getCoord(); 254 transform.transform(coordsIn, coordsEx); 255 coordsEx[2] = coordsIn[2]; 256 box.resize(coordsEx[0], coordsEx[1], coordsEx[2]); 257 pushVertex(coordsEx, vertIn.getTexCoord(), rgba); 258 } else { 259 box.resize(vertIn.getX(), vertIn.getY(), vertIn.getZ()); 260 pushVertex(vertIn.getCoord(), vertIn.getTexCoord(), rgba); 261 } 262 numVertices++; 263 } 264 pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba)265 private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { 266 pushIndex(numVertices); 267 pushNewVertexImpl(vertIn, transform, rgba); 268 } 269 270 private final AABBox tmpBox = new AABBox(); 271 272 /** 273 * Add the given {@link OutlineShape} to this region with the given optional {@link AffineTransform}. 274 * <p> 275 * In case {@link #setFrustum(Frustum) frustum culling is set}, the {@link OutlineShape} 276 * is dropped if it's {@link OutlineShape#getBounds() bounding-box} is fully outside of the frustum. 277 * The optional {@link AffineTransform} is applied to the bounding-box beforehand. 278 * </p> 279 * @param rgbaColor TODO 280 */ addOutlineShape(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor)281 public final void addOutlineShape(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { 282 if( null != frustum ) { 283 final AABBox shapeBox = shape.getBounds(); 284 final AABBox shapeBoxT; 285 if( null != t ) { 286 t.transform(shapeBox, tmpBox); 287 shapeBoxT = tmpBox; 288 } else { 289 shapeBoxT = shapeBox; 290 } 291 if( frustum.isAABBoxOutside(shapeBoxT) ) { 292 if(DEBUG_INSTANCE) { 293 System.err.println("Region.addOutlineShape(): Dropping outside shapeBoxT: "+shapeBoxT); 294 } 295 return; 296 } 297 } 298 final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); 299 final ArrayList<Vertex> vertsIn = shape.getVertices(); 300 if(DEBUG_INSTANCE) { 301 final int addedVerticeCount = shape.getAddedVerticeCount(); 302 final int verticeCount = vertsIn.size() + addedVerticeCount; 303 final int indexCount = trisIn.size() * 3; 304 System.err.println("Region.addOutlineShape().0: tris: "+trisIn.size()+", verts "+vertsIn.size()+", transform "+t); 305 System.err.println("Region.addOutlineShape().0: VerticeCount "+vertsIn.size()+" + "+addedVerticeCount+" = "+verticeCount); 306 System.err.println("Region.addOutlineShape().0: IndexCount "+indexCount); 307 } 308 // setupInitialComponentCount(verticeCount, indexCount); // FIXME: Use it ? 309 310 final int idxOffset = numVertices; 311 int vertsVNewIdxCount = 0, vertsTMovIdxCount = 0, vertsTNewIdxCount = 0, tris = 0; 312 final int vertsDupCountV = 0, vertsDupCountT = 0, vertsKnownMovedT = 0; 313 if( vertsIn.size() >= 3 ) { 314 if(DEBUG_INSTANCE) { 315 System.err.println("Region.addOutlineShape(): Processing Vertices"); 316 } 317 for(int i=0; i<vertsIn.size(); i++) { 318 pushNewVertexImpl(vertsIn.get(i), t, rgbaColor); 319 vertsVNewIdxCount++; 320 } 321 if(DEBUG_INSTANCE) { 322 System.err.println("Region.addOutlineShape(): Processing Triangles"); 323 } 324 for(int i=0; i<trisIn.size(); i++) { 325 final Triangle triIn = trisIn.get(i); 326 if(Region.DEBUG_INSTANCE) { 327 System.err.println("T["+i+"]: "+triIn); 328 } 329 // triEx.addVertexIndicesOffset(idxOffset); 330 // triangles.add( triEx ); 331 final Vertex[] triInVertices = triIn.getVertices(); 332 final int tv0Idx = triInVertices[0].getId(); 333 if( Integer.MAX_VALUE-idxOffset > tv0Idx ) { // Integer.MAX_VALUE != i0 // FIXME: renderer uses SHORT! 334 // valid 'known' idx - move by offset 335 if(Region.DEBUG_INSTANCE) { 336 System.err.println("T["+i+"]: Moved "+tv0Idx+" + "+idxOffset+" -> "+(tv0Idx+idxOffset)); 337 } 338 pushIndex(tv0Idx+idxOffset); 339 pushIndex(triInVertices[1].getId()+idxOffset); 340 pushIndex(triInVertices[2].getId()+idxOffset); 341 vertsTMovIdxCount+=3; 342 } else { 343 // invalid idx - generate new one 344 if(Region.DEBUG_INSTANCE) { 345 System.err.println("T["+i+"]: New Idx "+numVertices); 346 } 347 pushNewVertexIdxImpl(triInVertices[0], t, rgbaColor); 348 pushNewVertexIdxImpl(triInVertices[1], t, rgbaColor); 349 pushNewVertexIdxImpl(triInVertices[2], t, rgbaColor); 350 vertsTNewIdxCount+=3; 351 } 352 tris++; 353 } 354 } 355 if(DEBUG_INSTANCE) { 356 System.err.println("Region.addOutlineShape().X: idxOffset "+idxOffset+", tris: "+tris+", verts [idx "+vertsTNewIdxCount+", add "+vertsTNewIdxCount+" = "+(vertsVNewIdxCount+vertsTNewIdxCount)+"]"); 357 System.err.println("Region.addOutlineShape().X: verts: idx[v-new "+vertsVNewIdxCount+", t-new "+vertsTNewIdxCount+" = "+(vertsVNewIdxCount+vertsTNewIdxCount)+"]"); 358 System.err.println("Region.addOutlineShape().X: verts: idx t-moved "+vertsTMovIdxCount+", numVertices "+numVertices); 359 System.err.println("Region.addOutlineShape().X: verts: v-dups "+vertsDupCountV+", t-dups "+vertsDupCountT+", t-known "+vertsKnownMovedT); 360 // int vertsDupCountV = 0, vertsDupCountT = 0; 361 System.err.println("Region.addOutlineShape().X: box "+box); 362 } 363 markShapeDirty(); 364 } 365 addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor)366 public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) { 367 for (int i = 0; i < shapes.size(); i++) { 368 addOutlineShape(shapes.get(i), transform, rgbaColor); 369 } 370 } 371 372 /** @return the AxisAligned bounding box of current region */ getBounds()373 public final AABBox getBounds() { 374 return box; 375 } 376 377 /** 378 * Mark this region's shape dirty, i.e. it's 379 * Vertices, Triangles, and or Lines changed. 380 */ markShapeDirty()381 public final void markShapeDirty() { 382 dirty |= DIRTY_SHAPE; 383 } 384 /** Returns true if this region's shape are dirty, see {@link #markShapeDirty()}. */ isShapeDirty()385 public final boolean isShapeDirty() { 386 return 0 != ( dirty & DIRTY_SHAPE ) ; 387 } 388 /** 389 * Mark this region's state dirty, i.e. 390 * it's render attributes or parameters changed. 391 */ markStateDirty()392 public final void markStateDirty() { 393 dirty |= DIRTY_STATE; 394 } 395 /** Returns true if this region's state is dirty, see {@link #markStateDirty()}. */ isStateDirty()396 public final boolean isStateDirty() { 397 return 0 != ( dirty & DIRTY_STATE ) ; 398 } 399 400 /** 401 * See {@link #markShapeDirty()} and {@link #markStateDirty()}. 402 */ clearDirtyBits(final int v)403 protected final void clearDirtyBits(final int v) { 404 dirty &= ~v; 405 } getDirtyBits()406 protected final int getDirtyBits() { return dirty; } 407 toString()408 public String toString() { 409 return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; 410 } 411 }