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 }