GL android sync

This commit is contained in:
codeanticode
2012-05-30 18:56:14 +00:00
parent ee7ebdfc62
commit 64d189e12f
19 changed files with 7929 additions and 6784 deletions
@@ -4854,6 +4854,26 @@ public class PApplet extends Activity implements PConstants, Runnable {
}
static public String getExtension(String filename) {
String extension;
String lower = filename.toLowerCase();
int dot = filename.lastIndexOf('.');
if (dot == -1) {
extension = "unknown"; // no extension found
}
extension = lower.substring(dot + 1);
// check for, and strip any parameters on the url, i.e.
// filename.jpg?blah=blah&something=that
int question = extension.indexOf('?');
if (question != -1) {
extension = extension.substring(0, question);
}
return extension;
}
//////////////////////////////////////////////////////////////
@@ -385,12 +385,26 @@ public interface PConstants {
* textures with normalized coordinates */
public static final int TEXTURE2D = 0;
/** This constant identifies the nearest texture filter (point sampling) */
//public static final int POINT = 2; // shared with shape feature
/** This constant identifies the linear texture filter, usually called bilinear sampling */
public static final int BILINEAR = 3;
/** This constant identifies the linear/linear function to build mipmaps */
public static final int TRILINEAR = 4;
/** Texture quality constants */
public static final int LOW = 0;
public static final int MEDIUM = 1;
public static final int HIGH = 2;
public static final int BEST = 3;
/** Point sampling: both magnification and minification filtering are set to nearest */
//public static final int POINT = 2; // shared with shape feature
/** Linear sampling: magnification filtering is nearest, minification set to linear */
public static final int LINEAR = 3;
/** Bilinear sampling: both magnification filtering is set to linear and minification
* either to linear-mipmap-nearest (linear interplation is used within a mipmap, but
* not between different mipmaps). */
public static final int BILINEAR = 4;
/** Trilinear sampling: magnification filtering set to linear, minification to
* linear-mipmap-linear, which offers the best mipmap quality since linear
* interpolation to compute the value in each of two maps and then interpolates linearly
* between these two value. */
public static final int TRILINEAR = 5;
/** This constant identifies the clamp-to-edge wrapping mode */
public static final int CLAMP = 0;
/** This constant identifies the repeat wrapping mode */
@@ -399,12 +413,12 @@ public interface PConstants {
// shaders
static public final int FILL_SHADER_SIMPLE = 0;
static public final int FILL_SHADER_LIT = 1;
static public final int FILL_SHADER_TEX = 2;
static public final int FILL_SHADER_FULL = 3;
static public final int LINE_SHADER = 4;
static public final int POINT_SHADER = 5;
static public final int FLAT_SHADER = 0;
static public final int LIGHT_SHADER = 1;
static public final int TEXTURE_SHADER = 2;
static public final int FULL_SHADER = 3;
static public final int LINE3D_SHADER = 4;
static public final int POINT3D_SHADER = 5;
// stroke modes
@@ -513,7 +527,10 @@ public interface PConstants {
static final int ENABLE_PERSPECTIVE_CORRECTED_LINES = 10;
static final int DISABLE_PERSPECTIVE_CORRECTED_LINES = -10;
static final int HINT_COUNT = 11;
static final int DISABLE_TEXTURE_MIPMAPS = 11;
static final int ENABLE_TEXTURE_MIPMAPS = -11;
static final int HINT_COUNT = 12;
// error messages
@@ -127,7 +127,7 @@ public class PGraphics extends PImage implements PConstants {
public boolean smooth = false;
/// the anti-aliasing level for renderers that support it
protected int antialias;
protected int quality;
// ........................................................
@@ -517,8 +517,11 @@ public class PGraphics extends PImage implements PConstants {
* vertex() calls will be based on coordinates that are
* based on the IMAGE or NORMALIZED.
*/
public int textureMode;
public int textureMode = IMAGE;
public int textureWrap = CLAMP;
public int textureQuality = BEST;
/**
* Current horizontal coordinate for texture, will always
* be between 0 and 1, even if using textureMode(IMAGE).
@@ -5048,7 +5051,7 @@ public class PGraphics extends PImage implements PConstants {
* Display a warning that the specified method is only available with 3D.
* @param method The method name (no parentheses)
*/
static protected void showDepthWarning(String method) {
static public void showDepthWarning(String method) {
showWarning(method + "() can only be used with a renderer that " +
"supports 3D, such as P3D or OPENGL.");
}
@@ -5059,7 +5062,7 @@ public class PGraphics extends PImage implements PConstants {
* can only be used with x and y parameters in this renderer.
* @param method The method name (no parentheses)
*/
static protected void showDepthWarningXYZ(String method) {
static public void showDepthWarningXYZ(String method) {
showWarning(method + "() with x, y, and z coordinates " +
"can only be used with a renderer that " +
"supports 3D, such as P3D or OPENGL. " +
@@ -5070,7 +5073,7 @@ public class PGraphics extends PImage implements PConstants {
/**
* Display a warning that the specified method is simply unavailable.
*/
static protected void showMethodWarning(String method) {
static public void showMethodWarning(String method) {
showWarning(method + "() is not available with this renderer.");
}
@@ -5080,7 +5083,7 @@ public class PGraphics extends PImage implements PConstants {
* other variations are). For instance, if vertex(x, y, u, v) is not
* available, but vertex(x, y) is just fine.
*/
static protected void showVariationWarning(String str) {
static public void showVariationWarning(String str) {
showWarning(str + " is not available with this renderer.");
}
@@ -5090,7 +5093,7 @@ public class PGraphics extends PImage implements PConstants {
* that it could be either a completely missing function, although other
* variations of it may still work properly.
*/
static protected void showMissingWarning(String method) {
static public void showMissingWarning(String method) {
showWarning(method + "(), or this particular variation of it, " +
"is not available with this renderer.");
}
+86 -72
View File
@@ -336,6 +336,14 @@ public class PShape implements PConstants {
}
/**
* Return true if this shape is 2D. Defaults to true.
*/
public boolean is2D() {
return true;
}
/**
* Return true if this shape is 3D. Defaults to false.
*/
@@ -343,6 +351,7 @@ public class PShape implements PConstants {
return false;
}
///////////////////////////////////////////////////////////
//
@@ -663,120 +672,125 @@ public class PShape implements PConstants {
g.popStyle();
}
}
////////////////////////////////////////////////////////////////////////
//
// The new copy methods to put an SVG into a PShape3D, for example
// Shape copy
public PShape copy(PGraphics g) {
PShape res = null;
if (family == GROUP) {
res = g.createShape(GROUP);
copyGroup(g, res);
} else if (family == PRIMITIVE) {
res = g.createShape(kind, params);
copyPrimitive(res);
} else if (family == GEOMETRY) {
res = g.createShape(kind);
copyGeometry(res);
} else if (family == PATH) {
res = g.createShape(PATH);
copyPath(res);
static public PShape createShape(PApplet parent, PShape src) {
PShape dest = null;
if (src.family == GROUP) {
dest = parent.createShape(GROUP);
PShape.copyGroup(parent, src, dest);
} else if (src.family == PRIMITIVE) {
dest = parent.createShape(src.kind, src.params);
PShape.copyPrimitive(src, dest);
} else if (src.family == GEOMETRY) {
dest = parent.createShape(src.kind);
PShape.copyGeometry(src, dest);
} else if (src.family == PATH) {
dest = parent.createShape(PATH);
PShape.copyPath(src, dest);
}
return res;
dest.setName(src.name);
return dest;
}
protected void copyGroup(PGraphics g, PShape s) {
if (matrix != null) {
s.applyMatrix(matrix);
}
copyStyles(s);
copyImage(s);
for (int i = 0; i < childCount; i++) {
PShape c = children[i].copy(g);
s.addChild(c);
static public void copyGroup(PApplet parent, PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
for (int i = 0; i < src.childCount; i++) {
PShape c = PShape.createShape(parent, src.children[i]);
dest.addChild(c);
}
}
protected void copyPrimitive(PShape s) {
if (matrix != null) {
s.applyMatrix(matrix);
}
copyStyles(s);
copyImage(s);
static public void copyPrimitive(PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
}
protected void copyGeometry(PShape s) {
if (matrix != null) {
s.applyMatrix(matrix);
}
copyStyles(s);
copyImage(s);
static public void copyGeometry(PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
if (style) {
for (int i = 0; i < vertexCount; i++) {
float[] vert = vertices[i];
if (src.style) {
for (int i = 0; i < src.vertexCount; i++) {
float[] vert = src.vertices[i];
// Do we need to copy these as well?
// s.ambient(vert[AR] * 255, vert[AG] * 255, vert[AB] * 255);
// s.specular(vert[SPR] * 255, vert[SPG] * 255, vert[SPB] * 255);
// s.emissive(vert[ER] * 255, vert[EG] * 255, vert[EB] * 255);
// s.shininess(vert[SHINE]);
s.normal(vert[NX], vert[NY], vert[NZ]);
s.vertex(vert[X], vert[Y], vert[Z], vert[U], vert[V]);
dest.normal(vert[NX], vert[NY], vert[NZ]);
dest.vertex(vert[X], vert[Y], vert[Z], vert[U], vert[V]);
}
} else {
for (int i = 0; i < vertexCount; i++) {
float[] vert = vertices[i];
for (int i = 0; i < src.vertexCount; i++) {
float[] vert = src.vertices[i];
if (vert[PGraphics.Z] == 0) {
s.vertex(vert[X], vert[Y]);
dest.vertex(vert[X], vert[Y]);
} else {
s.vertex(vert[X], vert[Y], vert[Z]);
dest.vertex(vert[X], vert[Y], vert[Z]);
}
}
}
s.end();
dest.end();
}
protected void copyPath(PShape s) {
if (matrix != null) {
s.applyMatrix(matrix);
}
copyStyles(s);
copyImage(s);
s.close = close;
s.setPath(vertexCount, vertices, vertexCodeCount, vertexCodes);
static public void copyPath(PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
dest.close = src.close;
dest.setPath(src.vertexCount, src.vertices, src.vertexCodeCount, src.vertexCodes);
}
protected void copyStyles(PShape s) {
if (stroke) {
s.stroke = true;
s.strokeColor = strokeColor;
s.strokeWeight = strokeWeight;
s.strokeCap = strokeCap;
s.strokeJoin = strokeJoin;
static public void copyMatrix(PShape src, PShape dest) {
if (src.matrix != null) {
dest.applyMatrix(src.matrix);
}
}
static public void copyStyles(PShape src, PShape dest) {
if (src.stroke) {
dest.stroke = true;
dest.strokeColor = src.strokeColor;
dest.strokeWeight = src.strokeWeight;
dest.strokeCap = src.strokeCap;
dest.strokeJoin = src.strokeJoin;
} else {
s.stroke = false;
dest.stroke = false;
}
if (fill) {
s.fill = true;
s.fillColor = fillColor;
if (src.fill) {
dest.fill = true;
dest.fillColor = src.fillColor;
} else {
s.fill = false;
dest.fill = false;
}
}
protected void copyImage(PShape s) {
if (image != null) {
s.texture(image);
static public void copyImage(PShape src, PShape dest) {
if (src.image != null) {
dest.texture(src.image);
}
}
////////////////////////////////////////////////////////////////////////
@@ -1,3 +1,5 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -23,7 +25,7 @@
* have any questions.
*/
package processing.opengl.geom;
package processing.opengl;
import processing.core.PMatrix2D;
@@ -392,7 +394,7 @@ public class LinePath {
float miterlimit, PMatrix2D transform) {
final LinePath dest = new LinePath();
strokeTo(src, weight, caps, join, miterlimit, transform, new LineSink() {
strokeTo(src, weight, caps, join, miterlimit, transform, new LineStroker() {
public void moveTo(int x0, int y0) {
dest.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0));
}
@@ -418,7 +420,7 @@ public class LinePath {
private static void strokeTo(LinePath src, float width, int caps, int join,
float miterlimit, PMatrix2D transform,
LineSink lsink) {
LineStroker lsink) {
lsink = new LineStroker(lsink, FloatToS15_16(width), caps, join,
FloatToS15_16(miterlimit),
transform == null ? identity : transform);
@@ -428,7 +430,7 @@ public class LinePath {
}
private static void pathTo(PathIterator pi, LineSink lsink) {
private static void pathTo(PathIterator pi, LineStroker lsink) {
float coords[] = new float[2];
while (!pi.isDone()) {
switch (pi.currentSegment(coords)) {
@@ -1,3 +1,5 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -23,28 +25,20 @@
* questions.
*/
package processing.opengl.geom;
package processing.opengl;
import processing.core.PMatrix2D;
public class LineStroker extends LineSink {
LineSink output;
int lineWidth;
int capStyle;
int joinStyle;
int miterLimit;
int m00, m01;
int m10, m11;
int lineWidth2;
long scaledLineWidth2;
public class LineStroker {
private LineStroker output;
// private int lineWidth;
private int capStyle;
private int joinStyle;
// private int miterLimit;
private int m00, m01;
private int m10, m11;
private int lineWidth2;
private long scaledLineWidth2;
// For any pen offset (pen_dx, pen_dy) that does not depend on
// the line orientation, the pen should be transformed so that:
@@ -59,45 +53,30 @@ public class LineStroker extends LineSink {
//
// pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta))
// pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta))
int numPenSegments;
private int numPenSegments;
private int[] pen_dx;
private int[] pen_dy;
int[] pen_dx;
int[] pen_dy;
boolean[] penIncluded;
int[] join;
int[] offset = new int[2];
int[] reverse = new int[100];
int[] miter = new int[2];
long miterLimitSq;
int prev;
int rindex;
boolean started;
boolean lineToOrigin;
boolean joinToOrigin;
int sx0, sy0, sx1, sy1, x0, y0, x1, y1;
int mx0, my0, mx1, my1, omx, omy;
int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0;
double m00_2_m01_2;
double m10_2_m11_2;
double m00_m10_m01_m11;
private boolean[] penIncluded;
private int[] join;
private int[] offset = new int[2];
private int[] reverse = new int[100];
private int[] miter = new int[2];
private long miterLimitSq;
private int prev;
private int rindex;
private boolean started;
private boolean lineToOrigin;
private boolean joinToOrigin;
// private int sx0, sy0, sx1, sy1, x0, y0, x1, y1;
// private int mx0, my0, mx1, my1, omx, omy;
// private int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0;
private int sx0, sy0, sx1, sy1, x0, y0;
private int mx0, my0, omx, omy;
private int px0, py0;
private double m00_2_m01_2;
private double m10_2_m11_2;
private double m00_m10_m01_m11;
/**
* Empty constructor. <code>setOutput</code> and <code>setParameters</code>
@@ -110,7 +89,7 @@ public class LineStroker extends LineSink {
* Constructs a <code>LineStroker</code>.
*
* @param output
* an output <code>LineSink</code>.
* an output <code>LineStroker</code>.
* @param lineWidth
* the desired line width in pixels, in S15.16 format.
* @param capStyle
@@ -127,19 +106,19 @@ public class LineStroker extends LineSink {
* required in order to produce consistently shaped end caps and
* joins.
*/
public LineStroker(LineSink output, int lineWidth, int capStyle, int joinStyle,
public LineStroker(LineStroker output, int lineWidth, int capStyle, int joinStyle,
int miterLimit, PMatrix2D transform) {
setOutput(output);
setParameters(lineWidth, capStyle, joinStyle, miterLimit, transform);
}
/**
* Sets the output <code>LineSink</code> of this <code>LineStroker</code>.
* Sets the output <code>LineStroker</code> of this <code>LineStroker</code>.
*
* @param output
* an output <code>LineSink</code>.
* an output <code>LineStroker</code>.
*/
public void setOutput(LineSink output) {
public void setOutput(LineStroker output) {
this.output = output;
}
@@ -169,12 +148,12 @@ public class LineStroker extends LineSink {
this.m10 = LinePath.FloatToS15_16(transform.m10);
this.m11 = LinePath.FloatToS15_16(transform.m11);
this.lineWidth = lineWidth;
// this.lineWidth = lineWidth;
this.lineWidth2 = lineWidth >> 1;
this.scaledLineWidth2 = ((long) m00 * lineWidth2) >> 16;
this.capStyle = capStyle;
this.joinStyle = joinStyle;
this.miterLimit = miterLimit;
// this.miterLimit = miterLimit;
this.m00_2_m01_2 = (double) m00 * m00 + (double) m01 * m01;
this.m10_2_m11_2 = (double) m10 * m10 + (double) m11 * m11;
@@ -203,7 +182,7 @@ public class LineStroker extends LineSink {
for (int i = 0; i < numPenSegments; i++) {
double r = lineWidth / 2.0;
double theta = (double) i * 2.0 * Math.PI / numPenSegments;
double theta = i * 2 * Math.PI / numPenSegments;
double cos = Math.cos(theta);
double sin = Math.sin(theta);
@@ -528,12 +507,12 @@ public class LineStroker extends LineSink {
emitLineTo(x0 - mx, y0 - my, true);
emitLineTo(x1 - mx, y1 - my, true);
lx0 = x1 + mx;
ly0 = y1 + my;
lx0p = x1 - mx;
ly0p = y1 - my;
lx1 = x1;
ly1 = y1;
// lx0 = x1 + mx;
// ly0 = y1 + my;
// lx0p = x1 - mx;
// ly0p = y1 - my;
// lx1 = x1;
// ly1 = y1;
this.omx = mx;
this.omy = my;
@@ -639,8 +618,8 @@ public class LineStroker extends LineSink {
long lineLength(long ldx, long ldy) {
long ldet = ((long) m00 * m11 - (long) m01 * m10) >> 16;
long la = ((long) ldy * m00 - (long) ldx * m10) / ldet;
long lb = ((long) ldy * m01 - (long) ldx * m11) / ldet;
long la = (ldy * m00 - ldx * m10) / ldet;
long lb = (ldy * m01 - ldx * m11) / ldet;
long llen = (int) LinePath.hypot(la, lb);
return llen;
}
@@ -650,8 +629,8 @@ public class LineStroker extends LineSink {
drawRoundJoin(x0, y0, omx, omy, -omx, -omy, 1, false, false,
ROUND_JOIN_THRESHOLD);
} else if (capStyle == LinePath.CAP_SQUARE) {
long ldx = (long) (px0 - x0);
long ldy = (long) (py0 - y0);
long ldx = px0 - x0;
long ldy = py0 - y0;
long llen = lineLength(ldx, ldy);
if (0 < llen) {
long s = (long) lineWidth2 * 65536 / llen;
@@ -673,8 +652,8 @@ public class LineStroker extends LineSink {
drawRoundJoin(sx0, sy0, -mx0, -my0, mx0, my0, 1, false, false,
ROUND_JOIN_THRESHOLD);
} else if (capStyle == LinePath.CAP_SQUARE) {
long ldx = (long) (sx1 - sx0);
long ldy = (long) (sy1 - sy0);
long ldx = sx1 - sx0;
long ldy = sy1 - sy0;
long llen = lineLength(ldx, ldy);
if (0 < llen) {
long s = (long) lineWidth2 * 65536 / llen;
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2011-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -23,11 +23,13 @@
package processing.opengl;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PFont;
import processing.core.PImage;
import java.util.HashMap;
import processing.core.*;
/**
* All the infrastructure needed for optimized font rendering
* in OpenGL. Basically, this special class is needed because
@@ -50,6 +52,7 @@ class PFontTexture implements PConstants {
protected PGraphicsOpenGL pg;
protected PGL pgl;
protected PFont font;
protected boolean is3D;
protected int maxTexWidth;
protected int maxTexHeight;
@@ -63,11 +66,12 @@ class PFontTexture implements PConstants {
protected TextureInfo[] glyphTexinfos;
protected HashMap<PFont.Glyph, TextureInfo> texinfoMap;
public PFontTexture(PApplet parent, PFont font, int maxw, int maxh) {
public PFontTexture(PApplet parent, PFont font, int maxw, int maxh, boolean is3D) {
this.parent = parent;
this.font = font;
pg = (PGraphicsOpenGL)parent.g;
pgl = pg.pgl;
this.is3D = is3D;
initTexture(maxw, maxh);
}
@@ -113,7 +117,17 @@ class PFontTexture implements PConstants {
resize = false;
}
PTexture tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, BILINEAR));
PTexture tex;
if (is3D) {
// Bilinear sampling ensures that the texture doesn't look pixelated either
// when it is magnified or minified...
tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, BILINEAR, false));
} else {
// ...however, the effect of bilinear sampling is to add some blurriness to the text
// in its original size. In 2D, we assume that text will be shown at its original
// size, so linear sampling is chosen instead (which only affects minimized text).
tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, LINEAR, false));
}
if (textures == null) {
textures = new PTexture[1];
@@ -1,31 +1,33 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Part of the Processing project - http://processing.org
Copyright (c) 2010 Ben Fry and Casey Reas
Copyright (c) 2011-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.opengl;
import java.nio.IntBuffer;
import processing.core.PApplet;
import processing.core.PConstants;
import java.nio.IntBuffer;
/**
* Encapsulates a Frame Buffer Object for offscreen rendering.
* When created with onscreen == true, it represents the normal
@@ -36,6 +38,7 @@ import processing.core.PConstants;
*
* By Andres Colubri.
*/
public class PFramebuffer implements PConstants {
protected PApplet parent;
protected PGraphicsOpenGL pg;
+5 -4
View File
@@ -109,7 +109,7 @@ public class PGL {
/** Maximum length of linear paths to be stroked with the
* full algorithm that generates accurate caps and joins.
*/
public static final int MAX_CAPS_JOINS_LENGTH = 500;
public static final int MAX_CAPS_JOINS_LENGTH = 1000;
/** Minimum array size to use arrayCopy method(). **/
static protected final int MIN_ARRAYCOPY_SIZE = 2;
@@ -184,9 +184,10 @@ public class PGL {
public static final int GL_UNSIGNED_SHORT = GLES20.GL_UNSIGNED_SHORT;
public static final int GL_FLOAT = GLES20.GL_FLOAT;
public static final int GL_NEAREST = GLES20.GL_NEAREST;
public static final int GL_LINEAR = GLES20.GL_LINEAR;
public static final int GL_LINEAR_MIPMAP_LINEAR = GLES20.GL_LINEAR_MIPMAP_LINEAR;
public static final int GL_NEAREST = GLES20.GL_NEAREST;
public static final int GL_LINEAR = GLES20.GL_LINEAR;
public static final int GL_LINEAR_MIPMAP_NEAREST = GLES20.GL_LINEAR_MIPMAP_NEAREST;
public static final int GL_LINEAR_MIPMAP_LINEAR = GLES20.GL_LINEAR_MIPMAP_LINEAR;
public static final int GL_CLAMP_TO_EDGE = GLES20.GL_CLAMP_TO_EDGE;
public static final int GL_REPEAT = GLES20.GL_REPEAT;
@@ -22,28 +22,436 @@
package processing.opengl;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PMatrix3D;
import processing.core.PShape;
import processing.core.PShapeSVG;
import processing.data.XML;
public class PGraphics2D extends PGraphicsOpenGL {
public PGraphics2D() {
super();
//hints[ENABLE_ACCURATE_2D] = true;
hints[ENABLE_PERSPECTIVE_CORRECTED_LINES] = false;
}
/**
* Return true if this renderer supports 2D drawing. Defaults to true.
*/
//////////////////////////////////////////////////////////////
// RENDERER SUPPORT QUERIES
public boolean is2D() {
return true;
}
/**
* Return true if this renderer supports 2D drawing. Defaults to false.
*/
public boolean is3D() {
return false;
}
//////////////////////////////////////////////////////////////
// HINTS
public void hint(int which) {
if (which == ENABLE_PERSPECTIVE_CORRECTED_LINES) {
showWarning("2D lines cannot be perspective-corrected.");
return;
}
super.hint(which);
}
//////////////////////////////////////////////////////////////
// SHAPE
public void shape(PShape shape) {
if (shape.is2D()) {
super.shape(shape);
} else {
showWarning("The shape object is not 2D, cannot be displayed with this renderer");
}
}
public void shape(PShape shape, float x, float y) {
if (shape.is2D()) {
super.shape(shape, x, y);
} else {
showWarning("The shape object is not 2D, cannot be displayed with this renderer");
}
}
public void shape(PShape shape, float a, float b, float c, float d) {
if (shape.is2D()) {
super.shape(shape, a, b, c, d);
} else {
showWarning("The shape object is not 2D, cannot be displayed with this renderer");
}
}
public void shape(PShape shape, float x, float y, float z) {
showDepthWarningXYZ("shape");
}
public void shape(PShape shape, float x, float y, float z, float c, float d, float e) {
showDepthWarningXYZ("shape");
}
//////////////////////////////////////////////////////////////
// SHAPE I/O
static protected boolean isSupportedExtension(String extension) {
return extension.equals("svg") || extension.equals("svgz");
}
static protected PShape2D loadShapeImpl(PGraphics pg, String filename, String extension) {
PShapeSVG svg = null;
if (extension.equals("svg")) {
svg = new PShapeSVG(pg.parent, filename);
} else if (extension.equals("svgz")) {
try {
InputStream input = new GZIPInputStream(pg.parent.createInput(filename));
XML xml = new XML(PApplet.createReader(input));
svg = new PShapeSVG(xml);
} catch (IOException e) {
e.printStackTrace();
}
}
if (svg != null) {
PShape2D p2d = PShape2D.createShape(pg.parent, svg);
return p2d;
} else {
return null;
}
}
//////////////////////////////////////////////////////////////
// SHAPE CREATION
public PShape createShape(PShape source) {
return PShape2D.createShape(parent, source);
}
public PShape createShape() {
return createShape(POLYGON);
}
public PShape createShape(int type) {
return createShapeImpl(parent, type);
}
public PShape createShape(int kind, float... p) {
return createShapeImpl(parent, kind, p);
}
static protected PShape2D createShapeImpl(PApplet parent, int type) {
PShape2D shape = null;
if (type == PShape.GROUP) {
shape = new PShape2D(parent, PShape.GROUP);
} else if (type == PShape.PATH) {
shape = new PShape2D(parent, PShape.PATH);
} else if (type == POINTS) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(POINTS);
} else if (type == LINES) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(LINES);
} else if (type == TRIANGLE || type == TRIANGLES) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLES);
} else if (type == TRIANGLE_FAN) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLE_FAN);
} else if (type == TRIANGLE_STRIP) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLE_STRIP);
} else if (type == QUAD || type == QUADS) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(QUADS);
} else if (type == QUAD_STRIP) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(QUAD_STRIP);
} else if (type == POLYGON) {
shape = new PShape2D(parent, PShape.GEOMETRY);
shape.setKind(POLYGON);
}
return shape;
}
static protected PShape2D createShapeImpl(PApplet parent, int kind, float... p) {
PShape2D shape = null;
int len = p.length;
if (kind == POINT) {
if (len != 2) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(POINT);
} else if (kind == LINE) {
if (len != 4) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(LINE);
} else if (kind == TRIANGLE) {
if (len != 6) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(TRIANGLE);
} else if (kind == QUAD) {
if (len != 8) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(QUAD);
} else if (kind == RECT) {
if (len != 4 && len != 5 && len != 8) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(RECT);
} else if (kind == ELLIPSE) {
if (len != 4) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(ELLIPSE);
} else if (kind == ARC) {
if (len != 6) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape2D(parent, PShape.PRIMITIVE);
shape.setKind(ARC);
} else if (kind == BOX) {
showWarning("Primitive not supported in 2D");
} else if (kind == SPHERE) {
showWarning("Primitive not supported in 2D");
} else {
showWarning("Unrecognized primitive type");
}
if (shape != null) {
shape.setParams(p);
}
return shape;
}
//////////////////////////////////////////////////////////////
// BEZIER VERTICES
public void bezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
showDepthWarningXYZ("bezierVertex");
}
//////////////////////////////////////////////////////////////
// QUADRATIC BEZIER VERTICES
public void quadraticVertex(float x2, float y2, float z2,
float x4, float y4, float z4) {
showDepthWarningXYZ("quadVertex");
}
//////////////////////////////////////////////////////////////
// CURVE VERTICES
public void curveVertex(float x, float y, float z) {
showDepthWarningXYZ("curveVertex");
}
//////////////////////////////////////////////////////////////
// BOX
public void box(float w, float h, float d) {
showMethodWarning("box");
}
//////////////////////////////////////////////////////////////
// SPHERE
public void sphere(float r) {
showMethodWarning("sphere");
}
//////////////////////////////////////////////////////////////
// VERTEX SHAPES
public void vertex(float x, float y, float z) {
showDepthWarningXYZ("vertex");
}
public void vertex(float x, float y, float z, float u, float v) {
showDepthWarningXYZ("vertex");
}
//////////////////////////////////////////////////////////////
// MATRIX TRANSFORMATIONS
public void translate(float tx, float ty, float tz) {
showVariationWarning("translate");
}
public void rotateX(float angle) {
showDepthWarning("rotateX");
}
public void rotateY(float angle) {
showDepthWarning("rotateY");
}
public void rotateZ(float angle) {
showDepthWarning("rotateZ");
}
public void rotate(float angle, float vx, float vy, float vz) {
showVariationWarning("rotate");
}
public void applyMatrix(PMatrix3D source) {
showVariationWarning("applyMatrix");
}
public void applyMatrix(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
showVariationWarning("applyMatrix");
}
public void scale(float sx, float sy, float sz) {
showDepthWarningXYZ("scale");
}
//////////////////////////////////////////////////////////////
// SCREEN AND MODEL COORDS
public float screenX(float x, float y, float z) {
showDepthWarningXYZ("screenX");
return 0;
}
public float screenY(float x, float y, float z) {
showDepthWarningXYZ("screenY");
return 0;
}
public float screenZ(float x, float y, float z) {
showDepthWarningXYZ("screenZ");
return 0;
}
public PMatrix3D getMatrix(PMatrix3D target) {
showVariationWarning("getMatrix");
return target;
}
public void setMatrix(PMatrix3D source) {
showVariationWarning("setMatrix");
}
//////////////////////////////////////////////////////////////
// LIGHTS
public void lights() {
showMethodWarning("lights");
}
public void noLights() {
showMethodWarning("noLights");
}
public void ambientLight(float red, float green, float blue) {
showMethodWarning("ambientLight");
}
public void ambientLight(float red, float green, float blue,
float x, float y, float z) {
showMethodWarning("ambientLight");
}
public void directionalLight(float red, float green, float blue,
float nx, float ny, float nz) {
showMethodWarning("directionalLight");
}
public void pointLight(float red, float green, float blue,
float x, float y, float z) {
showMethodWarning("pointLight");
}
public void spotLight(float red, float green, float blue,
float x, float y, float z,
float nx, float ny, float nz,
float angle, float concentration) {
showMethodWarning("spotLight");
}
public void lightFalloff(float constant, float linear, float quadratic) {
showMethodWarning("lightFalloff");
}
public void lightSpecular(float v1, float v2, float v3) {
showMethodWarning("lightSpecular");
}
}
@@ -22,6 +22,550 @@
package processing.opengl;
import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.Hashtable;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.core.PShape;
import processing.core.PVector;
public class PGraphics3D extends PGraphicsOpenGL {
//////////////////////////////////////////////////////////////
// RENDERER SUPPORT QUERIES
public boolean is2D() {
return false;
}
public boolean is3D() {
return true;
}
//////////////////////////////////////////////////////////////
// SHAPE I/O
static protected boolean isSupportedExtension(String extension) {
return extension.equals("obj");
}
static protected PShape loadShapeImpl(PGraphics pg, String filename, String ext) {
ArrayList<PVector> vertices = new ArrayList<PVector>();
ArrayList<PVector> normals = new ArrayList<PVector>();
ArrayList<PVector> textures = new ArrayList<PVector>();
ArrayList<OBJFace> faces = new ArrayList<OBJFace>();
ArrayList<OBJMaterial> materials = new ArrayList<OBJMaterial>();
BufferedReader reader = pg.parent.createReader(filename);
parseOBJ(pg.parent, reader, vertices, normals, textures, faces, materials);
int prevColorMode = pg.colorMode;
float prevColorModeX = pg.colorModeX;
float prevColorModeY = pg.colorModeY;
float prevColorModeZ = pg.colorModeZ;
float prevColorModeA = pg.colorModeA;
boolean prevStroke = pg.stroke;
int prevTextureMode = pg.textureMode;
pg.colorMode(RGB, 1);
pg.stroke = false;
pg.textureMode = NORMAL;
// The OBJ geometry is stored in a group shape,
// with each face in a separate child geometry
// shape.
PShape root = createShapeImpl(pg.parent, GROUP);
int mtlIdxCur = -1;
OBJMaterial mtl = null;
for (int i = 0; i < faces.size(); i++) {
OBJFace face = faces.get(i);
// Getting current material.
if (mtlIdxCur != face.matIdx) {
mtlIdxCur = PApplet.max(0, face.matIdx); // To make sure that at least we get the default material.
mtl = materials.get(mtlIdxCur);
}
// Creating child shape for current face.
PShape child;
if (face.vertIdx.size() == 3) {
child = createShapeImpl(pg.parent, TRIANGLES); // Face is a triangle, so using appropriate shape kind.
} else if (face.vertIdx.size() == 4) {
child = createShapeImpl(pg.parent, QUADS); // Face is a quad, so using appropriate shape kind.
} else {
child = createShapeImpl(pg.parent, POLYGON); // Face is a general polygon
}
// Setting material properties for the new face
child.fill(mtl.kd.x, mtl.kd.y, mtl.kd.z);
child.ambient(mtl.ka.x, mtl.ka.y, mtl.ka.z);
child.specular(mtl.ks.x, mtl.ks.y, mtl.ks.z);
child.shininess(mtl.ns);
if (mtl.kdMap != null) {
// If current material is textured, then tinting the texture using the diffuse color.
child.tint(mtl.kd.x, mtl.kd.y, mtl.kd.z, mtl.d);
}
for (int j = 0; j < face.vertIdx.size(); j++){
int vertIdx, normIdx;
PVector vert, norms;
vert = norms = null;
vertIdx = face.vertIdx.get(j).intValue() - 1;
vert = vertices.get(vertIdx);
if (j < face.normIdx.size()) {
normIdx = face.normIdx.get(j).intValue() - 1;
if (-1 < normIdx) {
norms = normals.get(normIdx);
}
}
if (mtl != null && mtl.kdMap != null) {
// This face is textured.
int texIdx;
PVector tex = null;
if (j < face.texIdx.size()) {
texIdx = face.texIdx.get(j).intValue() - 1;
if (-1 < texIdx) {
tex = textures.get(texIdx);
}
}
child.texture(mtl.kdMap);
if (norms != null) {
child.normal(norms.x, norms.y, norms.z);
}
if (tex != null) {
child.vertex(vert.x, vert.y, vert.z, tex.x, tex.y);
} else {
child.vertex(vert.x, vert.y, vert.z);
}
} else {
// This face is not textured.
if (norms != null) {
child.normal(norms.x, norms.y, norms.z);
}
child.vertex(vert.x, vert.y, vert.z);
}
}
child.end(CLOSE);
root.addChild(child);
}
pg.colorMode(prevColorMode, prevColorModeX, prevColorModeY, prevColorModeZ, prevColorModeA);
pg.stroke = prevStroke;
pg.textureMode = prevTextureMode;
return root;
}
//////////////////////////////////////////////////////////////
// SHAPE CREATION
public PShape createShape(PShape source) {
return PShape3D.createShape(parent, source);
}
public PShape createShape() {
return createShape(POLYGON);
}
public PShape createShape(int type) {
return createShapeImpl(parent, type);
}
public PShape createShape(int kind, float... p) {
return createShapeImpl(parent, kind, p);
}
static protected PShape3D createShapeImpl(PApplet parent, int type) {
PShape3D shape = null;
if (type == PShape.GROUP) {
shape = new PShape3D(parent, PShape.GROUP);
} else if (type == PShape.PATH) {
shape = new PShape3D(parent, PShape.PATH);
} else if (type == POINTS) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(POINTS);
} else if (type == LINES) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(LINES);
} else if (type == TRIANGLE || type == TRIANGLES) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLES);
} else if (type == TRIANGLE_FAN) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLE_FAN);
} else if (type == TRIANGLE_STRIP) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(TRIANGLE_STRIP);
} else if (type == QUAD || type == QUADS) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(QUADS);
} else if (type == QUAD_STRIP) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(QUAD_STRIP);
} else if (type == POLYGON) {
shape = new PShape3D(parent, PShape.GEOMETRY);
shape.setKind(POLYGON);
}
return shape;
}
static protected PShape3D createShapeImpl(PApplet parent, int kind, float... p) {
PShape3D shape = null;
int len = p.length;
if (kind == POINT) {
if (len != 2 && len != 3) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(POINT);
} else if (kind == LINE) {
if (len != 4 && len != 6) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(LINE);
} else if (kind == TRIANGLE) {
if (len != 6) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(TRIANGLE);
} else if (kind == QUAD) {
if (len != 8) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(QUAD);
} else if (kind == RECT) {
if (len != 4 && len != 5 && len != 8) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(RECT);
} else if (kind == ELLIPSE) {
if (len != 4) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(ELLIPSE);
} else if (kind == ARC) {
if (len != 6) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(ARC);
} else if (kind == BOX) {
if (len != 1 && len != 3) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(BOX);
} else if (kind == SPHERE) {
if (len != 1) {
showWarning("Wrong number of parameters");
return null;
}
shape = new PShape3D(parent, PShape.PRIMITIVE);
shape.setKind(SPHERE);
} else {
showWarning("Unrecognized primitive type");
}
if (shape != null) {
shape.setParams(p);
}
return shape;
}
//////////////////////////////////////////////////////////////
// OBJ LOADING
static protected void parseOBJ(PApplet parent,
BufferedReader reader, ArrayList<PVector> vertices,
ArrayList<PVector> normals,
ArrayList<PVector> textures,
ArrayList<OBJFace> faces,
ArrayList<OBJMaterial> materials) {
Hashtable<String, Integer> mtlTable = new Hashtable<String, Integer>();
int mtlIdxCur = -1;
boolean readv, readvn, readvt;
try {
readv = readvn = readvt = false;
String line;
String gname = "object";
while ((line = reader.readLine()) != null) {
// Parse the line.
// The below patch/hack comes from Carlos Tomas Marti and is a
// fix for single backslashes in Rhino obj files
// BEGINNING OF RHINO OBJ FILES HACK
// Statements can be broken in multiple lines using '\' at the
// end of a line.
// In regular expressions, the backslash is also an escape
// character.
// The regular expression \\ matches a single backslash. This
// regular expression as a Java string, becomes "\\\\".
// That's right: 4 backslashes to match a single one.
while (line.contains("\\")) {
line = line.split("\\\\")[0];
final String s = reader.readLine();
if (s != null)
line += s;
}
// END OF RHINO OBJ FILES HACK
String[] elements = line.split("\\s+");
// if not a blank line, process the line.
if (elements.length > 0) {
if (elements[0].equals("v")) {
// vertex
PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(),
Float.valueOf(elements[2]).floatValue(),
Float.valueOf(elements[3]).floatValue());
vertices.add(tempv);
readv = true;
} else if (elements[0].equals("vn")) {
// normal
PVector tempn = new PVector(Float.valueOf(elements[1]).floatValue(),
Float.valueOf(elements[2]).floatValue(),
Float.valueOf(elements[3]).floatValue());
normals.add(tempn);
readvn = true;
} else if (elements[0].equals("vt")) {
// uv, inverting v to take into account Processing's invertex Y axis with
// respect to OpenGL.
PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(),
1 - Float.valueOf(elements[2]).floatValue());
textures.add(tempv);
readvt = true;
} else if (elements[0].equals("o")) {
// Object name is ignored, for now.
} else if (elements[0].equals("mtllib")) {
if (elements[1] != null) {
BufferedReader mreader = parent.createReader(elements[1]);
if (mreader != null) {
parseMTL(parent, mreader, materials, mtlTable);
}
}
} else if (elements[0].equals("g")) {
gname = 1 < elements.length ? elements[1] : "";
} else if (elements[0].equals("usemtl")) {
// Getting index of current active material (will be applied on all subsequent faces).
if (elements[1] != null) {
String mtlname = elements[1];
if (mtlTable.containsKey(mtlname)) {
Integer tempInt = mtlTable.get(mtlname);
mtlIdxCur = tempInt.intValue();
} else {
mtlIdxCur = -1;
}
}
} else if (elements[0].equals("f")) {
// Face setting
OBJFace face = new OBJFace();
face.matIdx = mtlIdxCur;
face.name = gname;
for (int i = 1; i < elements.length; i++) {
String seg = elements[i];
if (seg.indexOf("/") > 0) {
String[] forder = seg.split("/");
if (forder.length > 2) {
// Getting vertex and texture and normal indexes.
if (forder[0].length() > 0 && readv) {
face.vertIdx.add(Integer.valueOf(forder[0]));
}
if (forder[1].length() > 0 && readvt) {
face.texIdx.add(Integer.valueOf(forder[1]));
}
if (forder[2].length() > 0 && readvn) {
face.normIdx.add(Integer.valueOf(forder[2]));
}
} else if (forder.length > 1) {
// Getting vertex and texture/normal indexes.
if (forder[0].length() > 0 && readv) {
face.vertIdx.add(Integer.valueOf(forder[0]));
}
if (forder[1].length() > 0) {
if (readvt) {
face.texIdx.add(Integer.valueOf(forder[1]));
} else if (readvn) {
face.normIdx.add(Integer.valueOf(forder[1]));
}
}
} else if (forder.length > 0) {
// Getting vertex index only.
if (forder[0].length() > 0 && readv) {
face.vertIdx.add(Integer.valueOf(forder[0]));
}
}
} else {
// Getting vertex index only.
if (seg.length() > 0 && readv) {
face.vertIdx.add(Integer.valueOf(seg));
}
}
}
faces.add(face);
}
}
}
if (materials.size() == 0) {
// No materials definition so far. Adding one default material.
OBJMaterial defMtl = new OBJMaterial();
materials.add(defMtl);
}
} catch (Exception e) {
e.printStackTrace();
}
}
static protected void parseMTL(PApplet parent,
BufferedReader reader, ArrayList<OBJMaterial> materials,
Hashtable<String, Integer> materialsHash) {
try {
String line;
OBJMaterial currentMtl = null;
while ((line = reader.readLine()) != null) {
// Parse the line
line = line.trim();
String elements[] = line.split("\\s+");
if (elements.length > 0) {
// Extract the material data.
if (elements[0].equals("newmtl")) {
// Starting new material.
String mtlname = elements[1];
currentMtl = new OBJMaterial(mtlname);
materialsHash.put(mtlname, new Integer(materials.size()));
materials.add(currentMtl);
} else if (elements[0].equals("map_Kd") && elements.length > 1) {
// Loading texture map.
String texname = elements[1];
currentMtl.kdMap = parent.loadImage(texname);
} else if (elements[0].equals("Ka") && elements.length > 3) {
// The ambient color of the material
currentMtl.ka.x = Float.valueOf(elements[1]).floatValue();
currentMtl.ka.y = Float.valueOf(elements[2]).floatValue();
currentMtl.ka.z = Float.valueOf(elements[3]).floatValue();
} else if (elements[0].equals("Kd") && elements.length > 3) {
// The diffuse color of the material
currentMtl.kd.x = Float.valueOf(elements[1]).floatValue();
currentMtl.kd.y = Float.valueOf(elements[2]).floatValue();
currentMtl.kd.z = Float.valueOf(elements[3]).floatValue();
} else if (elements[0].equals("Ks") && elements.length > 3) {
// The specular color weighted by the specular coefficient
currentMtl.ks.x = Float.valueOf(elements[1]).floatValue();
currentMtl.ks.y = Float.valueOf(elements[2]).floatValue();
currentMtl.ks.z = Float.valueOf(elements[3]).floatValue();
} else if ((elements[0].equals("d") || elements[0].equals("Tr")) && elements.length > 1) {
// Reading the alpha transparency.
currentMtl.d = Float.valueOf(elements[1]).floatValue();
} else if (elements[0].equals("Ns") && elements.length > 1) {
// The specular component of the Phong shading model
currentMtl.ns = Float.valueOf(elements[1]).floatValue();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Stores a face from an OBJ file
static protected class OBJFace {
ArrayList<Integer> vertIdx;
ArrayList<Integer> texIdx;
ArrayList<Integer> normIdx;
int matIdx;
String name;
OBJFace() {
vertIdx = new ArrayList<Integer>();
texIdx = new ArrayList<Integer>();
normIdx = new ArrayList<Integer>();
matIdx = -1;
name = "";
}
}
// Stores a material defined in an MTL file.
static protected class OBJMaterial {
String name;
PVector ka;
PVector kd;
PVector ks;
float d;
float ns;
PImage kdMap;
OBJMaterial() {
this("default");
}
OBJMaterial(String name) {
this.name = name;
ka = new PVector(0.5f, 0.5f, 0.5f);
kd = new PVector(0.5f, 0.5f, 0.5f);
ks = new PVector(0.5f, 0.5f, 0.5f);
d = 1.0f;
ns = 0.0f;
kdMap = null;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -3,12 +3,12 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2011 Andres Colubri
Copyright (c) 2010 Ben Fry and Casey Reas
Copyright (c) 2011-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,17 +19,14 @@
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
*/
package processing.opengl;
import processing.core.*;
import java.io.IOException;
import java.net.URL;
import processing.core.PApplet;
import processing.core.PGraphics;
/**
* This class encapsulates a GLSL shader program, including a vertex
* and a fragment shader. Originally based in the code by JohnG
@@ -54,6 +51,8 @@ public class PShader {
protected int vertexShader;
protected int fragmentShader;
protected boolean active;
public PShader() {
parent = null;
pg = null;
@@ -67,7 +66,9 @@ public class PShader {
programObject = 0;
vertexShader = 0;
fragmentShader = 0;
fragmentShader = 0;
active = false;
}
public PShader(PApplet parent) {
@@ -154,6 +155,7 @@ public class PShader {
public void start() {
init();
pgl.glUseProgram(programObject);
active = true;
}
@@ -162,9 +164,18 @@ public class PShader {
*/
public void stop() {
pgl.glUseProgram(0);
active = false;
}
/**
* Returns true if the shader is running, false otherwise.
*/
public boolean active() {
return active;
}
/**
* Returns the ID location of the attribute parameter given its name.
*
@@ -0,0 +1,171 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.opengl;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PShape;
public class PShape2D extends PShapeOpenGL {
public PShape2D(PApplet parent, int family) {
super(parent, family);
}
public boolean is2D() {
return true;
}
public boolean is3D() {
return false;
}
////////////////////////////////////////////////////////////////////////
//
// Shape copy
static public PShape2D createShape(PApplet parent, PShape src) {
PShape2D dest = null;
if (src.getFamily() == GROUP) {
dest = PGraphics2D.createShapeImpl(parent, GROUP);
PShape2D.copyGroup(parent, src, dest);
} else if (src.getFamily() == PRIMITIVE) {
dest = PGraphics2D.createShapeImpl(parent, src.getKind(), src.getParams());
PShape.copyPrimitive(src, dest);
} else if (src.getFamily() == GEOMETRY) {
dest = PGraphics2D.createShapeImpl(parent, src.getKind());
PShape.copyGeometry(src, dest);
} else if (src.getFamily() == PATH) {
dest = PGraphics2D.createShapeImpl(parent, PATH);
PShape.copyPath(src, dest);
}
dest.setName(src.getName());
return dest;
}
static public void copyGroup(PApplet parent, PShape src, PShape dest) {
copyMatrix(src, dest);
copyStyles(src, dest);
copyImage(src, dest);
for (int i = 0; i < src.getChildCount(); i++) {
PShape c = PShape2D.createShape(parent, src.getChild(i));
dest.addChild(c);
}
}
///////////////////////////////////////////////////////////
//
// Drawing methods
public void vertex(float x, float y, float z) {
PGraphics.showDepthWarningXYZ("vertex");
}
public void vertex(float x, float y, float z, float u, float v) {
PGraphics.showDepthWarningXYZ("vertex");
}
///////////////////////////////////////////////////////////
//
// Bezier curves
public void bezierVertex(float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4) {
PGraphics.showDepthWarningXYZ("bezierVertex");
}
public void quadraticVertex(float x2, float y2, float z2,
float x4, float y4, float z4) {
PGraphics.showDepthWarningXYZ("quadVertex");
}
public void curveVertex(float x, float y, float z) {
PGraphics.showDepthWarningXYZ("curveVertex");
}
///////////////////////////////////////////////////////////
//
// Geometric transformations
public void translate(float tx, float ty, float tz) {
PGraphics.showVariationWarning("translate");
}
public void rotateX(float angle) {
PGraphics.showDepthWarning("rotateX");
}
public void rotateY(float angle) {
PGraphics.showDepthWarning("rotateY");
}
public void rotateZ(float angle) {
PGraphics.showDepthWarning("rotateZ");
}
public void rotate(float angle, float vx, float vy, float vz) {
PGraphics.showVariationWarning("rotate");
}
public void applyMatrix(float n00, float n01, float n02, float n03,
float n10, float n11, float n12, float n13,
float n20, float n21, float n22, float n23,
float n30, float n31, float n32, float n33) {
PGraphics.showVariationWarning("applyMatrix");
}
public void scale(float sx, float sy, float sz) {
PGraphics.showDepthWarningXYZ("scale");
}
///////////////////////////////////////////////////////////
//
// Setters/getters of individual vertices
public float getVertexZ(int index) {
PGraphics.showDepthWarningXYZ("getVertexZ");
return 0;
}
public void setVertex(int index, float x, float y) {
super.setVertex(index, x, y, 0);
}
public void setVertex(int index, float x, float y, float z) {
PGraphics.showDepthWarningXYZ("setVertex");
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+132 -62
View File
@@ -3,11 +3,12 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2010 Ben Fry and Casey Reas
Copyright (c) 2011-12 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -22,18 +23,16 @@
package processing.opengl;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
import processing.core.PImage;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
import processing.core.PImage;
/**
* This class wraps an OpenGL texture.
* By Andres Colubri
@@ -59,8 +58,8 @@ public class PTexture implements PConstants {
public int glHeight;
protected boolean usingMipmaps;
protected float maxTexCoordU;
protected float maxTexCoordV;
protected float maxTexcoordU;
protected float maxTexcoordV;
protected boolean flippedX;
protected boolean flippedY;
@@ -250,17 +249,72 @@ public class PTexture implements PConstants {
pgl.glBindTexture(glTarget, glID);
if (usingMipmaps) {
if (PGraphicsOpenGL.mipmapGeneration) {
if (PGraphicsOpenGL.autoMipmapGenSupported) {
// Automatic mipmap generation.
int[] rgbaPixels = new int[w * h];
convertToRGBA(pixels, rgbaPixels, format, w, h);
setTexels(rgbaPixels, x, y, w, h);
pgl.glGenerateMipmap(glTarget);
rgbaPixels = null;
} else {
// TODO: Manual mipmap generation.
// Open source implementation of gluBuild2DMipmaps here:
// http://code.google.com/p/glues/source/browse/trunk/glues/source/glues_mipmap.c
} else {
// TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage,
// making it work in npot textures (embed npot tex into larger pot tex?), subregions,
// and moving GLUtils.texImage2D (originally from Android SDK) into PGL.
// Actually, this whole code should go into PGL, so the Android implementation can
// use Bitmap, and desktop use BufferedImage.
/*
if (w != width || h != height) {
System.err.println("Sorry but I don't know how to generate mipmaps for a subregion.");
return;
}
// Code by Mike Miller obtained from here:
// http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/
int w0 = glWidth;
int h0 = glHeight;
int[] argbPixels = new int[w0 * h0];
convertToARGB(pixels, argbPixels, format);
int level = 0;
int denom = 1;
// We create a Bitmap because then we use its built-in filtered downsampling
// functionality.
Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888);
bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0);
while (w0 >= 1 || h0 >= 1) {
//First of all, generate the texture from our bitmap and set it to the according level
GLUtils.texImage2D(glTarget, level, bitmap, 0);
// We are done.
if (w0 == 1 && h0 == 1) {
break;
}
// Increase the mipmap level
level++;
denom *= 2;
// Downsampling bitmap. We must eventually arrive to the 1x1 level,
// and if the width and height are different, there will be a few 1D
// texture levels just before.
// This update formula also allows for NPOT resolutions.
w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom));
h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom));
// (see getScaledInstance in AWT Image)
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true);
// Clean up
bitmap.recycle();
bitmap = bitmap2;
}
*/
int[] rgbaPixels = new int[w * h];
convertToRGBA(pixels, rgbaPixels, format, w, h);
setTexels(rgbaPixels, x, y, w, h);
rgbaPixels = null;
}
} else {
int[] rgbaPixels = new int[w * h];
@@ -347,8 +401,8 @@ public class PTexture implements PConstants {
* Returns the maximum possible value for the texture coordinate U (horizontal).
* @return float
*/
public float getMaxTexCoordU() {
return maxTexCoordU;
public float getMaxU() {
return maxTexcoordU;
}
@@ -356,8 +410,8 @@ public class PTexture implements PConstants {
* Returns the maximum possible value for the texture coordinate V (vertical).
* @return float
*/
public float getMaxTexCoordV() {
return maxTexCoordV;
public float getMaxV() {
return maxTexcoordV;
}
@@ -743,8 +797,8 @@ public class PTexture implements PConstants {
// is non-power-of-two, then glWidth (glHeight) will be greater than w (h) because it
// is chosen to be the next power of two, and this quotient will give the appropriate
// maximum texture coordinate value given this situation.
maxTexCoordU = (float)width / glWidth;
maxTexCoordV = (float)height / glHeight;
maxTexcoordU = (float)width / glWidth;
maxTexcoordV = (float)height / glHeight;
}
@@ -878,8 +932,8 @@ public class PTexture implements PConstants {
glHeight = src.glHeight;
usingMipmaps = src.usingMipmaps;
maxTexCoordU = src.maxTexCoordU;
maxTexCoordV = src.maxTexCoordV;
maxTexcoordU = src.maxTexcoordU;
maxTexcoordV = src.maxTexcoordV;
flippedX = src.flippedX;
flippedY = src.flippedY;
@@ -905,14 +959,26 @@ public class PTexture implements PConstants {
res.format = ALPHA;
}
if (glMinFilter == PGL.GL_NEAREST) {
if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_NEAREST) {
res.sampling = POINT;
} else if (glMinFilter == PGL.GL_LINEAR) {
res.mipmaps = false;
} else if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_LINEAR) {
res.sampling = LINEAR;
res.mipmaps = false;
} else if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST) {
res.sampling = LINEAR;
res.mipmaps = true;
} else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR) {
res.sampling = BILINEAR;
} else if (glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR) {
res.mipmaps = false;
} else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST) {
res.sampling = BILINEAR;
res.mipmaps = true;
} else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR) {
res.sampling = TRILINEAR;
res.mipmaps = true;
}
if (glWrapS == PGL.GL_CLAMP_TO_EDGE) {
res.wrapU = CLAMP;
} else if (glWrapS == PGL.GL_REPEAT) {
@@ -938,7 +1004,7 @@ public class PTexture implements PConstants {
if (params.target == TEXTURE2D) {
glTarget = PGL.GL_TEXTURE_2D;
} else {
throw new RuntimeException("OPENGL2: Unknown texture target");
throw new RuntimeException("Unknown texture target");
}
if (params.format == RGB) {
@@ -948,20 +1014,23 @@ public class PTexture implements PConstants {
} else if (params.format == ALPHA) {
glFormat = PGL.GL_ALPHA;
} else {
throw new RuntimeException("OPENGL2: Unknown texture format");
throw new RuntimeException("Unknown texture format");
}
if (params.sampling == POINT) {
glMagFilter = PGL.GL_NEAREST;
glMinFilter = PGL.GL_NEAREST;
} else if (params.sampling == LINEAR) {
glMagFilter = PGL.GL_NEAREST;
glMinFilter = params.mipmaps ? PGL.GL_LINEAR_MIPMAP_NEAREST : PGL.GL_LINEAR;
} else if (params.sampling == BILINEAR) {
glMagFilter = PGL.GL_LINEAR;
glMinFilter = PGL.GL_LINEAR;
glMinFilter = params.mipmaps ? PGL.GL_LINEAR_MIPMAP_NEAREST : PGL.GL_LINEAR;
} else if (params.sampling == TRILINEAR) {
glMagFilter = PGL.GL_LINEAR;
glMinFilter = PGL.GL_LINEAR_MIPMAP_LINEAR;
glMinFilter = PGL.GL_LINEAR_MIPMAP_LINEAR;
} else {
throw new RuntimeException("OPENGL2: Unknown texture filtering mode");
throw new RuntimeException("Unknown texture filtering mode");
}
if (params.wrapU == CLAMP) {
@@ -969,7 +1038,7 @@ public class PTexture implements PConstants {
} else if (params.wrapU == REPEAT) {
glWrapS = PGL.GL_REPEAT;
} else {
throw new RuntimeException("OPENGL2: Unknown wrapping mode");
throw new RuntimeException("Unknown wrapping mode");
}
if (params.wrapV == CLAMP) {
@@ -977,10 +1046,11 @@ public class PTexture implements PConstants {
} else if (params.wrapV == REPEAT) {
glWrapT = PGL.GL_REPEAT;
} else {
throw new RuntimeException("OPENGL2: Unknown wrapping mode");
throw new RuntimeException("Unknown wrapping mode");
}
usingMipmaps = glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR;
usingMipmaps = glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST ||
glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR;
flippedX = false;
flippedY = false;
@@ -990,27 +1060,7 @@ public class PTexture implements PConstants {
///////////////////////////////////////////////////////////////////////////
// Parameters object
static public Parameters newParameters() {
return new Parameters();
}
static public Parameters newParameters(int format) {
return new Parameters(format);
}
static public Parameters newParameters(int format, int sampling) {
return new Parameters(format, sampling);
}
static public Parameters newParameters(Parameters params) {
return new Parameters(params);
}
/**
* This class stores the parameters for a texture: target, internal format, minimization filter
@@ -1028,10 +1078,15 @@ public class PTexture implements PConstants {
public int format;
/**
* Texture filtering (POINT, BILINEAR or TRILINEAR).
* Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR).
*/
public int sampling;
/**
* Use mipmaps or not.
*/
public boolean mipmaps;
/**
* Wrapping mode along U.
*/
@@ -1043,12 +1098,13 @@ public class PTexture implements PConstants {
public int wrapV;
/**
* Creates an instance of GLTextureParameters, setting all the parameters to default values.
* Sets all the parameters to default values.
*/
public Parameters() {
this.target = TEXTURE2D;
this.format = ARGB;
this.sampling = BILINEAR;
this.mipmaps = true;
this.wrapU = CLAMP;
this.wrapV = CLAMP;
}
@@ -1057,6 +1113,7 @@ public class PTexture implements PConstants {
this.target = TEXTURE2D;
this.format = format;
this.sampling = BILINEAR;
this.mipmaps = true;
this.wrapU = CLAMP;
this.wrapV = CLAMP;
}
@@ -1065,16 +1122,22 @@ public class PTexture implements PConstants {
this.target = TEXTURE2D;
this.format = format;
this.sampling = sampling;
this.mipmaps = true;
this.wrapU = CLAMP;
this.wrapV = CLAMP;
}
public Parameters(int format, int sampling, boolean mipmaps) {
this.target = TEXTURE2D;
this.format = format;
this.sampling = sampling;
this.mipmaps = mipmaps;
this.wrapU = CLAMP;
this.wrapV = CLAMP;
}
public Parameters(Parameters src) {
this.target = src.target;
this.format = src.format;
this.sampling = src.sampling;
this.wrapU = src.wrapU;
this.wrapV = src.wrapV;
set(src);
}
public void set(int format) {
@@ -1086,10 +1149,17 @@ public class PTexture implements PConstants {
this.sampling = sampling;
}
public void set(int format, int sampling, boolean mipmaps) {
this.format = format;
this.sampling = sampling;
this.mipmaps = mipmaps;
}
public void set(Parameters src) {
this.target = src.target;
this.format = src.format;
this.sampling = src.sampling;
this.mipmaps = src.mipmaps;
this.wrapU = src.wrapU;
this.wrapV = src.wrapV;
}
@@ -1,85 +0,0 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package processing.opengl.geom;
/**
* The <code>LineSink</code> interface accepts a series of line
* drawing commands: <code>moveTo</code>, <code>lineTo</code>,
* <code>close</code> (equivalent to a <code>lineTo</code> command
* with an argument equal to the argument of the last
* <code>moveTo</code> command), and <code>end</code>.
*
*/
public abstract class LineSink {
/**
* Moves the current drawing position to the point <code>(x0,
* y0)</code>.
*
* @param x0 the X coordinate in S15.16 format
* @param y0 the Y coordinate in S15.16 format
*/
public abstract void moveTo(int x0, int y0);
/**
* Provides a hint that the current segment should be joined to
* the following segment using an explicit miter or round join if
* required.
*
* <p> An application-generated path will generally have no need
* to contain calls to this method; they are typically introduced
* by a <code>Flattener</code> to mark segment divisions that
* appear in its input, and consumed by a <code>Stroker</code>
* that is responsible for emitting the miter or round join
* segments.
*
* <p> Other <code>LineSink</code> classes should simply pass this
* hint to their output sink as needed.
*/
public abstract void lineJoin();
/**
* Draws a line from the current drawing position to the point
* <code>(x1, y1)</code> and sets the current drawing position to
* <code>(x1, y1)</code>.
*
* @param x1 the X coordinate in S15.16 format
* @param y1 the Y coordinate in S15.16 format
*/
public abstract void lineTo(int x1, int y1);
/**
* Closes the current path by drawing a line from the current
* drawing position to the point specified by the moset recent
* <code>moveTo</code> command.
*/
public abstract void close();
/**
* Ends the current path. It may be necessary to end a path in
* order to allow end caps to be drawn.
*/
public abstract void end();
}