mirror of
https://github.com/processing/processing4.git
synced 2026-02-03 21:59:20 +01:00
1722 lines
52 KiB
Java
1722 lines
52 KiB
Java
/* -*- 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.
|
|
* <P>
|
|
* 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();
|
|
}
|
|
}
|
|
}
|