/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* PGraphicsGL - opengl version of the graphics engine Part of the Processing project - http://processing.org Copyright (c) 2004-05 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; 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. 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.*; import net.java.games.jogl.*; public class PGraphicsGL extends PGraphics3 { public GL gl; public GLU glu; public GLCanvas canvas; /** * true if the host system is big endian (mac, irix, sun), * false if little endian (intel). */ static public boolean BIG_ENDIAN = System.getProperty("sun.cpu.endian").equals("big"); /** * Create a new PGraphicsGL at the specified size. * @param applet the host applet, cannot be null */ public PGraphicsGL(int width, int height, PApplet applet) { //System.out.println("creating PGraphicsGL"); if (applet == null) { throw new RuntimeException("The applet passed to PGraphicsGL " + "cannot be null."); } //System.out.println("creating PGraphicsGL 2"); GLCapabilities capabilities = new GLCapabilities(); canvas = GLDrawableFactory.getFactory().createGLCanvas(capabilities); //System.out.println("creating PGraphicsGL 3"); final PApplet parent = applet; canvas.addGLEventListener(new GLEventListener() { public void display(GLDrawable drawable) { parent.display(); // this means it's time to go } public void init(GLDrawable drawable) { } public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) { } public void reshape(GLDrawable drawable, int x, int y, int width, int height) { } }); //System.out.println("creating PGraphicsGL 4"); applet.setLayout(null); applet.add(canvas); canvas.setBounds(0, 0, width, height); //System.out.println("creating PGraphicsGL 5"); canvas.addMouseListener(applet); canvas.addMouseMotionListener(applet); canvas.addKeyListener(applet); canvas.addFocusListener(applet); //System.out.println("creating PGraphicsGL 6"); // need to get proper opengl context since will be needed below gl = canvas.getGL(); glu = canvas.getGLU(); //System.out.println("creating PGraphicsGL 7"); // this sets width/height and calls allocate() in PGraphics resize(width, height); //defaults(); // call this just before setup instead //System.out.println("done creating gl"); } protected boolean displayed = false; // main applet thread requests an update, // but PGraphics has to respond back by calling PApplet.display() public void requestDisplay(PApplet parent) { //System.out.println("requesting display"); if (!displayed) { // these two method calls (and explanations) were taken from // the FpsAnimator implementation from the jogl hoo-ha // Try to get OpenGL context optimization since we know we // will be rendering this one drawable continually from // this thread; make the context current once instead of // making it current and freeing it each frame. canvas.setRenderingThread(Thread.currentThread()); //System.out.println(Thread.currentThread()); // Since setRenderingThread is currently advisory (because // of the poor JAWT implementation in the Motif AWT, which // performs excessive locking) we also prevent repaint(), // which is called from the AWT thread, from having an // effect for better multithreading behavior. This call is // not strictly necessary, but if end users write their // own animation loops which update multiple drawables per // tick then it may be necessary to enforce the order of // updates. canvas.setNoAutoRedrawMode(true); // maybe this will help? //canvas.requestFocus(); // done with this business displayed = true; } // request a display from the gl canvas. when it happens, // we'll hear about it from the GLEventListener, which will // in turn call PApplet.display()... hold your breath... try { canvas.display(); } catch (Exception e) { //} catch (InterruptedException e) { e.printStackTrace(); } } /* // this was additional stuff used when the animator // thread was being shut down.. how to handle this.. drawable.setNoAutoRedrawMode(false); try { // The surface is already unlocked and rendering // thread is already null if an exception occurred // during display(), so don't disable the rendering // thread again. if (noException) { drawable.setRenderingThread(null); } } finally { synchronized (PAppletThreadGL.this) { thread = null; PAppletThreadGL.this.notify(); } } */ /** * Called by resize(), but nothing to allocate for an OpenGL canvas. */ protected void allocate() { // nothing to do here just yet // normally this allocates the pixel buffer, etc. } // public void defaults() { } // this sets up the positions of the two base lights // not sure if this needs to be enabled in opengl private void syncMatrices() { gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadMatrixf(new float[] { projection.m00, projection.m10, projection.m20, projection.m30, projection.m01, projection.m11, projection.m21, projection.m31, projection.m02, projection.m12, projection.m22, projection.m32, projection.m03, projection.m13, projection.m23, projection.m33 }); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); gl.glScalef(1, -1, 1); } public void clearLights() { super.clearLights(); for (int i = 0; i < MAX_LIGHTS; i++) { lightDisable(i); } } public void beginFrame() { super.beginFrame(); syncMatrices(); report("top beginFrame()"); // these are necessary for alpha (i.e. fonts) to work gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); // this is necessary for 3D drawing gl.glEnable(GL.GL_DEPTH_TEST); // use <= since that's what processing.core does gl.glDepthFunc(GL.GL_LEQUAL); // I never really got the hang of lighting in OpenGL ... // I've done something like [the following] which at least // demonstrates that it works... --tom carden // because y is flipped gl.glFrontFace(GL.GL_CW); // coloured stuff gl.glEnable(GL.GL_COLOR_MATERIAL); gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE); gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR); // these tend to make life easier // (but sometimes at the expense of a little speed) // Not using them right now because we're doing our own lighting. //gl.glEnable(GL.GL_NORMALIZE); //gl.glEnable(GL.GL_AUTO_NORMAL); // I think this is OpenGL 1.2 only //gl.glEnable(GL.GL_RESCALE_NORMAL); //gl.GlLightModeli(GL.GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); report("bot beginFrame()"); // are there other things to do here? //System.out.println("beginFrame() stop error " + PApplet.hex(gl.glGetError())); } public void endFrame() { //System.out.println("endFrame() error " + PApplet.hex(gl.glGetError())); report("top endFrame()"); if (hints[DEPTH_SORT]) { if (triangleCount > 0) { depth_sort_triangles(); render_triangles(); } if (lineCount > 0) { depth_sort_lines(); render_lines(); } } //gl.glPopMatrix(); report("bot endFrame()"); } // For now we do our own lighting (so sum the specular and diffuse light colors...) protected void handle_lighting() { super.handle_lighting(); for (int i = vertex_start; i < vertex_end; i++) { float v[] = vertices[i]; v[R] = min(ONE, v[R] + v[SPR]); v[G] = min(ONE, v[G] + v[SPG]); v[B] = min(ONE, v[B] + v[SPB]); } } private final float min(float a, float b) { return (a < b) ? a : b; } protected void render_triangles() { //public void render_triangles() { report("into triangles"); //System.out.println("into triangles error " + PApplet.hex(gl.glGetError())); //System.out.println("rendering " + triangleCount + " triangles"); for (int i = 0; i < triangleCount; i ++) { //System.out.println(" rendering triangle " + i); float a[] = vertices[triangles[i][VERTEX1]]; float b[] = vertices[triangles[i][VERTEX2]]; float c[] = vertices[triangles[i][VERTEX3]]; int textureIndex = triangles[i][TEXTURE_INDEX]; if (textureIndex != -1) { //System.out.println("texture drawing"); PImage texture = textures[textureIndex]; report("before enable"); //gl.glEnable(GL.GL_TEXTURE_2D); report("after enable"); ImageCache cash = (ImageCache) texture.cache; // as in johnny if (cash == null) { // make sure this thing is cached and a power of 2 //if (texture.cache == null) { cache(texture); cash = (ImageCache) texture.cache; // mark for update (nope, already updated) //texture.updatePixels(0, 0, texture.width, texture.height); //texture.modified = true; } else if (texture.modified) { // TODO make this more efficient and just update a sub-part // based on mx1 et al, also use gl function to update // only a sub-portion of the image. //cash.update(texture.pixels, texture.width, texture.height); cash.update(texture); gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 4, cash.twidth, cash.theight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, //0, GL.GL_BGRA_EXT, GL.GL_UNSIGNED_BYTE, cash.tpixels); report("re-binding " + cash.twidth + " " + cash.theight + " " + cash.tpixels); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); report("re-binding 3"); gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); // actually bind this feller texture.modified = false; } report("before bind"); //System.out.println(gl.glIsTexture(image.tindex)); //GL_PERSPECTIVE_CORRECTION_HINT to GL_NICEST // and running the example again. To do this, use glHint(). // these don't seem to do much //gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); //gl.glPixelStorei(GL.GL_UNPACK_SWAP_BYTES, 1); int tindex = ((ImageCache) texture.cache).tindex; gl.glBindTexture(GL.GL_TEXTURE_2D, tindex); //if (image.format == ALPHA) { //System.out.println("binding with replace"); // gl.glTexEnvf(GL.GL_TEXTURE_ENV, // GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); //} else { //gl.glTexEnvf(GL.GL_TEXTURE_ENV, // GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); //} report("after bind"); /* } else { cache(texture); cash = (ImageCache) texture.cache; report("non-binding 0"); // may be needed for windows report("non-binding 1"); gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 4, cash.twidth, cash.theight, 0, GL.GL_BGRA_EXT, GL.GL_UNSIGNED_BYTE, cash.tpixels); report("non-binding 2 " + cash.twidth + " " + cash.theight + " " + cash.tpixels); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); report("non-binding 3"); gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); } */ float uscale = (float) texture.width / (float) cash.twidth; float vscale = (float) texture.height / (float) cash.theight; gl.glEnable(GL.GL_TEXTURE_2D); gl.glBegin(GL.GL_TRIANGLES); gl.glColor4f(a[R], a[G], a[B], a[A]); gl.glTexCoord2f(a[U] * uscale, a[V] * vscale); gl.glNormal3f(a[NX], a[NY], a[NZ]); gl.glVertex3f(a[VX], a[VY], a[VZ]); gl.glColor4f(b[R], b[G], b[B], b[A]); gl.glTexCoord2f(b[U] * uscale, b[V] * vscale); gl.glNormal3f(b[NX], b[NY], b[NZ]); gl.glVertex3f(b[VX], b[VY], b[VZ]); gl.glColor4f(c[R], c[G], c[B], c[A]); gl.glTexCoord2f(c[U] * uscale, c[V] * vscale); gl.glNormal3f(c[NX], c[NY], c[NZ]); gl.glVertex3f(c[VX], c[VY], c[VZ]); gl.glEnd(); report("non-binding 6"); gl.glDisable(GL.GL_TEXTURE_2D); } else { gl.glBegin(GL.GL_TRIANGLES); gl.glColor4f(a[R], a[G], a[B], a[A]); gl.glNormal3f(a[NX], a[NY], a[NZ]); gl.glVertex3f(a[VX], a[VY], a[VZ]); gl.glColor4f(b[R], b[G], b[B], b[A]); gl.glNormal3f(b[NX], b[NY], b[NZ]); gl.glVertex3f(b[VX], b[VY], b[VZ]); gl.glColor4f(c[R], c[G], c[B], c[A]); gl.glNormal3f(c[NX], c[NY], c[NZ]); gl.glVertex3f(c[VX], c[VY], c[VZ]); gl.glEnd(); } } report("out of triangles"); } public void render_lines() { //System.out.println("into lines error " + PApplet.hex(gl.glGetError())); int i = 0; for (int j = 0; j < pathCount; j++) { //report("render_lines 1"); // glLineWidth has to occur outside glBegin/glEnd gl.glLineWidth(lines[i][STROKE_WEIGHT]); //report("render_lines 2 " + lines[i][STROKE_WEIGHT]); gl.glBegin(GL.GL_LINE_STRIP); // always draw a first point float a[] = vertices[lines[i][VERTEX1]]; gl.glColor4f(a[SR], a[SG], a[SB], a[SA]); gl.glVertex3f(a[VX], a[VY], a[VZ]); //System.out.println("First point: " + a[VX] +", "+ a[VY] +", "+ a[VZ]); // on this and subsequent lines, only draw the second point for (int k = 0; k < pathLength[j]; k++) { float b[] = vertices[lines[i][VERTEX2]]; gl.glColor4f(b[SR], b[SG], b[SB], b[SA]); gl.glVertex3f(b[VX], b[VY], b[VZ]); i++; } gl.glEnd(); //report("render_lines 3 " + pathLength[j]); } //System.out.println("outta lines error " + PApplet.hex(gl.glGetError())); } /** * Handled entirely by OpenGL, so use this to override the superclass. */ protected void light_and_transform() { } ////////////////////////////////////////////////////////////// /** * Cache an image using a specified glTexName * (name is just an integer index). * * If a cacheIndex is already assigned in the image, * this request will be ignored. */ protected void cache(PImage image) { if (image.cache != null) return; int names[] = new int[1]; gl.glGenTextures(1, names); int index = names[0]; //if (image.tindex != -1) return; // use glGetIntegerv with the argument GL_MAX_TEXTURE_SIZE // to figure out min/max texture sizes //report("into cache"); //image.modified(); image.cache = new ImageCache(); ImageCache cash = (ImageCache) image.cache; //cash.update(image.pixels, image.width, image.height); cash.update(image); // first-time binding of entire texture gl.glEnable(GL.GL_TEXTURE_2D); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); //gl.glPixelStorei(GL.GL_UNPACK_SWAP_BYTES, 0); gl.glBindTexture(GL.GL_TEXTURE_2D, index); gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, 4, cash.twidth, cash.theight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, cash.tpixels); gl.glTexParameterf(GL.GL_TEXTURE_2D, //GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); gl.glTexParameterf(GL.GL_TEXTURE_2D, //GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); /* int err = glu.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, 4, image.width, image.height, GL.GL_RGBA, //GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE, image.pixels); //System.out.println("mipmap: " + err); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST_MIPMAP_NEAREST); gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST_MIPMAP_LINEAR); */ //gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP); //gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP); gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); //gl.glDisable(GL.GL_TEXTURE_2D); cash.tindex = index; } class ImageCache { int tindex; int tpixels[]; int twidth, theight; //public void update(int pixels[], int width, int height) { public void update(PImage source) { tindex = -1; // bit shifting this might be more efficient int width2 = (int) Math.pow(2, Math.ceil(Math.log(source.width) / Math.log(2))); int height2 = (int) Math.pow(2, Math.ceil(Math.log(source.height) / Math.log(2))); if ((width2 > twidth) || (height2 > theight)) { // either twidth/theight are zero, or size has changed tpixels = null; } if (tpixels == null) { twidth = width2; theight = height2; tpixels = new int[twidth * theight]; } // copy image data into the texture int p = 0; int t = 0; //if (System.getProperty("sun.cpu.endian").equals("big")) { if (BIG_ENDIAN) { switch (source.format) { case ALPHA: for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { tpixels[t++] = 0xFFFFFF00 | source.pixels[p++]; } t += twidth - source.width; } break; case RGB: //System.out.println("swapping RGB"); for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { int pixel = source.pixels[p++]; tpixels[t++] = (pixel << 8) | 0xff; //tpixels[t++] = pixel; // nice effect, actually } t += twidth - source.width; } break; case ARGB: //System.out.println("gonna swap ARGB"); for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { int pixel = source.pixels[p++]; tpixels[t++] = (pixel << 8) | ((pixel >> 24) & 0xff); } t += twidth - source.width; } break; } } else { // ARGB native, and RGBA opengl means ABGR on windows // for the most part just need to swap two components here // the sun.cpu.endian here might be "false", oddly enough.. // (that's why just using an "else", rather than check for "little") switch (source.format) { case ALPHA: for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { tpixels[t++] = (source.pixels[p++] << 24) | 0x00FFFFFF; } t += twidth - source.width; } break; case RGB: for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { int pixel = source.pixels[p++]; // needs to be ABGR, stored in memory xRGB // so R and B must be swapped, and the x just made FF tpixels[t++] = 0xff000000 | // force opacity for good measure ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | (pixel & 0x0000FF00); } t += twidth - source.width; } break; case ARGB: for (int y = 0; y < source.height; y++) { for (int x = 0; x < source.width; x++) { int pixel = source.pixels[p++]; // needs to be ABGR stored in memory ARGB // so R and B must be swapped, A and G just brought back in tpixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) | (pixel & 0xFF00FF00); } t += twidth - source.width; } break; } } } } ////////////////////////////////////////////////////////////// public void textMode(int mode) { // TODO get this guy straightened out if (mode != OBJECT) { throw new RuntimeException("only textMode(OBJECT) is " + "currently supported for OpenGL"); } super.textMode(mode); } public void cameraMode(int mode) { super.cameraMode(mode); syncMatrices(); } ////////////////////////////////////////////////////////////// /* public void endCamera() { //System.out.println("PGraphicsGL.endCamera() 1"); super.endCamera(); System.out.println("begin endCamera"); //System.out.println("PGraphicsGL.endCamera() " + width + " " + height); //System.exit(0); //System.out.println("into camera error " + PApplet.hex(gl.glGetError())); gl.glMatrixMode(GL.GL_PROJECTION); //gl.glLoadIdentity(); //System.out.println("camera should be"); //printCamera(); // opengl matrices are rotated from processing's gl.glLoadMatrixf(new float[] { projection.m00, projection.m10, projection.m20, projection.m30, projection.m01, projection.m11, projection.m21, projection.m31, projection.m02, projection.m12, projection.m22, projection.m32, projection.m03, projection.m13, projection.m23, projection.m33 } ); //gl.glScalef(1, -1, 1); //System.out.println("trying " + height); // this needs to be done since the model matrix will be // goofy since it's mid-frame and the translate/scale will be wrong gl.glMatrixMode(GL.GL_MODELVIEW); gl.glLoadIdentity(); // gl coordinates are reversed gl.glTranslatef(0, height, 0); gl.glScalef(1, -1, 1); report("out of endCamera"); } */ ////////////////////////////////////////////////////////////// protected int internalCreateAmbientLight(float lr, float lg, float lb) { int num = super.internalCreateAmbientLight(lr, lg, lb); lightEnable(num); glLightAmbient(num); glLightPosition(num); glLightFalloff(num); return num; } protected int internalCreateDirectionalLight(float lr, float lg, float lb, float nx, float ny, float nz) { int num = super.internalCreateDirectionalLight(lr, lg, lb, nx, ny, nz); lightEnable(num); glLightNoAmbient(num); glLightDirection(num); glLightDiffuse(num); glLightSpecular(num); glLightFalloff(num); return num; } protected int internalCreatePointLight(float lr, float lg, float lb, float x, float y, float z) { int num = super.internalCreatePointLight(lr, lg, lb, x, y, z); glLightNoAmbient(num); glLightPosition(num); glLightDiffuse(num); glLightSpecular(num); glLightFalloff(num); return num; } protected int internalCreateSpotLight(float lr, float lg, float lb, float x, float y, float z, float nx, float ny, float nz, float angle) { int num = super.internalCreateSpotLight(lr, lg, lb, x, y, z, nx, ny, nz, angle); glLightNoAmbient(num); glLightPosition(num); glLightDirection(num); glLightDiffuse(num); glLightSpecular(num); glLightFalloff(num); glLightSpotAngle(num); glLightSpotConcentration(num); return num; } // We're not actually turning on GL lights right now // because our home-grown ones work better for now. public void lights() { super.lights(); //gl.glEnable(GL.GL_LIGHTING); } public void noLights() { super.noLights(); gl.glDisable(GL.GL_LIGHTING); } public void lightEnable(int num) { super.lightEnable(num); gl.glEnable(GL.GL_LIGHT0 + num); } public void lightDisable(int num) { super.lightDisable(num); gl.glDisable(GL.GL_LIGHT0 + num); } public void lightPosition(int num, float x, float y, float z) { super.lightPosition(num, x, y, z); glLightPosition(num); } public void glLightPosition(int num) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_POSITION, new float[] { lightX[num], lightY[num], lightZ[num] }); } public void lightDirection(int num, float x, float y, float z) { super.lightDirection(num, x, y, z); glLightDirection(num); } public void glLightDirection(int num) { if (lightType[num] == DIRECTIONAL) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_POSITION, new float[] { lightNX[num], lightNY[num], lightNZ[num], 1 }); } else { // Spot light gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_SPOT_DIRECTION, new float[] { lightNX[num], lightNY[num], lightNZ[num] }); } } public void lightAmbient(int num, float x, float y, float z) { super.lightAmbient(num, x, y, z); glLightAmbient(num); } public void glLightNoAmbient(int num) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_AMBIENT, new float[] { 0, 0, 0 }); } public void glLightAmbient(int num) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_AMBIENT, new float[] { lightDiffuseR[num], lightDiffuseG[num], lightDiffuseB[num] }); } public void lightDiffuse(int num, float x, float y, float z) { super.lightDiffuse(num, x, y, z); glLightDiffuse(num); } public void glLightDiffuse(int num) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_DIFFUSE, new float[] { lightDiffuseR[num], lightDiffuseG[num], lightDiffuseB[num] }); } public void lightSpecular(int num, float x, float y, float z) { super.lightSpecular(num, x, y, z); glLightSpecular(num); } public void glLightSpecular(int num) { gl.glLightfv(GL.GL_LIGHT0 + num, GL.GL_SPECULAR, new float[] { lightSpecularR[num], lightSpecularG[num], lightSpecularB[num] }); } public void lightFalloff(int num, float constant, float linear, float quadratic) { super.lightFalloff(num, constant, linear, quadratic); glLightFalloff(num); } public void glLightFalloff(int num) { gl.glLightf(GL.GL_LIGHT0 + num, GL.GL_CONSTANT_ATTENUATION, lightConstantFalloff[num]); gl.glLightf(GL.GL_LIGHT0 + num, GL.GL_LINEAR_ATTENUATION, lightLinearFalloff[num]); gl.glLightf(GL.GL_LIGHT0 + num, GL.GL_QUADRATIC_ATTENUATION, lightQuadraticFalloff[num]); } public void lightSpotAngle(int num, float spotAngle) { super.lightSpotAngle(num, spotAngle); glLightSpotAngle(num); } public void glLightSpotAngle(int num) { gl.glLightf(GL.GL_LIGHT0 + num, GL.GL_SPOT_CUTOFF, lightSpotAngle[num]); } public void lightSpotConcentration(int num, float concentration) { super.lightSpotConcentration(num, concentration); glLightSpotConcentration(num); } public void glLightSpotConcentration(int num) { gl.glLightf(GL.GL_LIGHT0 + num, GL.GL_SPOT_EXPONENT, lightSpotConcentration[num]); } ////////////////////////////////////////////////////////////// /* public void fill(int rgb) { super.fill(rgb); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void fill(float gray) { super.fill(gray); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void fill(float gray, float alpha) { super.fill(gray, alpha); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void fill(float x, float y, float z) { super.fill(x, y, z); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void fill(float x, float y, float z, float a) { super.fill(x, y, z, a); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } */ protected void colorFill() { super.colorFill(); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE, new float[] { calcR, calcG, calcB, calcA }); } /* public void diffuse(int rgb) { super.diffuse(rgb); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void diffuse(float gray) { super.diffuse(gray); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void diffuse(float gray, float alpha) { super.diffuse(gray, alpha); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void diffuse(float x, float y, float z) { super.diffuse(x, y, z); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } public void diffuse(float x, float y, float z, float a) { super.diffuse(x, y, z, a); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, new float[] {calcR, calcG, calcB, calcA}); } */ ////////////////////////////////////////////////////////////// public void ambient(int rgb) { super.ambient(rgb); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, new float[] {calcR, calcG, calcB, calcA}); } public void ambient(float gray) { super.ambient(gray); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, new float[] {calcR, calcG, calcB, calcA}); } public void ambient(float x, float y, float z) { super.ambient(x, y, z); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, new float[] {calcR, calcG, calcB, calcA}); } ////////////////////////////////////////////////////////////// public void specular(int rgb) { super.specular(rgb); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, new float[] {calcR, calcG, calcB, calcA}); } public void specular(float gray) { super.specular(gray); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, new float[] {calcR, calcG, calcB, calcA}); } public void specular(float gray, float alpha) { super.specular(gray, alpha); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, new float[] {calcR, calcG, calcB, calcA}); } public void specular(float x, float y, float z) { super.specular(x, y, z); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, new float[] {calcR, calcG, calcB, calcA}); } public void specular(float x, float y, float z, float a) { super.specular(x, y, z, a); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, new float[] {calcR, calcG, calcB, calcA}); } ////////////////////////////////////////////////////////////// public void emissive(int rgb) { super.emissive(rgb); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, new float[] {calcR, calcG, calcB, calcA}); } public void emissive(float gray) { super.emissive(gray); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, new float[] {calcR, calcG, calcB, calcA}); } public void emissive(float x, float y, float z) { super.emissive(x, y, z); gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_EMISSION, new float[] {calcR, calcG, calcB, calcA}); } public void shininess(float shine) { super.shininess(shine); gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, shine); } ////////////////////////////////////////////////////////////// public void background(PImage image) { clear(); set(0, 0, image); } public void clear() { float backgroundR = (float) ((backgroundColor >> 16) & 0xff) / 255.0f; float backgroundG = (float) ((backgroundColor >> 8) & 0xff) / 255.0f; float backgroundB = (float) (backgroundColor & 0xff) / 255.0f; gl.glClearColor(backgroundR, backgroundG, backgroundB, 1); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); } ////////////////////////////////////////////////////////////// public void smooth() { gl.glEnable(GL.GL_POINT_SMOOTH); gl.glEnable(GL.GL_LINE_SMOOTH); gl.glEnable(GL.GL_POLYGON_SMOOTH); } public void noSmooth() { gl.glDisable(GL.GL_POINT_SMOOTH); gl.glDisable(GL.GL_LINE_SMOOTH); gl.glDisable(GL.GL_POLYGON_SMOOTH); } ////////////////////////////////////////////////////////////// public void loadPixels() { //throw new RuntimeException("loadPixels() not yet implemented for OpenGL"); if ((pixels == null) || (pixels.length != width*height)) { pixels = new int[width * height]; } /* for (int y = 0; y < height; y++) { // or SKIP_PIXELS with y*width //gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, (height-1) - y); gl.glReadPixels(0, y, width, y + 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels); } gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); */ gl.glReadPixels(0, 0, width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels); /* int temp[] = new int[width]; // 3 rows, skips the middle for (int y = 0; y < height/2; y++) { int yy = (height - 1) - y; System.arraycopy(pixels, y*width, temp, 0, width); System.arraycopy(pixels, yy*width, pixels, y*width, width); System.arraycopy(temp, 0, pixels, yy*width, width); } */ /* // now need to swap the RGBA components to ARGB (big endian) for (int i = 0; i < pixels.length; i++) { //pixels[i] = ((pixels[i] & 0xff) << 24) | pixels[i] = ((pixels[i] << 24) & 0xff) | // safer? ((pixels[i] >> 8) & 0xffffff); } */ // flip vertically (opengl stores images upside down), // and swap RGBA components to ARGB (big endian) int index = 0; int yindex = (height - 1) * width; for (int y = 0; y < height/2; y++) { if (BIG_ENDIAN) { for (int x = 0; x < width; x++) { int temp = pixels[index]; // ignores alpha component, just sets it opaque pixels[index] = 0xff000000 | ((pixels[yindex] >> 8) & 0x00ffffff); pixels[yindex] = 0xff000000 | ((temp >> 8) & 0x00ffffff); index++; yindex++; } } else { // LITTLE_ENDIAN, convert ABGR to ARGB for (int x = 0; x < width; x++) { int temp = pixels[index]; // identical to updatePixels because only two // components are being swapped pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) | (pixels[yindex] & 0xff00) | ((pixels[yindex] >> 16) & 0xff); pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= width*2; } } /** * Convert native OpenGL format into palatable ARGB format. * This function leaves alone (ignores) the alpha component. * Also flips the image vertically, since images are upside-down in GL. */ static void nativeToJavaRGB(PImage image) { int index = 0; int yindex = (image.height - 1) * image.width; for (int y = 0; y < image.height/2; y++) { if (BIG_ENDIAN) { for (int x = 0; x < image.width; x++) { int temp = image.pixels[index]; // ignores alpha component, just sets it opaque image.pixels[index] = 0xff000000 | ((image.pixels[yindex] >> 8) & 0x00ffffff); image.pixels[yindex] = 0xff000000 | ((temp >> 8) & 0x00ffffff); index++; yindex++; } } else { // LITTLE_ENDIAN, convert ABGR to ARGB for (int x = 0; x < image.width; x++) { int temp = image.pixels[index]; // identical to updatePixels because only two // components are being swapped image.pixels[index] = 0xff000000 | ((image.pixels[yindex] << 16) & 0xff0000) | (image.pixels[yindex] & 0xff00) | ((image.pixels[yindex] >> 16) & 0xff); image.pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= image.width*2; } } /** * Convert native OpenGL format into palatable ARGB format. * This function leaves alone (ignores) the alpha component. * Also flips the image vertically, since images are upside-down in GL. */ static void nativeToJavaARGB(PImage image) { int index = 0; int yindex = (image.height - 1) * image.width; for (int y = 0; y < image.height/2; y++) { if (BIG_ENDIAN) { for (int x = 0; x < image.width; x++) { int temp = image.pixels[index]; // ignores alpha component, just sets it opaque image.pixels[index] = (image.pixels[yindex] & 0xff000000) | ((image.pixels[yindex] >> 8) & 0x00ffffff); image.pixels[yindex] = (temp & 0xff000000) | ((temp >> 8) & 0x00ffffff); index++; yindex++; } } else { // LITTLE_ENDIAN, convert ABGR to ARGB for (int x = 0; x < image.width; x++) { int temp = image.pixels[index]; // identical to updatePixels because only two // components are being swapped image.pixels[index] = (image.pixels[yindex] & 0xff000000) | ((image.pixels[yindex] << 16) & 0xff0000) | (image.pixels[yindex] & 0xff00) | ((image.pixels[yindex] >> 16) & 0xff); image.pixels[yindex] = (temp & 0xff000000) | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= image.width*2; } } /** * Convert ARGB (Java/Processing) data to native OpenGL format. * This function leaves alone (ignores) the alpha component. * Also flips the image vertically, since images are upside-down in GL. */ static void javaToNativeRGB(PImage image) { int width = image.width; int height = image.height; int pixels[] = image.pixels; int index = 0; int yindex = (height - 1) * width; for (int y = 0; y < height/2; y++) { if (BIG_ENDIAN) { // and convert ARGB back to opengl RGBA components (big endian) for (int x = 0; x < image.width; x++) { int temp = pixels[index]; /* pixels[index] = ((pixels[yindex] >> 24) & 0xff) | ((pixels[yindex] << 8) & 0xffffff00); pixels[yindex] = ((temp >> 24) & 0xff) | ((temp << 8) & 0xffffff00); */ pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff; pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff; index++; yindex++; } } else { // convert ARGB back to native little endian ABGR for (int x = 0; x < width; x++) { int temp = pixels[index]; pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) | (pixels[yindex] & 0xff00) | ((pixels[yindex] >> 16) & 0xff); pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= width*2; } } /** * Convert Java ARGB to native OpenGL format. * Also flips the image vertically, since images are upside-down in GL. */ static void javaToNativeARGB(PImage image) { int width = image.width; int height = image.height; int pixels[] = image.pixels; int index = 0; int yindex = (height - 1) * width; for (int y = 0; y < height/2; y++) { if (BIG_ENDIAN) { // and convert ARGB back to opengl RGBA components (big endian) for (int x = 0; x < image.width; x++) { int temp = pixels[index]; pixels[index] = ((pixels[yindex] >> 24) & 0xff) | ((pixels[yindex] << 8) & 0xffffff00); pixels[yindex] = ((temp >> 24) & 0xff) | ((temp << 8) & 0xffffff00); index++; yindex++; } } else { // convert ARGB back to native little endian ABGR for (int x = 0; x < width; x++) { int temp = pixels[index]; pixels[index] = (pixels[yindex] & 0xff000000) | ((pixels[yindex] << 16) & 0xff0000) | (pixels[yindex] & 0xff00) | ((pixels[yindex] >> 16) & 0xff); pixels[yindex] = (pixels[yindex] & 0xff000000) | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= width*2; } } public void updatePixels() { // flip vertically (opengl stores images upside down), int index = 0; int yindex = (height - 1) * width; for (int y = 0; y < height/2; y++) { if (BIG_ENDIAN) { // and convert ARGB back to opengl RGBA components (big endian) for (int x = 0; x < width; x++) { int temp = pixels[index]; /* pixels[index] = ((pixels[yindex] >> 24) & 0xff) | ((pixels[yindex] << 8) & 0xffffff00); pixels[yindex] = ((temp >> 24) & 0xff) | ((temp << 8) & 0xffffff00); */ pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff; pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff; index++; yindex++; } } else { // convert ARGB back to native little endian ABGR for (int x = 0; x < width; x++) { int temp = pixels[index]; pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) | (pixels[yindex] & 0xff00) | ((pixels[yindex] >> 16) & 0xff); pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) | (temp & 0xff00) | ((temp >> 16) & 0xff); index++; yindex++; } } yindex -= width*2; } // re-pack ARGB data into RGBA for opengl (big endian) /* for (int i = 0; i < pixels.length; i++) { pixels[i] = ((pixels[i] >> 24) & 0xff) | ((pixels[i] << 8) & 0xffffff00); } */ //System.out.println("running glDrawPixels"); //gl.glRasterPos2i(width/2, height/2); //gl.glRasterPos2i(width/2, 1); //height/3); //gl.glRasterPos2i(1, height - 1); //1, 1); // for some reason, glRasterPos(0, height) won't draw anything. // my guess is that it's getting "clipped", so adding an epsilon // makes it work. also, height-1 would be the logical start, // but apparently that's not how opengl coordinates work gl.glRasterPos2f(0.0001f, height - 0.0001f); gl.glDrawPixels(width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, pixels); } public void updatePixels(int x, int y, int c, int d) { //throw new RuntimeException("updatePixels() not available with OpenGL"); // TODO make this actually work for a smaller region // problem is, it gets pretty messy with the y reflection, etc updatePixels(); } ////////////////////////////////////////////////////////////// int getset[] = new int[1]; public int get(int x, int y) { gl.glReadPixels(x, y, 1, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, getset); if (BIG_ENDIAN) { return 0xff000000 | ((getset[0] >> 8) & 0x00ffffff); } else { return 0xff000000 | ((getset[0] << 16) & 0xff0000) | (getset[0] & 0xff00) | ((getset[0] >> 16) & 0xff); } //throw new RuntimeException("get() not yet implemented for OpenGL"); //return 0; // TODO } public PImage get(int x, int y, int w, int h) { if (imageMode == CORNERS) { // if CORNER, do nothing w = (w - x); h = (h - x); } if (x < 0) { w += x; // clip off the left edge x = 0; } if (y < 0) { h += y; // clip off some of the height y = 0; } if (x + w > width) w = width - x; if (y + h > height) h = height - y; PImage newbie = new PImage(w, h); //new int[w*h], w, h, ARGB); gl.glReadPixels(x, y, w, h, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, newbie.pixels); nativeToJavaARGB(newbie); //newbie.updatePixels(); /* int index = y*width + x; int index2 = 0; for (int row = y; row < y+h; row++) { System.arraycopy(pixels, index, newbie.pixels, index2, w); index+=width; index2+=w; } */ return newbie; //throw new RuntimeException("get() not yet implemented for OpenGL"); //return null; // TODO } public PImage get() { return get(0, 0, width, height); } //PImage setter = new PImage(1, 1); public void set(int x, int y, int argb) { if (BIG_ENDIAN) { // convert ARGB to RGBA getset[0] = (argb << 8) | 0xff; } else { // convert ARGB to ABGR getset[0] = (argb & 0xff00ff00) | ((argb << 16) & 0xff0000) | //(argb & 0xff00) | ((argb >> 16) & 0xff); } gl.glRasterPos2f(x + EPSILON, y + EPSILON); gl.glDrawPixels(1, 1, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, getset); //throw new RuntimeException("set() not available with OpenGL"); } /** * Set an image directly to the screen. *

* TODO not optimized properly, creates a temporary buffer the * size of the image. Needs to instead use image cache, but that * requires two types of image cache. One for power of 2 textures * and another for glReadPixels/glDrawPixels data that's flipped * vertically. Both have their components all swapped to native. */ public void set(int x, int y, PImage source) { /* ImageCache cash = (ImageCache) source.cache; if (cash == null) { // this will flip the bits and make it a power of 2 cache(source); cash = (ImageCache) source.cache; } // now draw to the screen but set the scanline length */ int backup[] = new int[source.pixels.length]; System.arraycopy(source.pixels, 0, backup, 0, source.pixels.length); javaToNativeARGB(source); gl.glRasterPos2f(x + EPSILON, (height - y) - EPSILON); gl.glDrawPixels(source.width, source.height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, source.pixels); //nativeToJavaARGB(source); source.pixels = backup; } ////////////////////////////////////////////////////////////// /** * This is really inefficient and not a good idea. * Use get() and set() with a smaller image area. */ public void filter(int kind) { //throw new RuntimeException("filter() not available with OpenGL"); PImage temp = get(); temp.filter(kind); set(0, 0, temp); } /** * This is really inefficient and not a good idea. * Use get() and set() with a smaller image area. */ public void filter(int kind, float param) { //throw new RuntimeException("filter() not available with OpenGL"); PImage temp = get(); temp.filter(kind, param); set(0, 0, temp); } ////////////////////////////////////////////////////////////// // TODO implement these with glCopyPixels //public void copy(PImage src, int dx, int dy) { //throw new RuntimeException("copy() not available with OpenGL"); //} /** * TODO - extremely slow and not optimized. * Currently calls a loadPixels() on the whole canvas, * then does the copy, then it calls updatePixels(). */ public void copy(int sx1, int sy1, int sx2, int sy2, int dx1, int dy1, int dx2, int dy2) { //throw new RuntimeException("copy() not available with OpenGL"); loadPixels(); super.copy(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); updatePixels(); } /** * TODO - extremely slow and not optimized. * Currently calls a loadPixels() on the whole canvas, * then does the copy, then it calls updatePixels(). */ public void copy(PImage src, int sx1, int sy1, int sx2, int sy2, int dx1, int dy1, int dx2, int dy2) { loadPixels(); super.copy(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2); updatePixels(); } ////////////////////////////////////////////////////////////// public void blend(int sx, int sy, int dx, int dy, int mode) { set(dx, dy, PImage.blend(get(sx, sy), get(dx, dy), mode)); //loadPixels(); //super.blend(sx, sy, dx, dy, mode); //updatePixels(); } public void blend(PImage src, int sx, int sy, int dx, int dy, int mode) { set(dx, dy, PImage.blend(src.get(sx, sy), get(dx, dy), mode)); //loadPixels(); //super.blend(src, sx, sy, dx, dy, mode); //updatePixels(); } /** * TODO - extremely slow and not optimized. * Currently calls a loadPixels() on the whole canvas, * then does the blend, then it calls updatePixels(). */ public void blend(int sx1, int sy1, int sx2, int sy2, int dx1, int dy1, int dx2, int dy2, int mode) { loadPixels(); super.blend(sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); updatePixels(); } /** * TODO - extremely slow and not optimized. * Currently calls a loadPixels() on the whole canvas, * then does the blend, then it calls updatePixels(). */ public void blend(PImage src, int sx1, int sy1, int sx2, int sy2, int dx1, int dy1, int dx2, int dy2, int mode) { loadPixels(); super.blend(src, sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2, mode); updatePixels(); } ////////////////////////////////////////////////////////////// public void save(String filename) { loadPixels(); super.save(filename); } ////////////////////////////////////////////////////////////// /** * Report on anything from glError(). * Don't use this inside glBegin/glEnd otherwise it'll * throw an GL_INVALID_OPERATION error. */ public void report(String where) { //if (true) return; int err = gl.glGetError(); if (err == 0) { return; //System.out.println("no error"); } else { System.out.print(where + ": "); System.out.print(PApplet.hex(err, 4) + " "); switch (err) { case 0x0500: System.out.print("GL_INVALID_ENUM"); break; case 0x0501: System.out.print("GL_INVALID_VALUE"); break; case 0x0502: System.out.print("GL_INVALID_OPERATION"); break; case 0x0503: System.out.print("GL_STACK_OVERFLOW"); break; case 0x0504: System.out.print("GL_STACK_UNDERFLOW"); break; case 0x0505: System.out.print("GL_OUT_OF_MEMORY"); break; default: System.out.print("UNKNOWN"); } System.out.println(); } } }