/***********************************************************************
* mt4j Copyright (c) 2008 - 2009, C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.components.visibleComponents.shapes;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.media.opengl.GL;
import org.mt4j.util.math.Tools3D;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GLStencilUtil;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.opengl.PGraphicsOpenGL;
/**
* This class can only be used with OpenGL!
* <br>This class can be used to draw concave, non-simple
* polygons using the stencil buffer.
*
* @author Christopher Ruff
*/
public class MTStencilPolygon extends MTPolygon {
/** The pa. */
private PApplet pa;
/** The min max. */
private float[] minMax;
/** The min x. */
private float minX;
/** The min y. */
private float minY;
/** The max x. */
private float maxX;
/** The max y. */
private float maxY;
/** The contours. */
private List<Vertex[]> contours;
//Gradient Quad points
/** The x1 r. */
private float x1R;
/** The x1 g. */
private float x1G;
/** The x1 b. */
private float x1B;
/** The x1 a. */
private float x1A;
/** The x2 r. */
private float x2R;
/** The x2 g. */
private float x2G;
/** The x2 b. */
private float x2B;
/** The x2 a. */
private float x2A;
/** The x3 r. */
private float x3R;
/** The x3 g. */
private float x3G;
/** The x3 b. */
private float x3B;
/** The x3 a. */
private float x3A;
/** The x4 r. */
private float x4R;
/** The x4 g. */
private float x4G;
/** The x4 b. */
private float x4B;
/** The x4 a. */
private float x4A;
/** The use gradient. */
private boolean useGradient;
/**
* Instantiates a new mT stencil polygon.
*
* @param vertices the vertices
* @param pApplet the applet
*/
public MTStencilPolygon(Vertex[] vertices, PApplet pApplet) {
super(vertices, pApplet);
ArrayList<Vertex[]> contours = new ArrayList<Vertex[]>();
contours.add(vertices);
this.contours = contours;
this.init(vertices, contours, pa);
}
/**
* The Constructor.
*
* @param innerVertices the vertices used for picking and other calculations, its best to use the biggest contour for this
* @param contours the contour(s) of the shape
* @param pApplet the applet
*/
public MTStencilPolygon(Vertex[] innerVertices, ArrayList<Vertex[]> contours, PApplet pApplet) {
super(innerVertices, pApplet);
this.init(innerVertices, contours, pApplet);
}
/**
* Inits the.
*
* @param innerVertices the inner vertices
* @param contours the contours
* @param pApplet the applet
*/
private void init(Vertex[] innerVertices, ArrayList<Vertex[]> contours, PApplet pApplet){
this.pa = this.getRenderer();
this.contours = contours;
//This class may only be used with opengl anyway!
this.setUseDirectGL(true);
this.setDrawSmooth(true);
//Convert BezierVertices to regular Vertices
//SEGMENTS CONRTOLS THE DETAIL OF THE BEZIER CURVE APPROXIMATION
int segments = 12;
//TODO let the user do that beforehand?
Vertex[] allVerts = ToolsGeometry.createVertexArrFromBezierArr(innerVertices, segments);
this.setVertices(allVerts);
//Replace beziervertices in the SUB-PATHS (outlines) of the glyphs with many calculated regular vertices
//The subpaths are used to draw the outline
this.contours = ToolsGeometry.createVertexArrFromBezierVertexArrays(contours, segments);
reCalcMinMax();
//TODO handle
useGradient = false;
this.setStrokeWeight(1.0f);
//FIXME use?
// this.setEnableTesselation(true);
}
/**
* Re calc min max.
*/
private void reCalcMinMax(){
minMax = ToolsGeometry.getMinXYMaxXY(this.getVerticesLocal());
minX = minMax[0]-5;
minY = minMax[1]-5;
maxX = minMax[2]+5;
maxY = minMax[3]+5;
}
/**
* NOTE: this also sets the contours to one and uses the new
* vertices as the only contour! If you want to set other countours
* use the setContours or setVerticesAndContours method!.
*
* @param vertices the vertices
*/
@Override
public void setVertices(Vertex[] vertices) {
super.setVertices(vertices);
ArrayList<Vertex[]> outlines = new ArrayList<Vertex[]>();
outlines.add(vertices);
this.contours = outlines;
reCalcMinMax();
}
/**
* Sets new outlines for this stencil polygon.
* This is a separate method, because when you want
* to ouline polygons with holes, you have to have separate,
* not connected outline arrays.
*
* @param contours the contours
*/
public void setNewContours(ArrayList<Vertex[]> contours){
this.contours = contours;
this.setMatricesDirty(true);
}
/**
* Sets the new vertices and contours.
*
* @param vertices the vertices
* @param contours the contours
*/
public void setNewVerticesAndContours(Vertex[] vertices, ArrayList<Vertex[]> contours){
this.contours = contours;
setVertices(vertices);
}
/**
* Just draws the character without applying its own local matrix,
* useful when another components want to draw this component.
*
* @param gl the gl
*/
public void drawComponent(GL gl) {
if (isUseDirectGL()){
if (isUseDisplayList()){
int[] displayListIDs = this.getGeometryInfo().getDisplayListIDs();
if (!this.isNoFill())
gl.glCallList(displayListIDs[0]); //Draw fill
if (!this.isNoStroke())
gl.glCallList(displayListIDs[1]); //Draw outline
}else{
drawPureGL(gl);
}
}
}
@Override
public void drawComponent(PGraphics g) {
if (isUseDirectGL()){
GL gl=((PGraphicsOpenGL)this.getRenderer().g).beginGL();
drawComponent(gl);
((PGraphicsOpenGL)this.getRenderer().g).endGL();
}
}
/**
* Draw pure gl.
*
* @param gl the gl
*/
private void drawPureGL(GL gl){
FloatBuffer vertBuff = this.getGeometryInfo().getVertBuff();
FloatBuffer colorBuff = this.getGeometryInfo().getColorBuff();
FloatBuffer strokeColBuff = this.getGeometryInfo().getStrokeColBuff();
gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL.GL_COLOR_ARRAY);
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOVerticesName());
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOColorName());
gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
}else{
gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertBuff);
gl.glColorPointer(4, GL.GL_FLOAT, 0, colorBuff);
}
//Normals
if (this.getGeometryInfo().isContainsNormals()){
gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBONormalsName());
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
}else{
gl.glNormalPointer(GL.GL_FLOAT, 0, this.getGeometryInfo().getNormalsBuff());
}
}
// /*
if (!this.isNoFill()){
/*
///////////////////////
// Draw Into Stencil //
///////////////////////
gl.glClearStencil(0);
gl.glColorMask(false,false,false,false);
gl.glDisable(GL.GL_BLEND);
gl.glDepthMask(false);//remove..?
//Enable stencilbuffer
gl.glEnable(GL.GL_STENCIL_TEST);
// gl.glStencilMask (0x01);
gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INVERT);
gl.glStencilFunc (GL.GL_ALWAYS, 0, ~0);
//Draw into stencil
gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0, vertBuff.capacity()/3);
if (this.getGeometryInfo().isContainsNormals()){
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}
//////////////////////
// Draw fill Overlay//
//////////////////////
gl.glDepthMask(true);
gl.glColorMask(true, true, true, true);
gl.glEnable (GL.GL_BLEND);
gl.glStencilOp (GL.GL_ZERO, GL.GL_ZERO, GL.GL_ZERO);
gl.glStencilFunc(GL.GL_EQUAL, 0x01, 0x01);
if (useGradient){
gl.glBegin (GL.GL_QUADS);
gl.glColor4f(x1R, x1G, x1B, x1A);
gl.glVertex3d (minX, minY, 0.0);
gl.glColor4f(x2R, x2G, x2B, x2A);
gl.glVertex3d (maxX, minY, 0.0);
gl.glColor4f(x3R, x3G, x3B, x3A);
gl.glVertex3d (maxX, maxY, 0.0);
gl.glColor4f(x4R, x4G, x4B, x4A);
gl.glVertex3d (minX, maxY, 0.0);
gl.glEnd ();
}else{
gl.glColor4d (colorBuff.get(0), colorBuff.get(1), colorBuff.get(2), colorBuff.get(3));
gl.glBegin (GL.GL_QUADS);
gl.glVertex3d (minX, minY, 0.0);
gl.glVertex3d (maxX, minY, 0.0);
gl.glVertex3d (maxX, maxY, 0.0);
gl.glVertex3d (minX, maxY, 0.0);
gl.glEnd ();
}
gl.glDisable (GL.GL_STENCIL_TEST);
*/
///////////////////////
// Draw Into Stencil //
///////////////////////
// GLStencilUtil.getInstance().beginDrawClipShape(gl);
if (GLStencilUtil.getInstance().isClipActive()){
gl.glPushAttrib(GL.GL_STENCIL_BUFFER_BIT);
}else{
//Enable stencilbuffer
gl.glEnable(GL.GL_STENCIL_TEST);
gl.glClearStencil(GLStencilUtil.getInstance().stencilValueStack.peek());
gl.glClear(GL.GL_STENCIL_BUFFER_BIT);
}
// gl.glPushAttrib(GL.GL_STENCIL_TEST);
// gl.glDisable(GL.GL_STENCIL_TEST);
// gl.glClearStencil(GLStencilUtil.getInstance().stencilValueStack.peek());
// gl.glClearStencil(0);
// gl.glClear(GL.GL_STENCIL_BUFFER_BIT);
gl.glColorMask(false,false,false,false);
gl.glDisable(GL.GL_BLEND);
gl.glDepthMask(false);//remove..?
// gl.glStencilMask (0x01);
gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INVERT);
gl.glStencilFunc (GL.GL_ALWAYS, 0, ~0);
// gl.glStencilFunc (GL.GL_ALWAYS, GLStencilUtil.getInstance().stencilValueStack.peek(), ~GLStencilUtil.getInstance().stencilValueStack.peek());
//Draw into stencil
gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0, vertBuff.capacity()/3);
if (this.getGeometryInfo().isContainsNormals()){
gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}
//////////////////////
// Draw fill Overlay//
//////////////////////
gl.glDepthMask(true);
gl.glColorMask(true, true, true, true);
gl.glEnable (GL.GL_BLEND);
gl.glStencilOp (GL.GL_KEEP, GL.GL_KEEP, GL.GL_REPLACE);
// gl.glStencilOp (GL.GL_ZERO, GL.GL_REPLACE, GL.GL_REPLACE);
// gl.glStencilOp (GL.GL_KEEP, GL.GL_KEEP, GL.GL_ZERO);
// gl.glStencilOp (GL.GL_ZERO, GL.GL_ZERO, GL.GL_ZERO); //Org
// gl.glStencilFunc(GL.GL_EQUAL, 0x01, 0x01); //org
// if (GLStencilUtil.getInstance().isClipActive()){
gl.glStencilFunc(GL.GL_NOTEQUAL, GLStencilUtil.getInstance().stencilValueStack.peek(), GLStencilUtil.getInstance().stencilValueStack.peek());
// gl.glStencilFunc(GL.GL_NOTEQUAL, 0x01, 0x01);
// }else{
// gl.glStencilFunc(GL.GL_EQUAL, GLStencilUtil.getInstance().stencilValueStack.peek(), GLStencilUtil.getInstance().stencilValueStack.peek());
// }
if (useGradient){
gl.glBegin (GL.GL_QUADS);
gl.glColor4f(x1R, x1G, x1B, x1A);
gl.glVertex3d (minX, minY, 0.0);
gl.glColor4f(x2R, x2G, x2B, x2A);
gl.glVertex3d (maxX, minY, 0.0);
gl.glColor4f(x3R, x3G, x3B, x3A);
gl.glVertex3d (maxX, maxY, 0.0);
gl.glColor4f(x4R, x4G, x4B, x4A);
gl.glVertex3d (minX, maxY, 0.0);
gl.glEnd ();
}else{
gl.glColor4d (colorBuff.get(0), colorBuff.get(1), colorBuff.get(2), colorBuff.get(3));
gl.glBegin (GL.GL_QUADS);
gl.glVertex3d (minX, minY, 0.0);
gl.glVertex3d (maxX, minY, 0.0);
gl.glVertex3d (maxX, maxY, 0.0);
gl.glVertex3d (minX, maxY, 0.0);
gl.glEnd ();
}
if (GLStencilUtil.getInstance().isClipActive()){
gl.glPopAttrib();
}else{
gl.glDisable (GL.GL_STENCIL_TEST);
}
}
//////////////////////////////
// Draw aliased outlines //
//////////////////////////////
if (!isNoStroke()){
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, this.getGeometryInfo().getVBOStrokeColorName());
gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
}else{
gl.glColorPointer(4, GL.GL_FLOAT, 0, strokeColBuff);
}
// gl.glDepthMask(false); //FIXME enable? disable?
// // Draw aliased off-pixels to real
// gl.glEnable (GL.GL_BLEND);
// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
//
// gl.glStencilOp (GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);
// gl.glStencilFunc (GL.GL_EQUAL, 0x00, 0x01); //THIS IS THE ORIGINAL!
// gl.glEnable(GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, true);
gl.glLineWidth(this.getStrokeWeight());
short lineStipple = this.getLineStipple();
if (lineStipple != 0){
gl.glLineStipple(1, lineStipple);
gl.glEnable(GL.GL_LINE_STIPPLE);
}
//DRAW
// gl.glDrawElements(GL.GL_LINE_STRIP, indexBuff.capacity(), GL.GL_UNSIGNED_INT, indexBuff);
// gl.glDrawArrays(GL.GL_LINE_STRIP, 0, vertexArr.length);
/////TEST/// //TODO make vertex pointer arrays?
gl.glColor4d (strokeColBuff.get(0), strokeColBuff.get(1), strokeColBuff.get(2), strokeColBuff.get(3));
for (Vertex[] outline : contours){
gl.glBegin (GL.GL_LINE_STRIP);
for (Vertex vertex : outline)
gl.glVertex3f (vertex.getX(), vertex.getY(), vertex.getZ());
gl.glEnd();
}
// gl.glDisable (GL.GL_LINE_SMOOTH);
//FIXME TEST
Tools3D.setLineSmoothEnabled(gl, false);
gl.glDisable(GL.GL_LINE_STIPPLE);
}
// gl.glDisable (GL.GL_STENCIL_TEST);
// gl.glDepthMask(true);
//Disable client states
gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL.GL_COLOR_ARRAY);
if (this.isUseVBOs()){
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
/* (non-Javadoc)
* @see com.jMT.components.visibleComponents.shapes.AbstractShape#generateDisplayLists()
*/
@Override //TODO JUST COMPILE DrawPureGL() into a list!?
public void generateDisplayLists() {
this.getGeometryInfo().setDisplayListIDs(Tools3D.generateStencilDisplayList(
pa, this.getGeometryInfo().getVertBuff(), this.getGeometryInfo().getTexBuff(), this.getGeometryInfo().getColorBuff(), this.getGeometryInfo().getStrokeColBuff(),
this.getGeometryInfo().getIndexBuff(),true, this.getStrokeWeight(), this.getVerticesLocal(), contours));
}
/**
* returns the vertex arrays which shape the outline of the character.
*
* @return the contours
*/
public List<Vertex[]> getContours(){
return this.contours;
}
/**
* Gets the max x.
*
* @return the max x
*/
public float getMaxX() {
return maxX;
}
/**
* Gets the max y.
*
* @return the max y
*/
public float getMaxY() {
return maxY;
}
/**
* Gets the min x.
*
* @return the min x
*/
public float getMinX() {
return minX;
}
/**
* Gets the min y.
*
* @return the min y
*/
public float getMinY() {
return minY;
}
}