diff options
Diffstat (limited to 'src/jogl/classes/com')
157 files changed, 29214 insertions, 11654 deletions
diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java index 2b51be164..fef9d61dd 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Audio.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -36,7 +36,7 @@ import java.io.*; public class Audio { private static Audio instance = null; - private Mixer mixer; + private final Mixer mixer; public synchronized static Audio getInstance() { if (instance == null) { @@ -53,9 +53,9 @@ public class Audio { return mixer; } - public Track newTrack(File file) throws IOException + public Track newTrack(final File file) throws IOException { - Track res = new Track(file); + final Track res = new Track(file); mixer.add(res); return res; } diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java index 60972873e..bbfe72b08 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Mixer.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -48,12 +48,12 @@ public class Mixer { private volatile boolean shutdownDone; // Windows Event object - private long event; + private final long event; - private volatile ArrayList/*<Track>*/ tracks = new ArrayList(); + private volatile ArrayList<Track> tracks = new ArrayList<Track>(); - private Vec3f leftSpeakerPosition = new Vec3f(-1, 0, 0); - private Vec3f rightSpeakerPosition = new Vec3f( 1, 0, 0); + private final Vec3f leftSpeakerPosition = new Vec3f(-1, 0, 0); + private final Vec3f rightSpeakerPosition = new Vec3f( 1, 0, 0); private float falloffFactor = 1.0f; @@ -64,7 +64,7 @@ public class Mixer { private Mixer() { event = CreateEvent(); new FillerThread().start(); - MixerThread m = new MixerThread(); + final MixerThread m = new MixerThread(); m.setPriority(Thread.MAX_PRIORITY - 1); m.start(); } @@ -73,14 +73,14 @@ public class Mixer { return mixer; } - synchronized void add(Track track) { - ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone(); + synchronized void add(final Track track) { + final ArrayList<Track> newTracks = new ArrayList<Track>(tracks); newTracks.add(track); tracks = newTracks; } - synchronized void remove(Track track) { - ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone(); + synchronized void remove(final Track track) { + final ArrayList<Track> newTracks = new ArrayList<Track>(tracks); newTracks.remove(track); tracks = newTracks; } @@ -88,14 +88,14 @@ public class Mixer { // NOTE: due to a bug on the APX device, we only have mono sounds, // so we currently only pay attention to the position of the left // speaker - public void setLeftSpeakerPosition(float x, float y, float z) { + public void setLeftSpeakerPosition(final float x, final float y, final float z) { leftSpeakerPosition.set(x, y, z); } // NOTE: due to a bug on the APX device, we only have mono sounds, // so we currently only pay attention to the position of the left // speaker - public void setRightSpeakerPosition(float x, float y, float z) { + public void setRightSpeakerPosition(final float x, final float y, final float z) { rightSpeakerPosition.set(x, y, z); } @@ -109,7 +109,7 @@ public class Mixer { falloffFactor + r^2 </PRE> */ - public void setFalloffFactor(float factor) { + public void setFalloffFactor(final float factor) { falloffFactor = factor; } @@ -119,7 +119,7 @@ public class Mixer { SetEvent(event); try { shutdownLock.wait(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { } } } @@ -129,15 +129,16 @@ public class Mixer { super("Mixer Thread"); } + @Override public void run() { while (!shutdown) { - List/*<Track>*/ curTracks = tracks; + final List<Track> curTracks = tracks; - for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) { - Track track = (Track) iter.next(); + for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + final Track track = iter.next(); try { track.fill(); - } catch (IOException e) { + } catch (final IOException e) { e.printStackTrace(); remove(track); } @@ -146,7 +147,7 @@ public class Mixer { try { // Run ten times per second Thread.sleep(100); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } @@ -157,7 +158,7 @@ public class Mixer { // Temporary mixing buffer // Interleaved left and right channels float[] mixingBuffer; - private Vec3f temp = new Vec3f(); + private final Vec3f temp = new Vec3f(); MixerThread() { super("Mixer Thread"); @@ -166,10 +167,11 @@ public class Mixer { } } + @Override public void run() { while (!shutdown) { // Get the next buffer - long mixerBuffer = getNextMixerBuffer(); + final long mixerBuffer = getNextMixerBuffer(); if (mixerBuffer != 0) { ByteBuffer buf = getMixerBufferData(mixerBuffer); @@ -201,27 +203,27 @@ public class Mixer { // This assertion should be in place if we have stereo if ((mixingBuffer.length % 2) != 0) { - String msg = "FATAL ERROR: odd number of samples in the mixing buffer"; + final String msg = "FATAL ERROR: odd number of samples in the mixing buffer"; System.out.println(msg); throw new InternalError(msg); } // Run down all of the registered tracks mixing them in - List/*<Track>*/ curTracks = tracks; + final List<Track> curTracks = tracks; - for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) { - Track track = (Track) iter.next(); + for (final Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + final Track track = iter.next(); // Consider only playing tracks if (track.isPlaying()) { // First recompute its gain - Vec3f pos = track.getPosition(); - float leftGain = gain(pos, leftSpeakerPosition); - float rightGain = gain(pos, rightSpeakerPosition); + final Vec3f pos = track.getPosition(); + final float leftGain = gain(pos, leftSpeakerPosition); + final float rightGain = gain(pos, rightSpeakerPosition); // Now mix it in int i = 0; while (i < mixingBuffer.length) { if (track.hasNextSample()) { - float sample = track.nextSample(); + final float sample = track.nextSample(); mixingBuffer[i++] = sample * leftGain; mixingBuffer[i++] = sample * rightGain; } else { @@ -238,7 +240,7 @@ public class Mixer { // Now that we have our data, send it down to the card int outPos = 0; for (int i = 0; i < mixingBuffer.length; i++) { - short val = (short) mixingBuffer[i]; + final short val = (short) mixingBuffer[i]; buf.put(outPos++, (byte) val); buf.put(outPos++, (byte) (val >> 8)); } @@ -277,9 +279,9 @@ public class Mixer { // falloffFactor // ------------------- // falloffFactor + r^2 - private float gain(Vec3f pos, Vec3f speakerPos) { + private float gain(final Vec3f pos, final Vec3f speakerPos) { temp.sub(pos, speakerPos); - float dotp = temp.dot(temp); + final float dotp = temp.dot(temp); return (falloffFactor / (falloffFactor + dotp)); } } @@ -319,8 +321,8 @@ public class Mixer { private static Constructor directByteBufferConstructor; private static Map createdBuffers = new HashMap(); // Map Long, ByteBuffer - private static ByteBuffer newDirectByteBuffer(long address, long capacity) { - Long key = new Long(address); + private static ByteBuffer newDirectByteBuffer(final long address, final long capacity) { + final Long key = Long.valueOf(address); ByteBuffer buf = (ByteBuffer) createdBuffers.get(key); if (buf == null) { buf = newDirectByteBufferImpl(address, capacity); @@ -330,30 +332,30 @@ public class Mixer { } return buf; } - private static ByteBuffer newDirectByteBufferImpl(long address, long capacity) { + private static ByteBuffer newDirectByteBufferImpl(final long address, final long capacity) { if (directByteBufferClass == null) { try { directByteBufferClass = Class.forName("java.nio.DirectByteBuffer"); - byte[] tmp = new byte[0]; + final byte[] tmp = new byte[0]; directByteBufferConstructor = directByteBufferClass.getDeclaredConstructor(new Class[] { Integer.TYPE, tmp.getClass(), Integer.TYPE }); directByteBufferConstructor.setAccessible(true); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } } - + if (directByteBufferConstructor != null) { try { return (ByteBuffer) directByteBufferConstructor.newInstance(new Object[] { - new Integer((int) capacity), + Integer.valueOf((int) capacity), null, - new Integer((int) address) + Integer.valueOf((int) address) }); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } } diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java index c45430d23..18698f5ea 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/SoundBuffer.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -35,16 +35,16 @@ package com.jogamp.audio.windows.waveout; import java.io.*; class SoundBuffer { - private byte[] data; - private boolean needsByteSwap; + private final byte[] data; + private final boolean needsByteSwap; private int numBytes; - private int bytesPerSample; + private final int bytesPerSample; private int numSamples; private boolean playing; private boolean empty; // Note: needsByteSwap argument makes assumptions about the format - SoundBuffer(int size, int bytesPerSample, boolean needsByteSwap) { + SoundBuffer(final int size, final int bytesPerSample, final boolean needsByteSwap) { this.bytesPerSample = bytesPerSample; this.needsByteSwap = needsByteSwap; data = new byte[size * bytesPerSample]; @@ -55,7 +55,7 @@ class SoundBuffer { return playing; } - void playing(boolean playing) { + void playing(final boolean playing) { this.playing = playing; } @@ -63,11 +63,11 @@ class SoundBuffer { return empty; } - void empty(boolean empty) { + void empty(final boolean empty) { this.empty = empty; } - void fill(InputStream input) throws IOException { + void fill(final InputStream input) throws IOException { synchronized(this) { if (playing) { throw new IllegalStateException("Can not fill a buffer that is playing"); @@ -75,7 +75,7 @@ class SoundBuffer { } empty(true); - int num = input.read(data); + final int num = input.read(data); if (num > 0) { numBytes = num; numSamples = numBytes / bytesPerSample; @@ -96,8 +96,8 @@ class SoundBuffer { // This is called by the mixer and must be extremely fast // FIXME: may want to reconsider use of floating point at this point // FIXME: assumes all sounds are of the same format to avoid normalization - float getSample(int sample) { - int startByte = sample * bytesPerSample; + float getSample(final int sample) { + final int startByte = sample * bytesPerSample; // FIXME: assumes no more than 4 bytes per sample int res = 0; if (needsByteSwap) { @@ -106,7 +106,7 @@ class SoundBuffer { res |= (data[i] & 0xff); } } else { - int endByte = startByte + bytesPerSample - 1; + final int endByte = startByte + bytesPerSample - 1; for (int i = startByte; i <= endByte; i++) { res <<= 8; res |= (data[i] & 0xff); @@ -119,6 +119,6 @@ class SoundBuffer { res = (byte) res; } - return (float) res; + return res; } } diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java index b57bf1dc6..5e55786ac 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Track.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -56,7 +56,7 @@ public class Track { // If we're playing the file, this is its input stream private InputStream input; // Keep around the file name - private File file; + private final File file; // Whether we're playing this sound private boolean playing; // Whether we're looping this sound @@ -64,7 +64,7 @@ public class Track { // The position of this sound; defaults to being at the origin private volatile Vec3f position = new Vec3f(); - Track(File file) throws IOException { + Track(final File file) throws IOException { if (!file.getName().endsWith(".rawsound")) { throw new IOException("Unsupported file format (currently supports only raw sounds)"); } @@ -96,7 +96,7 @@ public class Track { openInput(); // Fill it immediately fill(); - } catch (IOException e) { + } catch (final IOException e) { e.printStackTrace(); return; } @@ -109,7 +109,7 @@ public class Track { return playing; } - public synchronized void setLooping(boolean looping) { + public synchronized void setLooping(final boolean looping) { this.looping = looping; } @@ -117,7 +117,7 @@ public class Track { return looping; } - public void setPosition(float x, float y, float z) { + public void setPosition(final float x, final float y, final float z) { position = new Vec3f(x, y, z); } @@ -125,7 +125,7 @@ public class Track { if (input == null) { return; } - SoundBuffer curBuffer = fillingBuffer; + final SoundBuffer curBuffer = fillingBuffer; if (!curBuffer.empty()) { return; } @@ -151,8 +151,8 @@ public class Track { // These are only for use by the Mixer private float leftGain; private float rightGain; - - void setLeftGain(float leftGain) { + + void setLeftGain(final float leftGain) { this.leftGain = leftGain; } @@ -160,7 +160,7 @@ public class Track { return leftGain; } - void setRightGain(float rightGain) { + void setRightGain(final float rightGain) { this.rightGain = rightGain; } @@ -180,7 +180,7 @@ public class Track { // This is called by the mixer and must be extremely fast float nextSample() { - float res = activeBuffer.getSample(samplePosition++); + final float res = activeBuffer.getSample(samplePosition++); ++samplesRead; if (!hasNextSample()) { swapBuffers(); @@ -193,7 +193,7 @@ public class Track { } synchronized void swapBuffers() { - SoundBuffer tmp = activeBuffer; + final SoundBuffer tmp = activeBuffer; activeBuffer = fillingBuffer; fillingBuffer = tmp; fillingBuffer.empty(true); diff --git a/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java b/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java index 1afdaf081..831b25c91 100644 --- a/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java +++ b/src/jogl/classes/com/jogamp/audio/windows/waveout/Vec3f.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -48,11 +48,11 @@ class Vec3f { public Vec3f() {} - public Vec3f(Vec3f arg) { + public Vec3f(final Vec3f arg) { set(arg); } - public Vec3f(float x, float y, float z) { + public Vec3f(final float x, final float y, final float z) { set(x, y, z); } @@ -60,18 +60,18 @@ class Vec3f { return new Vec3f(this); } - public void set(Vec3f arg) { + public void set(final Vec3f arg) { set(arg.x, arg.y, arg.z); } - public void set(float x, float y, float z) { + public void set(final float x, final float y, final float z) { this.x = x; this.y = y; this.z = z; } /** Sets the ith component, 0 <= i < 3 */ - public void set(int i, float val) { + public void set(final int i, final float val) { switch (i) { case 0: x = val; break; case 1: y = val; break; @@ -81,7 +81,7 @@ class Vec3f { } /** Gets the ith component, 0 <= i < 3 */ - public float get(int i) { + public float get(final int i) { switch (i) { case 0: return x; case 1: return y; @@ -94,11 +94,11 @@ class Vec3f { public float y() { return y; } public float z() { return z; } - public void setX(float x) { this.x = x; } - public void setY(float y) { this.y = y; } - public void setZ(float z) { this.z = z; } + public void setX(final float x) { this.x = x; } + public void setY(final float y) { this.y = y; } + public void setZ(final float z) { this.z = z; } - public float dot(Vec3f arg) { + public float dot(final Vec3f arg) { return x * arg.x + y * arg.y + z * arg.z; } @@ -111,87 +111,87 @@ class Vec3f { } public void normalize() { - float len = length(); + final float len = length(); if (len == 0.0f) return; scale(1.0f / len); } /** Returns this * val; creates new vector */ - public Vec3f times(float val) { - Vec3f tmp = new Vec3f(this); + public Vec3f times(final float val) { + final Vec3f tmp = new Vec3f(this); tmp.scale(val); return tmp; } /** this = this * val */ - public void scale(float val) { + public void scale(final float val) { x *= val; y *= val; z *= val; } /** Returns this + arg; creates new vector */ - public Vec3f plus(Vec3f arg) { - Vec3f tmp = new Vec3f(); + public Vec3f plus(final Vec3f arg) { + final Vec3f tmp = new Vec3f(); tmp.add(this, arg); return tmp; } /** this = this + b */ - public void add(Vec3f b) { + public void add(final Vec3f b) { add(this, b); } /** this = a + b */ - public void add(Vec3f a, Vec3f b) { + public void add(final Vec3f a, final Vec3f b) { x = a.x + b.x; y = a.y + b.y; z = a.z + b.z; } /** Returns this + s * arg; creates new vector */ - public Vec3f addScaled(float s, Vec3f arg) { - Vec3f tmp = new Vec3f(); + public Vec3f addScaled(final float s, final Vec3f arg) { + final Vec3f tmp = new Vec3f(); tmp.addScaled(this, s, arg); return tmp; } /** this = a + s * b */ - public void addScaled(Vec3f a, float s, Vec3f b) { + public void addScaled(final Vec3f a, final float s, final Vec3f b) { x = a.x + s * b.x; y = a.y + s * b.y; z = a.z + s * b.z; } /** Returns this - arg; creates new vector */ - public Vec3f minus(Vec3f arg) { - Vec3f tmp = new Vec3f(); + public Vec3f minus(final Vec3f arg) { + final Vec3f tmp = new Vec3f(); tmp.sub(this, arg); return tmp; } /** this = this - b */ - public void sub(Vec3f b) { + public void sub(final Vec3f b) { sub(this, b); } /** this = a - b */ - public void sub(Vec3f a, Vec3f b) { + public void sub(final Vec3f a, final Vec3f b) { x = a.x - b.x; y = a.y - b.y; z = a.z - b.z; } /** Returns this cross arg; creates new vector */ - public Vec3f cross(Vec3f arg) { - Vec3f tmp = new Vec3f(); + public Vec3f cross(final Vec3f arg) { + final Vec3f tmp = new Vec3f(); tmp.cross(this, arg); return tmp; } /** this = a cross b. NOTE: "this" must be a different vector than both a and b. */ - public void cross(Vec3f a, Vec3f b) { + public void cross(final Vec3f a, final Vec3f b) { x = a.y * b.z - a.z * b.y; y = a.z * b.x - a.x * b.z; z = a.x * b.y - a.y * b.x; @@ -200,12 +200,13 @@ class Vec3f { /** Sets each component of this vector to the product of the component with the corresponding component of the argument vector. */ - public void componentMul(Vec3f arg) { + public void componentMul(final Vec3f arg) { x *= arg.x; y *= arg.y; z *= arg.z; } + @Override public String toString() { return "(" + x + ", " + y + ", " + z + ")"; } diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java index 5334d45cf..75067a3b7 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,6 +41,7 @@ package com.jogamp.gluegen.opengl; import com.jogamp.gluegen.CodeGenUtils; import com.jogamp.gluegen.JavaType; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -50,6 +51,9 @@ import java.lang.reflect.Method; import java.nio.Buffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -57,61 +61,96 @@ import java.util.Set; public class BuildComposablePipeline { - public static final int GEN_DEBUG = 1 << 0; // default - public static final int GEN_TRACE = 1 << 1; // default + /** <p>Default: true</p>. */ + public static final int GEN_DEBUG = 1 << 0; + /** <p>Default: true</p>. */ + public static final int GEN_TRACE = 1 << 1; + /** <p>Default: false</p>. */ public static final int GEN_CUSTOM = 1 << 2; + /** + * By extra command-line argument: <code>prolog_xor_downstream</code>. + * <p> + * If true, either prolog (if exist) is called or downstream's method, but not both. + * By default, both methods would be called. + * </p> + * <p>Default: false</p> + */ public static final int GEN_PROLOG_XOR_DOWNSTREAM = 1 << 3; + /** + * By extra command-line argument: <code>gl_identity_by_assignable_class</code>. + * <p> + * If true, implementation does not utilize downstream's <code>isGL*()</code> + * implementation, but determines whether the GL profile is matched by interface inheritance. + * </p> + * <p>Default: false</p> + */ + public static final int GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS = 1 << 4; + + private static final HashMap<String, String> addedGLHooks = new HashMap<String, String>(); + private static final String[] addedGLHookMethodNames = new String[] { + "mapBuffer", "mapBufferRange", + "mapNamedBuffer", "mapNamedBufferRange" }; + static { + for(int i=0; i<addedGLHookMethodNames.length; i++) { + addedGLHooks.put(addedGLHookMethodNames[i], addedGLHookMethodNames[i]); + } + } + int mode; - private String outputDir; - private String outputPackage; - private String outputName; - private Class<?> classToComposeAround; - private Class<?> classPrologOpt; - private Class<?> classDownstream; + private final String outputDir; + private final String outputPackage; + private final String outputName; + private final Class<?> classToComposeAround; + private final Class<?> classPrologOpt; + private final Class<?> classDownstream; // Only desktop OpenGL has immediate mode glBegin / glEnd private boolean hasImmediateMode; // Desktop OpenGL and GLES1 have GL_STACK_OVERFLOW and GL_STACK_UNDERFLOW errors - private boolean hasStackOverflow; + private boolean hasGL2ES1StackOverflow; - public static Class<?> getClass(String name) { + public static Class<?> getClass(final String name) { Class<?> clazz = null; try { clazz = Class.forName(name); - } catch (Exception e) { + } catch (final Exception e) { throw new RuntimeException( "Could not find class \"" + name + "\"", e); } return clazz; } - public static Method getMethod(Class<?> clazz, Method m) { + public static Method getMethod(final Class<?> clazz, final Method m) { Method res = null; try { res = clazz.getMethod(m.getName(), m.getParameterTypes()); - } catch (Exception e) { + } catch (final Exception e) { } return res; } - public static void main(String[] args) { - String classToComposeAroundName = args[0]; + public static void main(final String[] args) { + final String classToComposeAroundName = args[0]; Class<?> classPrologOpt, classDownstream; - Class<?> classToComposeAround = getClass(classToComposeAroundName); + final Class<?> classToComposeAround = getClass(classToComposeAroundName); - String outputDir = args[1]; + final String outputDir = args[1]; String outputPackage, outputName; int mode; if (args.length > 2) { - String outputClazzName = args[2]; + final String outputClazzName = args[2]; outputPackage = getPackageName(outputClazzName); outputName = getBaseClassName(outputClazzName); classPrologOpt = getClass(args[3]); classDownstream = getClass(args[4]); mode = GEN_CUSTOM; if (args.length > 5) { - if (args[5].equals("prolog_xor_downstream")) { - mode |= GEN_PROLOG_XOR_DOWNSTREAM; + for(int i=5; i<args.length; i++) { + if (args[i].equals("prolog_xor_downstream")) { + mode |= GEN_PROLOG_XOR_DOWNSTREAM; + } else if (args[i].equals("gl_identity_by_assignable_class")) { + mode |= GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS; + } } } } else { @@ -119,22 +158,22 @@ public class BuildComposablePipeline { outputName = null; // TBD .. classPrologOpt = null; classDownstream = classToComposeAround; - mode = GEN_DEBUG | GEN_TRACE; + mode = GEN_DEBUG | GEN_TRACE ; } - BuildComposablePipeline composer = + final BuildComposablePipeline composer = new BuildComposablePipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream); try { composer.emit(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException( "Error generating composable pipeline source files", e); } } - protected BuildComposablePipeline(int mode, String outputDir, String outputPackage, String outputName, - Class<?> classToComposeAround, Class<?> classPrologOpt, Class<?> classDownstream) { + protected BuildComposablePipeline(final int mode, final String outputDir, final String outputPackage, final String outputName, + final Class<?> classToComposeAround, final Class<?> classPrologOpt, final Class<?> classDownstream) { this.mode = mode; this.outputDir = outputDir; this.outputPackage = outputPackage; @@ -149,15 +188,17 @@ public class BuildComposablePipeline { } try { + // Keep assignment w/ null comparison for clarification. + // If no exception is thrown, return value is always non-null; hasImmediateMode = - (classToComposeAround.getMethod("glBegin", new Class<?>[]{Integer.TYPE}) != null); - } catch (Exception e) { + null != classToComposeAround.getMethod("glBegin", new Class<?>[]{Integer.TYPE}); + } catch (final Exception e) { } try { - hasStackOverflow = + hasGL2ES1StackOverflow = hasImmediateMode && (classToComposeAround.getField("GL_STACK_OVERFLOW") != null); - } catch (Exception e) { + } catch (final Exception e) { } } @@ -167,33 +208,46 @@ public class BuildComposablePipeline { */ public void emit() throws IOException { - List<Method> publicMethodsRaw = Arrays.asList(classToComposeAround.getMethods()); + final List<Method> publicMethodsRaw = Arrays.asList(classToComposeAround.getMethods()); - Set<PlainMethod> publicMethodsPlain = new HashSet<PlainMethod>(); - for (Iterator<Method> iter = publicMethodsRaw.iterator(); iter.hasNext();) { - Method method = iter.next(); + final Set<PlainMethod> publicMethodsPlainSet = new HashSet<PlainMethod>(); + for (final Iterator<Method> iter = publicMethodsRaw.iterator(); iter.hasNext();) { + final Method method = iter.next(); // Don't hook methods which aren't real GL methods, // such as the synthetic "isGL2ES2" "getGL2ES2" - String name = method.getName(); - boolean runHooks = name.startsWith("gl"); - if (!name.startsWith("getGL") && !name.startsWith("isGL") && !name.equals("toString")) { - publicMethodsPlain.add(new PlainMethod(method, runHooks)); + final String name = method.getName(); + if ( !name.equals("getDownstreamGL") && + !name.equals("toString") ) { + final boolean syntheticIsGL = name.startsWith("isGL"); + final boolean syntheticGetGL = name.startsWith("getGL"); + final boolean runHooks = name.startsWith("gl") || syntheticIsGL || syntheticGetGL || addedGLHooks.containsKey(name); + publicMethodsPlainSet.add(new PlainMethod(method, runHooks, syntheticIsGL, syntheticGetGL)); } } + // sort methods to make them easier to find + final List<PlainMethod> publicMethodsPlainSorted = new ArrayList<PlainMethod>(); + publicMethodsPlainSorted.addAll(publicMethodsPlainSet); + Collections.sort(publicMethodsPlainSorted, new Comparator<PlainMethod>() { + @Override + public int compare(final PlainMethod o1, final PlainMethod o2) { + return o1.getWrappedMethod().getName().compareTo(o2.getWrappedMethod().getName()); + } + }); + if (0 != (mode & GEN_DEBUG)) { - (new DebugPipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlain.iterator()); + (new DebugPipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } if (0 != (mode & GEN_TRACE)) { - (new TracePipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlain.iterator()); + (new TracePipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } if (0 != (mode & GEN_CUSTOM)) { - (new CustomPipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream)).emit(publicMethodsPlain.iterator()); + (new CustomPipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } } - public static String getPackageName(String clazzName) { - int lastDot = clazzName.lastIndexOf('.'); + public static String getPackageName(final String clazzName) { + final int lastDot = clazzName.lastIndexOf('.'); if (lastDot == -1) { // no package, class is at root level return null; @@ -201,8 +255,8 @@ public class BuildComposablePipeline { return clazzName.substring(0, lastDot); } - public static String getBaseClassName(String clazzName) { - int lastDot = clazzName.lastIndexOf('.'); + public static String getBaseClassName(final String clazzName) { + final int lastDot = clazzName.lastIndexOf('.'); if (lastDot == -1) { // no package, class is at root level return clazzName; @@ -211,14 +265,18 @@ public class BuildComposablePipeline { } //------------------------------------------------------- - protected class PlainMethod { + protected static class PlainMethod { - Method m; - boolean runHooks; + final Method m; + final boolean runHooks; + final boolean isSynthethicIsGL; + final boolean isSynthethicGetGL; - PlainMethod(Method m, boolean runHooks) { + PlainMethod(final Method m, final boolean runHooks, final boolean isSynthethicIsGL, final boolean isSynthethicGetGL) { this.m = m; this.runHooks = runHooks; + this.isSynthethicIsGL = isSynthethicIsGL; + this.isSynthethicGetGL = isSynthethicGetGL; } public Method getWrappedMethod() { @@ -229,11 +287,15 @@ public class BuildComposablePipeline { return runHooks; } + public boolean isSynthetic() { return isSynthethicIsGL || isSynthethicGetGL; } + public boolean isSyntheticIsGL() { return isSynthethicIsGL; } + public boolean isSyntheticGetGL() { return isSynthethicGetGL; } + @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (obj instanceof PlainMethod) { - PlainMethod b = (PlainMethod) obj; - boolean res = + final PlainMethod b = (PlainMethod) obj; + final boolean res = m.getName().equals(b.m.getName()) && m.getModifiers() == b.m.getModifiers() && m.getReturnType().equals(b.m.getReturnType()) @@ -246,7 +308,7 @@ public class BuildComposablePipeline { @Override public int hashCode() { int hash = m.getName().hashCode() ^ m.getModifiers() ^ m.getReturnType().hashCode(); - Class<?>[] args = m.getParameterTypes(); + final Class<?>[] args = m.getParameterTypes(); for (int i = 0; i < args.length; i++) { hash ^= args[i].hashCode(); } @@ -255,8 +317,8 @@ public class BuildComposablePipeline { @Override public String toString() { - Class<?>[] args = m.getParameterTypes(); - StringBuilder argsString = new StringBuilder(); + final Class<?>[] args = m.getParameterTypes(); + final StringBuilder argsString = new StringBuilder(); argsString.append("("); for (int i = 0; i < args.length; i++) { if (i > 0) { @@ -267,6 +329,7 @@ public class BuildComposablePipeline { argsString.append(")"); return m.toString() + "\n\tname: " + m.getName() + + "\n\tsynt: isGL " + isSynthethicIsGL+", getGL "+isSynthethicGetGL + "\n\tmods: " + m.getModifiers() + "\n\tretu: " + m.getReturnType() + "\n\targs[" + args.length + "]: " + argsString.toString(); @@ -300,7 +363,7 @@ public class BuildComposablePipeline { * @exception IllegalArgumentException if classToComposeAround is not an * interface. */ - PipelineEmitter(String outputDir, String outputPackage, Class<?> baseInterfaceClass, Class<?> prologClassOpt, Class<?> downstreamClass) { + PipelineEmitter(final String outputDir, final String outputPackage, final Class<?> baseInterfaceClass, final Class<?> prologClassOpt, final Class<?> downstreamClass) { this.outputDir = outputDir; this.outputPackage = outputPackage; this.baseInterfaceClass = baseInterfaceClass; @@ -317,38 +380,38 @@ public class BuildComposablePipeline { } } - public void emit(Iterator<PlainMethod> methodsToWrap) throws IOException { - String outputClassName = getOutputName(); + public void emit(final Iterator<PlainMethod> methodsToWrap) throws IOException { + final String outputClassName = getOutputName(); this.file = new File(outputDir + File.separatorChar + outputClassName + ".java"); - String parentDir = file.getParent(); + final String parentDir = file.getParent(); if (parentDir != null) { - File pDirFile = new File(parentDir); + final File pDirFile = new File(parentDir); pDirFile.mkdirs(); } - PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter(file))); + final PrintWriter output = new PrintWriter(new BufferedWriter(new FileWriter(file))); - List<Class<?>> baseInterfaces = Arrays.asList(baseInterfaceClass.getInterfaces()); - HashSet<Class<?>> clazzList = new HashSet<Class<?>>(); + final List<Class<?>> baseInterfaces = Arrays.asList(baseInterfaceClass.getInterfaces()); + final HashSet<Class<?>> clazzList = new HashSet<Class<?>>(); clazzList.add(baseInterfaceClass); clazzList.addAll(baseInterfaces); - int ifNamesNumber = clazzList.size(); + final int ifNamesNumber = clazzList.size(); // keep original order .. clazzList.clear(); - String[] ifNames = new String[ifNamesNumber]; + final String[] ifNames = new String[ifNamesNumber]; { int i = 0; - for (Iterator<Class<?>> iter = baseInterfaces.iterator(); iter.hasNext();) { - Class<?> ifClass = iter.next(); + for (final Iterator<Class<?>> iter = baseInterfaces.iterator(); iter.hasNext();) { + final Class<?> ifClass = iter.next(); if (!clazzList.contains(ifClass)) { ifNames[i++] = ifClass.getName(); clazzList.add(ifClass); } } - if (null != baseInterfaceClass && !clazzList.contains(baseInterfaceClass)) { + if ( !clazzList.contains(baseInterfaceClass) ) { ifNames[i++] = baseInterfaceClass.getName(); clazzList.add(baseInterfaceClass); } @@ -359,12 +422,12 @@ public class BuildComposablePipeline { clazzList.add(prologClassOpt); } - ArrayList<String> imports = new ArrayList<String>(); + final ArrayList<String> imports = new ArrayList<String>(); imports.add("java.io.*"); imports.add("javax.media.opengl.*"); imports.add("com.jogamp.gluegen.runtime.*"); imports.add(Buffer.class.getPackage().getName()+".*"); - for (Class<?> clasS : clazzList) { + for (final Class<?> clasS : clazzList) { imports.add(clasS.getName()); } @@ -377,7 +440,8 @@ public class BuildComposablePipeline { ifNames, null, new CodeGenUtils.EmissionCallback() { - public void emit(PrintWriter w) { + @Override + public void emit(final PrintWriter w) { emitClassDocComment(w); } }); @@ -386,15 +450,14 @@ public class BuildComposablePipeline { constructorHook(output); - emitGLIsMethods(output); - emitGLGetMethods(output); + emitSyntheticGLMethods(output); while (methodsToWrap.hasNext()) { - PlainMethod pm = methodsToWrap.next(); - Method m = pm.getWrappedMethod(); + final PlainMethod pm = methodsToWrap.next(); + final Method m = pm.getWrappedMethod(); emitMethodDocComment(output, m); emitSignature(output, m); - emitBody(output, m, pm.runHooks()); + emitBody(output, pm); } postMethodEmissionHook(output); @@ -426,30 +489,27 @@ public class BuildComposablePipeline { return null; } - protected void emitMethodDocComment(PrintWriter output, Method m) { + protected void emitMethodDocComment(final PrintWriter output, final Method m) { } - protected void emitSignature(PrintWriter output, Method m) { - output.print(" public "); - output.print(' '); - output.print(JavaType.createForClass(m.getReturnType()).getName()); - output.print(' '); - output.print(m.getName()); - output.print('('); - output.print(getArgListAsString(m, true, true)); - output.println(")"); + protected void emitSignature(final PrintWriter output, final Method m) { + output.format(" @Override%n public %s %s(%s)%n", + JavaType.createForClass(m.getReturnType()).getName(), + m.getName(), + getArgListAsString(m, true, true)); } - protected void emitBody(PrintWriter output, Method m, boolean runHooks) { + protected void emitBody(final PrintWriter output, final PlainMethod pm) { + final boolean runHooks = pm.runHooks(); + final Method m = pm.getWrappedMethod(); output.println(" {"); - output.print(" "); - Class<?> retType = m.getReturnType(); + final Class<?> retType = m.getReturnType(); - boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(m); - boolean callPostDownstreamHook = runHooks && hasPostDownstreamCallHook(m); - boolean callDownstream = (null != getMethod(downstreamClass, m)) + final boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(pm); + final boolean callPostDownstreamHook = runHooks && hasPostDownstreamCallHook(pm); + final boolean callDownstream = (null != getMethod(downstreamClass, m)) && !(0 != (GEN_PROLOG_XOR_DOWNSTREAM & getMode()) && callPreDownstreamHook); - boolean hasResult = (retType != Void.TYPE); + final boolean hasResult = (retType != Void.TYPE); if (!callDownstream) { if (!emptyDownstreamAllowed()) { @@ -476,45 +536,54 @@ public class BuildComposablePipeline { output.print(" return "); } } - preDownstreamCallHook(output, m); + preDownstreamCallHook(output, pm); } if (callDownstream) { - if (hasResult) { - if (callPostDownstreamHook) { - output.print(" " + JavaType.createForClass(retType).getName()); - output.print(" _res = "); - } else { - output.print(" return "); + if( pm.isSyntheticIsGL() ) { + emitGLIsMethodBody(output, pm); + } else if( pm.isSyntheticGetGL() ) { + emitGLGetMethodBody(output, pm); + } else { + if (hasResult) { + if (callPostDownstreamHook) { + output.print(" " + JavaType.createForClass(retType).getName()); + output.print(" _res = "); + } else { + output.print(" return "); + } + } + else { + output.print(" "); } + output.print(getDownstreamObjectName()); + output.print('.'); + output.print(m.getName()); + output.print('('); + output.print(getArgListAsString(m, false, true)); + output.println(");"); } - output.print(getDownstreamObjectName()); - output.print('.'); - output.print(m.getName()); - output.print('('); - output.print(getArgListAsString(m, false, true)); - output.println(");"); } if (callPostDownstreamHook) { - postDownstreamCallHook(output, m); + postDownstreamCallHook(output, pm); } if (hasResult && callDownstream && callPostDownstreamHook) { output.println(" return _res;"); } - output.println(" }"); + output.println(" }"); } - protected String getArgListAsString(Method m, boolean includeArgTypes, boolean includeArgNames) { - StringBuilder buf = new StringBuilder(256); + protected String getArgListAsString(final Method m, final boolean includeArgTypes, final boolean includeArgNames) { + final StringBuilder buf = new StringBuilder(256); if (!includeArgNames && !includeArgTypes) { throw new IllegalArgumentException( "Cannot generate arglist without both arg types and arg names"); } - Class<?>[] argTypes = m.getParameterTypes(); + final Class<?>[] argTypes = m.getParameterTypes(); for (int i = 0; i < argTypes.length; ++i) { if (includeArgTypes) { buf.append(JavaType.createForClass(argTypes[i]).getName()); @@ -549,7 +618,7 @@ public class BuildComposablePipeline { * Called after the class headers have been generated, but before any * method wrappers have been generated. */ - protected void preMethodEmissionHook(PrintWriter output) { + protected void preMethodEmissionHook(final PrintWriter output) { output.println(" public static final boolean DEBUG = jogamp.opengl.Debug.debug(\"" + getOutputName() + "\");"); } @@ -562,10 +631,11 @@ public class BuildComposablePipeline { * Called after the method wrappers have been generated, but before the * closing parenthesis of the class is emitted. */ - protected void postMethodEmissionHook(PrintWriter output) { + protected void postMethodEmissionHook(final PrintWriter output) { + output.println(" @Override"); output.println(" public String toString() {"); output.println(" StringBuilder sb = new StringBuilder();"); - output.println(" sb.append(\"" + getOutputName() + " [ implementing " + baseInterfaceClass.getName() + ",\\n\\t\");"); + output.println(" sb.append(\"" + getOutputName() + " [this 0x\"+Integer.toHexString(hashCode())+\" implementing " + baseInterfaceClass.getName() + ",\\n\\t\");"); if (null != prologClassOpt) { output.println(" sb.append(\" prolog: \"+" + getPrologObjectNameOpt() + ".toString()+\",\\n\\t\");"); } @@ -577,17 +647,17 @@ public class BuildComposablePipeline { /** * Called before the pipeline routes the call to the downstream object. */ - protected abstract void preDownstreamCallHook(PrintWriter output, Method m); + protected abstract void preDownstreamCallHook(PrintWriter output, PlainMethod pm); - protected abstract boolean hasPreDownstreamCallHook(Method m); + protected abstract boolean hasPreDownstreamCallHook(PlainMethod pm); /** * Called after the pipeline has routed the call to the downstream object, * but before the calling function exits or returns a value. */ - protected abstract void postDownstreamCallHook(PrintWriter output, Method m); + protected abstract void postDownstreamCallHook(PrintWriter output, PlainMethod pm); - protected abstract boolean hasPostDownstreamCallHook(Method m); + protected abstract boolean hasPostDownstreamCallHook(PlainMethod pm); protected abstract int getMode(); @@ -601,71 +671,57 @@ public class BuildComposablePipeline { /** * Emits one of the isGL* methods. */ - protected void emitGLIsMethod(PrintWriter output, String type) { - output.println(" public boolean is" + type + "() {"); - Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); - if (clazz.isAssignableFrom(baseInterfaceClass)) { + protected void emitGLIsMethodBody(final PrintWriter output, final PlainMethod plainMethod) { + final String methodName = plainMethod.getWrappedMethod().getName(); + final String type = methodName.substring(2); + + if( type.equals("GL") ) { output.println(" return true;"); + } else if( 0 != ( GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) && + !type.equals("GLES") && + !type.endsWith("core") && + !type.endsWith("Compatible") ) + { + final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); + if (clazz.isAssignableFrom(baseInterfaceClass)) { + output.println(" return true;"); + } else { + output.println(" return false;"); + } } else { - output.println(" return false;"); + output.println(" return " + getDownstreamObjectName() + ".is" + type + "();"); } - output.println(" }"); - } - - /** - * Emits all of the isGL* methods. - */ - protected void emitGLIsMethods(PrintWriter output) { - emitGLIsMethod(output, "GL"); - emitGLIsMethod(output, "GL4bc"); - emitGLIsMethod(output, "GL4"); - emitGLIsMethod(output, "GL3bc"); - emitGLIsMethod(output, "GL3"); - emitGLIsMethod(output, "GL2"); - emitGLIsMethod(output, "GLES1"); - emitGLIsMethod(output, "GLES2"); - emitGLIsMethod(output, "GL2ES1"); - emitGLIsMethod(output, "GL2ES2"); - emitGLIsMethod(output, "GL2GL3"); - output.println(" public boolean isGLES() {"); - output.println(" return isGLES2() || isGLES1();"); - output.println(" }"); - output.println(" public boolean isGLES2Compatible() {"); - output.println(" return " + getDownstreamObjectName() + ".isGLES2Compatible();"); - output.println(" }"); } /** * Emits one of the getGL* methods. */ - protected void emitGLGetMethod(PrintWriter output, String type) { - output.println(" public javax.media.opengl." + type + " get" + type + "() {"); - Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); - if (clazz.isAssignableFrom(baseInterfaceClass)) { + protected void emitGLGetMethodBody(final PrintWriter output, final PlainMethod plainMethod) { + final String methodName = plainMethod.getWrappedMethod().getName(); + final String type = methodName.substring(3); + + if( type.equals("GL") ) { output.println(" return this;"); + } else if( type.equals("GLProfile") ) { + output.println(" return " + getDownstreamObjectName() + ".getGLProfile();"); } else { - output.println(" throw new GLException(\"Not a " + type + " implementation\");"); + final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); + if (clazz.isAssignableFrom(baseInterfaceClass)) { + output.println(" if( is" + type + "() ) { return this; }"); + output.println(" throw new GLException(\"Not a " + type + " implementation\");"); + } else { + output.println(" throw new GLException(\"Not a " + type + " implementation\");"); + } } - output.println(" }"); } /** - * Emits all of the getGL* methods. + * Emits all synthetic GL* methods, but not isGL* nor getGL* */ - protected void emitGLGetMethods(PrintWriter output) { - emitGLGetMethod(output, "GL"); - emitGLGetMethod(output, "GL4bc"); - emitGLGetMethod(output, "GL4"); - emitGLGetMethod(output, "GL3bc"); - emitGLGetMethod(output, "GL3"); - emitGLGetMethod(output, "GL2"); - emitGLGetMethod(output, "GLES1"); - emitGLGetMethod(output, "GLES2"); - emitGLGetMethod(output, "GL2ES1"); - emitGLGetMethod(output, "GL2ES2"); - emitGLGetMethod(output, "GL2GL3"); - output.println(" public GLProfile getGLProfile() {"); - output.println(" return " + getDownstreamObjectName() + ".getGLProfile();"); + protected void emitSyntheticGLMethods(final PrintWriter output) { + output.println(" @Override"); + output.println(" public final GL getDownstreamGL() throws GLException {"); + output.println(" return " + getDownstreamObjectName() + ";"); output.println(" }"); } } // end class PipelineEmitter @@ -676,34 +732,39 @@ public class BuildComposablePipeline { String className; int mode; - CustomPipeline(int mode, String outputDir, String outputPackage, String outputName, Class<?> baseInterfaceClass, Class<?> prologClassOpt, Class<?> downstreamClass) { + CustomPipeline(final int mode, final String outputDir, final String outputPackage, final String outputName, final Class<?> baseInterfaceClass, final Class<?> prologClassOpt, final Class<?> downstreamClass) { super(outputDir, outputPackage, baseInterfaceClass, prologClassOpt, downstreamClass); className = outputName; this.mode = mode; } + @Override protected String getOutputName() { return className; } + @Override protected int getMode() { return mode; } + @Override protected boolean emptyMethodAllowed() { return true; } + @Override protected boolean emptyDownstreamAllowed() { return true; } @Override - protected void preMethodEmissionHook(PrintWriter output) { + protected void preMethodEmissionHook(final PrintWriter output) { super.preMethodEmissionHook(output); } - protected void constructorHook(PrintWriter output) { + @Override + protected void constructorHook(final PrintWriter output) { output.print(" public " + getOutputName() + "("); output.print(downstreamName + " " + getDownstreamObjectName()); if (null != prologNameOpt) { @@ -726,14 +787,15 @@ public class BuildComposablePipeline { } @Override - protected void postMethodEmissionHook(PrintWriter output) { + protected void postMethodEmissionHook(final PrintWriter output) { super.postMethodEmissionHook(output); if (null != prologNameOpt) { output.print(" private " + prologNameOpt + " " + getPrologObjectNameOpt() + ";"); } } - protected void emitClassDocComment(PrintWriter output) { + @Override + protected void emitClassDocComment(final PrintWriter output) { output.println("/**"); output.println(" * Composable pipeline {@link " + outputPackage + "." + outputName + "}, implementing the interface"); output.println(" * {@link " + baseInterfaceClass.getName() + "}"); @@ -767,11 +829,14 @@ public class BuildComposablePipeline { output.println("*/"); } - protected boolean hasPreDownstreamCallHook(Method m) { - return null != getMethod(prologClassOpt, m); + @Override + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return null != getMethod(prologClassOpt, pm.getWrappedMethod()); } - protected void preDownstreamCallHook(PrintWriter output, Method m) { + @Override + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (null != prologNameOpt) { output.print(getPrologObjectNameOpt()); output.print('.'); @@ -782,11 +847,13 @@ public class BuildComposablePipeline { } } - protected boolean hasPostDownstreamCallHook(Method m) { + @Override + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { return false; } - protected void postDownstreamCallHook(PrintWriter output, Method m) { + @Override + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { } } // end class CustomPipeline @@ -794,33 +861,38 @@ public class BuildComposablePipeline { String className; - DebugPipeline(String outputDir, String outputPackage, Class<?> baseInterfaceClass, Class<?> downstreamClass) { + DebugPipeline(final String outputDir, final String outputPackage, final Class<?> baseInterfaceClass, final Class<?> downstreamClass) { super(outputDir, outputPackage, baseInterfaceClass, null, downstreamClass); className = "Debug" + getBaseInterfaceName(); } + @Override protected String getOutputName() { return className; } + @Override protected int getMode() { return 0; } + @Override protected boolean emptyMethodAllowed() { return false; } + @Override protected boolean emptyDownstreamAllowed() { return false; } @Override - protected void preMethodEmissionHook(PrintWriter output) { + protected void preMethodEmissionHook(final PrintWriter output) { super.preMethodEmissionHook(output); } - protected void constructorHook(PrintWriter output) { + @Override + protected void constructorHook(final PrintWriter output) { output.print(" public " + getOutputName() + "("); output.println(downstreamName + " " + getDownstreamObjectName() + ")"); output.println(" {"); @@ -840,27 +912,23 @@ public class BuildComposablePipeline { } @Override - protected void postMethodEmissionHook(PrintWriter output) { + protected void postMethodEmissionHook(final PrintWriter output) { super.postMethodEmissionHook(output); - output.println(" private void checkGLGetError(String caller)"); - output.println(" {"); + output.println(" private int checkGLError() {"); if (hasImmediateMode) { - output.println(" if (insideBeginEndPair) {"); - output.println(" return;"); - output.println(" }"); + output.println(" if (insideBeginEndPair) return GL_NO_ERROR;"); output.println(); } - output.println(" // Debug code to make sure the pipeline is working; leave commented out unless testing this class"); - output.println(" //System.err.println(\"Checking for GL errors " - + "after call to \" + caller);"); - output.println(); - output.println(" int err = " - + getDownstreamObjectName() - + ".glGetError();"); - output.println(" if (err == GL_NO_ERROR) { return; }"); - output.println(); - output.println(" StringBuilder buf = new StringBuilder(Thread.currentThread()+"); - output.println(" \" glGetError() returned the following error codes after a call to \" + caller + \": \");"); + output.format(" return %s.glGetError();%n", getDownstreamObjectName()); + output.println(" }"); + + output.println(" private void writeGLError(int err, String fmt, Object... args)"); + output.println(" {"); + output.println(" StringBuilder buf = new StringBuilder();"); + output.println(" buf.append(Thread.currentThread().toString());"); + output.println(" buf.append(\" glGetError() returned the following error codes after a call to \");"); + output.println(" buf.append(String.format(fmt, args));"); + output.println(" buf.append(\": \");"); output.println(); output.println(" // Loop repeatedly to allow for distributed GL implementations,"); output.println(" // as detailed in the glGetError() specification"); @@ -870,9 +938,9 @@ public class BuildComposablePipeline { output.println(" case GL_INVALID_ENUM: buf.append(\"GL_INVALID_ENUM \"); break;"); output.println(" case GL_INVALID_VALUE: buf.append(\"GL_INVALID_VALUE \"); break;"); output.println(" case GL_INVALID_OPERATION: buf.append(\"GL_INVALID_OPERATION \"); break;"); - if (hasStackOverflow) { - output.println(" case GL_STACK_OVERFLOW: buf.append(\"GL_STACK_OVERFLOW \"); break;"); - output.println(" case GL_STACK_UNDERFLOW: buf.append(\"GL_STACK_UNDERFLOW \"); break;"); + if (hasGL2ES1StackOverflow) { + output.println(" case GL2ES1.GL_STACK_OVERFLOW: buf.append(\"GL_STACK_OVERFLOW \"); break;"); + output.println(" case GL2ES1.GL_STACK_UNDERFLOW: buf.append(\"GL_STACK_UNDERFLOW \"); break;"); } output.println(" case GL_OUT_OF_MEMORY: buf.append(\"GL_OUT_OF_MEMORY \"); break;"); output.println(" case GL_NO_ERROR: throw new InternalError(\"Should not be treating GL_NO_ERROR as error\");"); @@ -901,31 +969,42 @@ public class BuildComposablePipeline { output.println(" private GLContext _context;"); } - protected void emitClassDocComment(PrintWriter output) { - output.println("/** <P> Composable pipeline which wraps an underlying {@link GL} implementation,"); - output.println(" providing error checking after each OpenGL method call. If an error occurs,"); - output.println(" causes a {@link GLException} to be thrown at exactly the point of failure."); - output.println(" Sample code which installs this pipeline: </P>"); - output.println(); - output.println("<PRE>"); - output.println(" GL gl = drawable.setGL(new DebugGL(drawable.getGL()));"); - output.println("</PRE>"); - output.println("*/"); + @Override + protected void emitClassDocComment(final PrintWriter output) { + output.println("/**"); + output.println(" * <p>"); + output.println(" * Composable pipeline which wraps an underlying {@link GL} implementation,"); + output.println(" * providing error checking after each OpenGL method call. If an error occurs,"); + output.println(" * causes a {@link GLException} to be thrown at exactly the point of failure."); + output.println(" * </p>"); + output.println(" * <p>"); + output.println(" * Sample code which installs this pipeline:"); + output.println(" * <pre>"); + output.println(" * gl = drawable.setGL(new DebugGL(drawable.getGL()));"); + output.println(" * </pre>"); + output.println(" * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}"); + output.println(" * </p>"); + output.println(" */"); } - protected boolean hasPreDownstreamCallHook(Method m) { - return true; + @Override + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } - protected void preDownstreamCallHook(PrintWriter output, Method m) { + @Override + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { output.println(" checkContext();"); } - protected boolean hasPostDownstreamCallHook(Method m) { - return true; + @Override + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } - protected void postDownstreamCallHook(PrintWriter output, Method m) { + @Override + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (m.getName().equals("glBegin")) { output.println(" insideBeginEndPair = true;"); output.println(" // NOTE: can't check glGetError(); it's not allowed inside glBegin/glEnd pair"); @@ -934,24 +1013,39 @@ public class BuildComposablePipeline { output.println(" insideBeginEndPair = false;"); } - output.println(" String txt = new String(\"" + m.getName() + "(\" +"); - Class<?>[] params = m.getParameterTypes(); - for (int i = 0; params != null && i < params.length; i++) { - output.print(" \"<" + params[i].getName() + ">"); + output.println(" final int err = checkGLError();"); + output.println(" if (err != GL_NO_ERROR) {"); + + final StringBuilder fmtsb = new StringBuilder(); + final StringBuilder argsb = new StringBuilder(); + + fmtsb.append("\"%s("); + argsb.append("\"").append(m.getName()).append("\""); + final Class<?>[] params = m.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + if (i > 0) { + fmtsb.append(", "); + } + fmtsb.append("<").append(params[i].getName()).append(">"); if (params[i].isArray()) { - output.print("\" +"); + //nothing } else if (params[i].equals(int.class)) { - output.print(" 0x\"+Integer.toHexString(arg" + i + ").toUpperCase() +"); + fmtsb.append(" 0x%X"); + argsb.append(", arg").append(i); } else { - output.print(" \"+arg" + i + " +"); - } - if (i < params.length - 1) { - output.println(" \", \" +"); + fmtsb.append(" %s"); + argsb.append(", arg").append(i); } } - output.println(" \")\");"); + fmtsb.append(")\","); + argsb.append(");"); + // calls to glGetError() are only allowed outside of glBegin/glEnd pairs - output.println(" checkGLGetError( txt );"); + output.print(" writeGLError(err, "); + output.println(fmtsb.toString()); + output.print(" "); + output.println(argsb.toString()); + output.println(" }"); } } } // end class DebugPipeline @@ -961,33 +1055,38 @@ public class BuildComposablePipeline { String className; - TracePipeline(String outputDir, String outputPackage, Class<?> baseInterfaceClass, Class<?> downstreamClass) { + TracePipeline(final String outputDir, final String outputPackage, final Class<?> baseInterfaceClass, final Class<?> downstreamClass) { super(outputDir, outputPackage, baseInterfaceClass, null, downstreamClass); className = "Trace" + getBaseInterfaceName(); } + @Override protected String getOutputName() { return className; } + @Override protected int getMode() { return 0; } + @Override protected boolean emptyMethodAllowed() { return false; } + @Override protected boolean emptyDownstreamAllowed() { return false; } @Override - protected void preMethodEmissionHook(PrintWriter output) { + protected void preMethodEmissionHook(final PrintWriter output) { super.preMethodEmissionHook(output); } - protected void constructorHook(PrintWriter output) { + @Override + protected void constructorHook(final PrintWriter output) { output.print(" public " + getOutputName() + "("); output.println(downstreamName + " " + getDownstreamObjectName() + ", PrintStream " + getOutputStreamName() + ")"); output.println(" {"); @@ -1003,7 +1102,7 @@ public class BuildComposablePipeline { } @Override - protected void postMethodEmissionHook(PrintWriter output) { + protected void postMethodEmissionHook(final PrintWriter output) { super.postMethodEmissionHook(output); output.println("private PrintStream " + getOutputStreamName() + ";"); output.println("private int indent = 0;"); @@ -1037,27 +1136,37 @@ public class BuildComposablePipeline { output.println("}"); } - protected void emitClassDocComment(PrintWriter output) { - output.println("/** <P> Composable pipeline which wraps an underlying {@link GL} implementation,"); - output.println(" providing tracing information to a user-specified {@link java.io.PrintStream}"); - output.println(" before and after each OpenGL method call. Sample code which installs this pipeline: </P>"); - output.println(); - output.println("<PRE>"); - output.println(" GL gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err));"); - output.println("</PRE>"); - output.println("*/"); + @Override + protected void emitClassDocComment(final PrintWriter output) { + output.println("/**"); + output.println(" * <p>"); + output.println(" * Composable pipeline which wraps an underlying {@link GL} implementation,"); + output.println(" * providing tracing information to a user-specified {@link java.io.PrintStream}"); + output.println(" * before and after each OpenGL method call."); + output.println(" * </p>"); + output.println(" * <p>"); + output.println(" * Sample code which installs this pipeline:"); + output.println(" * <pre>"); + output.println(" * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err));"); + output.println(" * </pre>"); + output.println(" * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}"); + output.println(" * </p>"); + output.println(" */"); } - protected boolean hasPreDownstreamCallHook(Method m) { - return true; + @Override + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } - protected void preDownstreamCallHook(PrintWriter output, Method m) { + @Override + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (m.getName().equals("glEnd") || m.getName().equals("glEndList")) { - output.println("indent-=2;"); + output.println(" indent-=2;"); output.println(" printIndent();"); } else { - output.println("printIndent();"); + output.println(" printIndent();"); } output.print(" print("); @@ -1065,17 +1174,23 @@ public class BuildComposablePipeline { output.println(");"); } - protected boolean hasPostDownstreamCallHook(Method m) { - return true; + @Override + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } - protected void postDownstreamCallHook(PrintWriter output, Method m) { - Class<?> ret = m.getReturnType(); + @Override + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); + final Class<?> ret = m.getReturnType(); if (ret != Void.TYPE) { output.println(" println(\" = \"+_res);"); } else { output.println(" println(\"\");"); } + + if (m.getName().equals("glBegin")) + output.println(" indent+=2;"); } private String getOutputStreamName() { @@ -1083,8 +1198,8 @@ public class BuildComposablePipeline { } } // end class TracePipeline - public static final void printFunctionCallString(PrintWriter output, Method m) { - Class<?>[] params = m.getParameterTypes(); + public static final void printFunctionCallString(final PrintWriter output, final Method m) { + final Class<?>[] params = m.getParameterTypes(); output.print(" \"" + m.getName() + "(\""); for (int i = 0; i < params.length; i++) { output.print("+\"<" + params[i].getName() + ">"); diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java index 482d35cae..3b08b5386 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -68,7 +68,7 @@ import java.util.regex.Pattern; * <br> * * <pre> - * + * * #ifndef GL_XXXX * GLAPI <returnType> <APIENTRY|GLAPIENTRY> glFuncName(<params>) * #endif GL_XXXX @@ -78,7 +78,7 @@ import java.util.regex.Pattern; * For example, if it parses the following data: * * <pre> - * + * * #ifndef GL_VERSION_1_3 * GLAPI void APIENTRY glActiveTexture (GLenum); * GLAPI void APIENTRY glMultiTexCoord1dv (GLenum, const GLdouble *); @@ -89,7 +89,7 @@ import java.util.regex.Pattern; * GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); * GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid *); * #endif - * + * * </pre> * * It will associate @@ -105,16 +105,23 @@ import java.util.regex.Pattern; * */ public class BuildStaticGLInfo { - // Handles function pointer - protected static int funcIdentifierGroup = 10; - protected static Pattern funcPattern = - Pattern.compile("^(GLAPI|GL_API|GL_APICALL|EGLAPI|extern)?(\\s*)((unsigned|const)\\s+)?(\\w+)(\\s*\\*)?(\\s+)(GLAPIENTRY|GL_APIENTRY|APIENTRY|EGLAPIENTRY|WINAPI)?(\\s*)([ew]?gl\\w+)\\s?(\\(.*)"); + // Handles function pointer + protected static final int funcIdentifierGroup = 9; + protected static final Pattern funcPattern = + Pattern.compile("^(GLAPI|GL_API|GL_APICALL|EGLAPI|extern)?(\\s*)((unsigned|const)\\s+)?(\\w+)(\\s+\\*\\s*|\\s*\\*\\s+|\\s+)?(GLAPIENTRY|GL_APIENTRY|APIENTRY|EGLAPIENTRY|WINAPI)?(\\s*)([ew]?gl\\w+)\\s?(\\(.*)"); - protected static Pattern associationPattern = + protected static final Pattern associationPattern = Pattern.compile("\\#ifndef ([CEW]?GL[XU]?_[A-Za-z0-9_]+)(.*)"); - protected static int defineIdentifierGroup = 1; - protected static Pattern definePattern = + protected static final Pattern ifPattern = + Pattern.compile("\\#if(.*)"); + protected static final Pattern elsePattern = + Pattern.compile("\\#(elif|else)(.*)"); + protected static final Pattern endifPattern = + Pattern.compile("\\#endif(.*)"); + + protected static final int defineIdentifierGroup = 1; + protected static final Pattern definePattern = Pattern.compile("\\#define ([CEW]?GL[XU]?_[A-Za-z0-9_]+)\\s*([A-Za-z0-9_]+)(.*)"); // Maps function / #define names to Set of names of the extensions they're declared in @@ -131,41 +138,41 @@ public class BuildStaticGLInfo { * classes reside, and the remaining arguments are paths to the C header * files that should be parsed */ - public static void main(String[] args) throws IOException { + public static void main(final String[] args) throws IOException { if (args.length > 0 && args[0].equals("-test")) { - BuildStaticGLInfo builder = new BuildStaticGLInfo(); + final BuildStaticGLInfo builder = new BuildStaticGLInfo(); builder.setDebug(true); - String[] newArgs = new String[args.length - 1]; + final String[] newArgs = new String[args.length - 1]; System.arraycopy(args, 1, newArgs, 0, args.length - 1); builder.parse(newArgs); builder.dump(); System.exit(0); } - String packageName = args[0]; - String packageDir = args[1]; + final String packageName = args[0]; + final String packageDir = args[1]; - String[] cHeaderFilePaths = new String[args.length - 2]; + final String[] cHeaderFilePaths = new String[args.length - 2]; System.arraycopy(args, 2, cHeaderFilePaths, 0, cHeaderFilePaths.length); - BuildStaticGLInfo builder = new BuildStaticGLInfo(); + final BuildStaticGLInfo builder = new BuildStaticGLInfo(); try { builder.parse(cHeaderFilePaths); - File file = new File(packageDir + File.separatorChar + "StaticGLInfo.java"); - String parentDir = file.getParent(); + final File file = new File(packageDir + File.separatorChar + "StaticGLInfo.java"); + final String parentDir = file.getParent(); if (parentDir != null) { - File pDirFile = new File(parentDir); + final File pDirFile = new File(parentDir); pDirFile.mkdirs(); } - PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file))); + final PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file))); builder.emitJavaCode(writer, packageName); writer.flush(); writer.close(); - } catch (Exception e) { - StringBuilder buf = new StringBuilder("{ "); + } catch (final Exception e) { + final StringBuilder buf = new StringBuilder("{ "); for (int i = 0; i < cHeaderFilePaths.length; ++i) { buf.append(cHeaderFilePaths[i]); buf.append(" "); @@ -176,13 +183,13 @@ public class BuildStaticGLInfo { } } - public void setDebug(boolean v) { + public void setDebug(final boolean v) { DEBUG = v; } /** Parses the supplied C header files and adds the function associations contained therein to the internal map. */ - public void parse(String[] cHeaderFilePaths) throws IOException { + public void parse(final String[] cHeaderFilePaths) throws IOException { for (int i = 0; i < cHeaderFilePaths.length; i++) { parse(cHeaderFilePaths[i]); } @@ -190,42 +197,66 @@ public class BuildStaticGLInfo { /** Parses the supplied C header file and adds the function associations contained therein to the internal map. */ - public void parse(String cHeaderFilePath) throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(cHeaderFilePath)); + public void parse(final String cHeaderFilePath) throws IOException { + final BufferedReader reader = new BufferedReader(new FileReader(cHeaderFilePath)); String line, activeAssociation = null; Matcher m = null; + int block = 0; while ((line = reader.readLine()) != null) { int type = 0; // 1-define, 2-function - // see if we're inside a #ifndef GL_XXX block and matching a function - if (activeAssociation != null) { + if ( 0 < block ) { // inside a #ifndef GL_XXX block and matching a function, if block > 0 String identifier = null; - if ((m = funcPattern.matcher(line)).matches()) { - identifier = m.group(funcIdentifierGroup).trim(); - type = 2; - } else if ((m = definePattern.matcher(line)).matches()) { - identifier = m.group(defineIdentifierGroup).trim(); - type = 1; - } else if (line.startsWith("#endif")) { - if (DEBUG) { - System.err.println("END ASSOCIATION BLOCK: <" + activeAssociation + ">"); + if( 2 >= block ) { // not within sub-blocks > 2, i.e. further typedefs + if ((m = funcPattern.matcher(line)).matches()) { + identifier = m.group(funcIdentifierGroup).trim(); + type = 2; + } else if ((m = definePattern.matcher(line)).matches()) { + identifier = m.group(defineIdentifierGroup).trim(); + type = 1; } - activeAssociation = null; } - if ((identifier != null) - && (activeAssociation != null) - && // Handles #ifndef GL_... #define GL_... - !identifier.equals(activeAssociation)) { + if ( identifier != null && + activeAssociation != null && + !identifier.equals(activeAssociation) // Handles #ifndef GL_... #define GL_... + ) + { addAssociation(identifier, activeAssociation); if (DEBUG) { - System.err.println(" ADDING ASSOCIATION: <" + identifier + "> <" + activeAssociation + "> ; type " + type); + System.err.println("<"+block+"> ADDING ASSOCIATION: <" + identifier + "> <" + activeAssociation + "> ; type " + type); + } + } else { + if ((m = ifPattern.matcher(line)).matches()) { + final String comment = m.group(1).trim(); + block++; + if (DEBUG) { + System.err.println("<"+block+"> BEGIN IF BLOCK: <" + comment + ">"); + } + } else if ((m = elsePattern.matcher(line)).matches()) { + final String comment = m.group(1).trim(); + if (DEBUG) { + System.err.println("<"+block+"> ELSE BLOCK: <" + comment + ">"); + } + } else if ((m = endifPattern.matcher(line)).matches()) { + final String comment = m.group(1).trim(); + block--; + if( 0 == block ) { + if (DEBUG) { + System.err.println("<"+block+"> END ASSOCIATION BLOCK: <" + activeAssociation + " <-> " + comment + ">"); + } + activeAssociation = null; + } else { + if (DEBUG) { + System.err.println("<"+block+"> END IF BLOCK: <" + comment + ">"); + } + } } } } else if ((m = associationPattern.matcher(line)).matches()) { // found a new #ifndef GL_XXX block activeAssociation = m.group(1).trim(); - + block++; if (DEBUG) { - System.err.println("BEGIN ASSOCIATION BLOCK: <" + activeAssociation + ">"); + System.err.println("<"+block+"> BEGIN ASSOCIATION BLOCK: <" + activeAssociation + ">"); } } } @@ -233,23 +264,23 @@ public class BuildStaticGLInfo { } public void dump() { - for (String name : extensionToDeclarationMap.keySet()) { - Set<String> decls = extensionToDeclarationMap.get(name); + for (final String name : extensionToDeclarationMap.keySet()) { + final Set<String> decls = extensionToDeclarationMap.get(name); System.out.println("<" + name + "> :"); - List<String> l = new ArrayList<String>(); + final List<String> l = new ArrayList<String>(); l.addAll(decls); Collections.sort(l); - for (String str : l) { + for (final String str : l) { System.out.println(" <" + str + ">"); } } } - public Set<String> getExtension(String identifier) { + public Set<String> getExtension(final String identifier) { return declarationToExtensionMap.get(identifier); } - public Set<String> getDeclarations(String extension) { + public Set<String> getDeclarations(final String extension) { return extensionToDeclarationMap.get(extension); } @@ -257,7 +288,7 @@ public class BuildStaticGLInfo { return extensionToDeclarationMap.keySet(); } - public void emitJavaCode(PrintWriter output, String packageName) { + public void emitJavaCode(final PrintWriter output, final String packageName) { output.println("package " + packageName + ";"); output.println(); output.println("import java.util.*;"); @@ -309,7 +340,7 @@ public class BuildStaticGLInfo { // Compute max capacity int maxCapacity = 0; - for (String name : declarationToExtensionMap.keySet()) { + for (final String name : declarationToExtensionMap.keySet()) { if (!name.startsWith("GL")) { ++maxCapacity; } @@ -317,17 +348,17 @@ public class BuildStaticGLInfo { output.println(" funcToAssocMap = new HashMap(" + maxCapacity + "); // approximate max capacity"); output.println(" String group;"); - ArrayList<String> sets = new ArrayList<String>(extensionToDeclarationMap.keySet()); + final ArrayList<String> sets = new ArrayList<String>(extensionToDeclarationMap.keySet()); Collections.sort(sets); - for (String groupName : sets) { - Set<String> funcs = extensionToDeclarationMap.get(groupName); - List<String> l = new ArrayList<String>(); + for (final String groupName : sets) { + final Set<String> funcs = extensionToDeclarationMap.get(groupName); + final List<String> l = new ArrayList<String>(); l.addAll(funcs); Collections.sort(l); - Iterator<String> funcIter = l.iterator(); + final Iterator<String> funcIter = l.iterator(); boolean printedHeader = false; while (funcIter.hasNext()) { - String funcName = funcIter.next(); + final String funcName = funcIter.next(); if (!funcName.startsWith("GL")) { if (!printedHeader) { output.println(); @@ -349,14 +380,14 @@ public class BuildStaticGLInfo { //---------------------------------------------------------------------- // Internals only below this point // - protected void addAssociation(String identifier, String association) { + protected void addAssociation(final String identifier, final String association) { Set<String> extensions = declarationToExtensionMap.get(identifier); if(null == extensions) { extensions = new HashSet<String>(); declarationToExtensionMap.put(identifier, extensions); } extensions.add(association); - + Set<String> identifiers = extensionToDeclarationMap.get(association); if (identifiers == null) { identifiers = new HashSet<String>(); diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/GLConfiguration.java b/src/jogl/classes/com/jogamp/gluegen/opengl/GLConfiguration.java index ba025e18c..5da45abfe 100755..100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/GLConfiguration.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/GLConfiguration.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -59,15 +59,17 @@ import java.util.StringTokenizer; public class GLConfiguration extends ProcAddressConfiguration { // The following data members support ignoring an entire extension at a time - private List<String> glHeaders = new ArrayList<String>(); - private Set<String> ignoredExtensions = new HashSet<String>(); - private Set<String> extensionsRenamedIntoCore = new HashSet<String>(); + private final List<String> glHeaders = new ArrayList<String>(); + private final Set<String> ignoredExtensions = new HashSet<String>(); + private final Set<String> forcedExtensions = new HashSet<String>(); + private final Set<String> extensionsRenamedIntoCore = new HashSet<String>(); private BuildStaticGLInfo glInfo; // Maps function names to the kind of buffer object it deals with - private Map<String, GLEmitter.BufferObjectKind> bufferObjectKinds = new HashMap<String, GLEmitter.BufferObjectKind>(); - private GLEmitter emitter; - private Set<String> dropUniqVendorExtensions = new HashSet<String>(); + private final Map<String, GLEmitter.BufferObjectKind> bufferObjectKinds = new HashMap<String, GLEmitter.BufferObjectKind>(); + private final Set<String> bufferObjectOnly = new HashSet<String>(); + private final GLEmitter emitter; + private final Set<String> dropUniqVendorExtensions = new HashSet<String>(); // This directive is off by default but can help automatically // indicate which extensions have been folded into the core OpenGL @@ -75,46 +77,52 @@ public class GLConfiguration extends ProcAddressConfiguration { private boolean autoUnifyExtensions = false; private boolean allowNonGLExtensions = false; - public GLConfiguration(GLEmitter emitter) { + public GLConfiguration(final GLEmitter emitter) { super(); this.emitter = emitter; try { setProcAddressNameExpr("PFN $UPPERCASE({0}) PROC"); - } catch (NoSuchElementException e) { + } catch (final NoSuchElementException e) { throw new RuntimeException("Error configuring ProcAddressNameExpr", e); } } @Override - protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { + protected void dispatch(final String cmd, final StringTokenizer tok, final File file, final String filename, final int lineNo) throws IOException { if (cmd.equalsIgnoreCase("IgnoreExtension")) { - String sym = readString("IgnoreExtension", tok, filename, lineNo); + final String sym = readString("IgnoreExtension", tok, filename, lineNo); ignoredExtensions.add(sym); + } else if (cmd.equalsIgnoreCase("ForceExtension")) { + final String sym = readString("ForceExtension", tok, filename, lineNo); + forcedExtensions.add(sym); } else if (cmd.equalsIgnoreCase("RenameExtensionIntoCore")) { - String sym = readString("RenameExtensionIntoCore", tok, filename, lineNo); + final String sym = readString("RenameExtensionIntoCore", tok, filename, lineNo); extensionsRenamedIntoCore.add(sym); } else if (cmd.equalsIgnoreCase("AllowNonGLExtensions")) { allowNonGLExtensions = readBoolean("AllowNonGLExtensions", tok, filename, lineNo).booleanValue(); } else if (cmd.equalsIgnoreCase("AutoUnifyExtensions")) { autoUnifyExtensions = readBoolean("AutoUnifyExtensions", tok, filename, lineNo).booleanValue(); } else if (cmd.equalsIgnoreCase("GLHeader")) { - String sym = readString("GLHeader", tok, filename, lineNo); + final String sym = readString("GLHeader", tok, filename, lineNo); glHeaders.add(sym); } else if (cmd.equalsIgnoreCase("BufferObjectKind")) { readBufferObjectKind(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("BufferObjectOnly")) { + final String sym = readString("BufferObjectOnly", tok, filename, lineNo); + bufferObjectOnly.add(sym); } else if (cmd.equalsIgnoreCase("DropUniqVendorExtensions")) { - String sym = readString("DropUniqVendorExtensions", tok, filename, lineNo); + final String sym = readString("DropUniqVendorExtensions", tok, filename, lineNo); dropUniqVendorExtensions.add(sym); } else { super.dispatch(cmd, tok, file, filename, lineNo); } } - protected void readBufferObjectKind(StringTokenizer tok, String filename, int lineNo) { + protected void readBufferObjectKind(final StringTokenizer tok, final String filename, final int lineNo) { try { - String kindString = tok.nextToken(); + final String kindString = tok.nextToken(); GLEmitter.BufferObjectKind kind = null; - String target = tok.nextToken(); + final String target = tok.nextToken(); if (kindString.equalsIgnoreCase("UnpackPixel")) { kind = GLEmitter.BufferObjectKind.UNPACK_PIXEL; } else if (kindString.equalsIgnoreCase("PackPixel")) { @@ -123,14 +131,16 @@ public class GLConfiguration extends ProcAddressConfiguration { kind = GLEmitter.BufferObjectKind.ARRAY; } else if (kindString.equalsIgnoreCase("Element")) { kind = GLEmitter.BufferObjectKind.ELEMENT; + } else if (kindString.equalsIgnoreCase("Indirect")) { + kind = GLEmitter.BufferObjectKind.INDIRECT; } else { throw new RuntimeException("Error parsing \"BufferObjectKind\" command at line " + lineNo + " in file \"" + filename + "\": illegal BufferObjectKind \"" - + kindString + "\", expected one of UnpackPixel, PackPixel, Array, or Element"); + + kindString + "\", expected one of UnpackPixel, PackPixel, Array, Element or Indirect"); } bufferObjectKinds.put(target, kind); - } catch (NoSuchElementException e) { + } catch (final NoSuchElementException e) { throw new RuntimeException("Error parsing \"BufferObjectKind\" command at line " + lineNo + " in file \"" + filename + "\"", e); } @@ -140,10 +150,10 @@ public class GLConfiguration extends ProcAddressConfiguration { automatically generates prologue code for functions associated with buffer objects. */ @Override - public List<String> javaPrologueForMethod(MethodBinding binding, boolean forImplementingMethodCall, boolean eraseBufferAndArrayTypes) { + public List<String> javaPrologueForMethod(final MethodBinding binding, final boolean forImplementingMethodCall, final boolean eraseBufferAndArrayTypes) { List<String> res = super.javaPrologueForMethod(binding, forImplementingMethodCall, eraseBufferAndArrayTypes); - GLEmitter.BufferObjectKind kind = getBufferObjectKind(binding.getName()); + final GLEmitter.BufferObjectKind kind = getBufferObjectKind(binding.getName()); if (kind != null) { // Need to generate appropriate prologue based on both buffer // object kind and whether this variant of the MethodBinding @@ -151,7 +161,7 @@ public class GLConfiguration extends ProcAddressConfiguration { // // NOTE we MUST NOT mutate the array returned from the super // call! - ArrayList<String> res2 = new ArrayList<String>(); + final ArrayList<String> res2 = new ArrayList<String>(); if (res != null) { res2.addAll(res); } @@ -167,14 +177,16 @@ public class GLConfiguration extends ProcAddressConfiguration { prologue = prologue + "ArrayVBO"; } else if (kind == GLEmitter.BufferObjectKind.ELEMENT) { prologue = prologue + "ElementVBO"; + } else if (kind == GLEmitter.BufferObjectKind.INDIRECT) { + prologue = prologue + "IndirectVBO"; } else { throw new RuntimeException("Unknown BufferObjectKind " + kind); } if (emitter.isBufferObjectMethodBinding(binding)) { - prologue = prologue + "Enabled"; + prologue = prologue + "Bound"; } else { - prologue = prologue + "Disabled"; + prologue = prologue + "Unbound"; } prologue = prologue + "(true);"; @@ -184,8 +196,8 @@ public class GLConfiguration extends ProcAddressConfiguration { // Must also filter out bogus rangeCheck directives for VBO/PBO // variants if (emitter.isBufferObjectMethodBinding(binding)) { - for (Iterator<String> iter = res.iterator(); iter.hasNext();) { - String line = iter.next(); + for (final Iterator<String> iter = res.iterator(); iter.hasNext();) { + final String line = iter.next(); if (line.indexOf("Buffers.rangeCheck") >= 0) { iter.remove(); } @@ -199,19 +211,24 @@ public class GLConfiguration extends ProcAddressConfiguration { @Override public void dumpIgnores() { System.err.println("GL Ignored extensions: "); - for (String str : ignoredExtensions) { + for (final String str : ignoredExtensions) { + System.err.println("\t" + str); + } + System.err.println("GL Forced extensions: "); + for (final String str : forcedExtensions) { System.err.println("\t" + str); } super.dumpIgnores(); } - protected boolean shouldIgnoreExtension(String symbol, boolean criteria) { + protected boolean shouldIgnoreExtension(final String symbol, final boolean criteria) { if (criteria && glInfo != null) { - Set<String> extensionNames = glInfo.getExtension(symbol); - if(null!=extensionNames) { - for(Iterator<String> i=extensionNames.iterator(); i.hasNext(); ) { - String extensionName = i.next(); - if (extensionName != null && ignoredExtensions.contains(extensionName)) { + final Set<String> extensionNames = glInfo.getExtension(symbol); + if( null != extensionNames ) { + boolean ignoredExtension = false; + for(final Iterator<String> i=extensionNames.iterator(); !ignoredExtension && i.hasNext(); ) { + final String extensionName = i.next(); + if ( extensionName != null && ignoredExtensions.contains(extensionName) ) { if (DEBUG_IGNORES) { System.err.print("Ignore symbol <" + symbol + "> of extension <" + extensionName + ">"); if(extensionNames.size()==1) { @@ -220,15 +237,32 @@ public class GLConfiguration extends ProcAddressConfiguration { System.err.println(", WARNING MULTIPLE OCCURENCE: "+extensionNames); } } - return true; + ignoredExtension = true; + } + } + if( ignoredExtension ) { + ignoredExtension = !shouldForceExtension( symbol, true, symbol ); + if( ignoredExtension ) { + final Set<String> origSymbols = getRenamedJavaSymbols( symbol ); + if(null != origSymbols) { + for(final String origSymbol : origSymbols) { + if( shouldForceExtension( origSymbol, true, symbol ) ) { + ignoredExtension = false; + break; + } + } + } } } + if( ignoredExtension ) { + return true; + } } - boolean isGLEnum = GLNameResolver.isGLEnumeration(symbol); - boolean isGLFunc = GLNameResolver.isGLFunction(symbol); + final boolean isGLEnum = GLNameResolver.isGLEnumeration(symbol); + final boolean isGLFunc = GLNameResolver.isGLFunction(symbol); if (isGLFunc || isGLEnum) { if (GLNameResolver.isExtensionVEN(symbol, isGLFunc)) { - String extSuffix = GLNameResolver.getExtensionSuffix(symbol, isGLFunc); + final String extSuffix = GLNameResolver.getExtensionSuffix(symbol, isGLFunc); if (getDropUniqVendorExtensions(extSuffix)) { if (DEBUG_IGNORES) { System.err.println("Ignore UniqVendorEXT: " + symbol + ", vendor " + extSuffix); @@ -241,21 +275,44 @@ public class GLConfiguration extends ProcAddressConfiguration { return false; } + public boolean shouldForceExtension(final String symbol, final boolean criteria, final String renamedSymbol) { + if (criteria && glInfo != null) { + final Set<String> extensionNames = glInfo.getExtension(symbol); + if( null != extensionNames ) { + for(final Iterator<String> i=extensionNames.iterator(); i.hasNext(); ) { + final String extensionName = i.next(); + if ( extensionName != null && forcedExtensions.contains(extensionName) ) { + if (DEBUG_IGNORES) { + System.err.print("Not Ignore symbol <" + symbol + " -> " + renamedSymbol + "> of extension <" + extensionName + ">"); + if(extensionNames.size()==1) { + System.err.println(", single ."); + } else { + System.err.println(", WARNING MULTIPLE OCCURENCE: "+extensionNames); + } + } + return true; + } + } + } + } + return false; + } + @Override - public boolean shouldIgnoreInInterface(String symbol) { + public boolean shouldIgnoreInInterface(final String symbol) { return shouldIgnoreInInterface(symbol, true); } - public boolean shouldIgnoreInInterface(String symbol, boolean checkEXT) { + public boolean shouldIgnoreInInterface(final String symbol, final boolean checkEXT) { return shouldIgnoreExtension(symbol, checkEXT) || super.shouldIgnoreInInterface(symbol); } @Override - public boolean shouldIgnoreInImpl(String symbol) { + public boolean shouldIgnoreInImpl(final String symbol) { return shouldIgnoreInImpl(symbol, true); } - public boolean shouldIgnoreInImpl(String symbol, boolean checkEXT) { + public boolean shouldIgnoreInImpl(final String symbol, final boolean checkEXT) { return shouldIgnoreExtension(symbol, checkEXT) || super.shouldIgnoreInImpl(symbol); } @@ -273,28 +330,32 @@ public class GLConfiguration extends ProcAddressConfiguration { } /** shall the non unified (uniq) vendor extensions be dropped ? */ - public boolean getDropUniqVendorExtensions(String extName) { + public boolean getDropUniqVendorExtensions(final String extName) { return dropUniqVendorExtensions.contains(extName); } /** Returns the kind of buffer object this function deals with, or null if none. */ - GLEmitter.BufferObjectKind getBufferObjectKind(String name) { + GLEmitter.BufferObjectKind getBufferObjectKind(final String name) { return bufferObjectKinds.get(name); } - public boolean isBufferObjectFunction(String name) { + public boolean isBufferObjectFunction(final String name) { return (getBufferObjectKind(name) != null); } + public boolean isBufferObjectOnly(final String name) { + return bufferObjectOnly.contains(name); + } + /** Parses any GL headers specified in the configuration file for the purpose of being able to ignore an extension at a time. */ - public void parseGLHeaders(GlueEmitterControls controls) throws IOException { + public void parseGLHeaders(final GlueEmitterControls controls) throws IOException { if (!glHeaders.isEmpty()) { glInfo = new BuildStaticGLInfo(); glInfo.setDebug(GlueGen.debug()); - for (String file : glHeaders) { - String fullPath = controls.findHeaderFile(file); + for (final String file : glHeaders) { + final String fullPath = controls.findHeaderFile(file); if (fullPath == null) { throw new IOException("Unable to locate header file \"" + file + "\""); } diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java b/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java index 809c6783d..bc230c83a 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -72,12 +72,12 @@ public class GLEmitter extends ProcAddressEmitter { // Keeps track of which MethodBindings were created for handling // Buffer Object variants. Used as a Set rather than a Map. - private Map<MethodBinding, MethodBinding> bufferObjectMethodBindings = new IdentityHashMap<MethodBinding, MethodBinding>(); + private final Map<MethodBinding, MethodBinding> bufferObjectMethodBindings = new IdentityHashMap<MethodBinding, MethodBinding>(); - enum BufferObjectKind { UNPACK_PIXEL, PACK_PIXEL, ARRAY, ELEMENT} + enum BufferObjectKind { UNPACK_PIXEL, PACK_PIXEL, ARRAY, ELEMENT, INDIRECT} @Override - public void beginEmission(GlueEmitterControls controls) throws IOException { + public void beginEmission(final GlueEmitterControls controls) throws IOException { getGLConfig().parseGLHeaders(controls); renameExtensionsIntoCore(); if (getGLConfig().getAutoUnifyExtensions()) { @@ -93,30 +93,30 @@ public class GLEmitter extends ProcAddressEmitter { // already in the core namespace in desktop OpenGL. It builds upon // renaming mechanisms that are built elsewhere. - GLConfiguration config = getGLConfig(); - Set<String> extensionsRenamedIntoCore = config.getExtensionsRenamedIntoCore(); - BuildStaticGLInfo glInfo = config.getGLInfo(); + final GLConfiguration config = getGLConfig(); + final Set<String> extensionsRenamedIntoCore = config.getExtensionsRenamedIntoCore(); + final BuildStaticGLInfo glInfo = config.getGLInfo(); if (null == glInfo) { if (extensionsRenamedIntoCore.size() > 0) { throw new RuntimeException("ExtensionRenamedIntoCore (num: " + extensionsRenamedIntoCore.size() + "), but no GLHeader"); } return; } - for (String extension : extensionsRenamedIntoCore) { + for (final String extension : extensionsRenamedIntoCore) { if(JavaConfiguration.DEBUG_RENAMES) { System.err.println("<RenameExtensionIntoCore: "+extension+" BEGIN"); } - Set<String> declarations = glInfo.getDeclarations(extension); + final Set<String> declarations = glInfo.getDeclarations(extension); if (declarations != null) { - for (Iterator<String> iterator = declarations.iterator(); iterator.hasNext();) { - String decl = iterator.next(); - boolean isGLFunction = GLNameResolver.isGLFunction(decl); + for (final Iterator<String> iterator = declarations.iterator(); iterator.hasNext();) { + final String decl = iterator.next(); + final boolean isGLFunction = GLNameResolver.isGLFunction(decl); boolean isGLEnumeration = false; if (!isGLFunction) { isGLEnumeration = GLNameResolver.isGLEnumeration(decl); } if (isGLFunction || isGLEnumeration) { - String renamed = GLNameResolver.normalize(decl, isGLFunction); + final String renamed = GLNameResolver.normalize(decl, isGLFunction); if (!renamed.equals(decl)) { config.addJavaSymbolRename(decl, renamed); } @@ -125,7 +125,7 @@ public class GLEmitter extends ProcAddressEmitter { } if(JavaConfiguration.DEBUG_RENAMES) { System.err.println("RenameExtensionIntoCore: "+extension+" END>"); - } + } } } @@ -134,33 +134,36 @@ public class GLEmitter extends ProcAddressEmitter { private List<ConstantDefinition> constants; private List<FunctionSymbol> functions; - public void filterSymbols(List<ConstantDefinition> constants, - List<FunctionSymbol> functions) { + @Override + public void filterSymbols(final List<ConstantDefinition> constants, + final List<FunctionSymbol> functions) { this.constants = constants; this.functions = functions; doWork(); } + @Override public List<ConstantDefinition> getConstants() { return constants; } + @Override public List<FunctionSymbol> getFunctions() { return functions; } private void doWork() { - BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); + final BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); if (glInfo == null) { return; } // Try to retain a "good" ordering for these symbols - Map<String, ConstantDefinition> constantMap = new LinkedHashMap<String, ConstantDefinition>(); - for (ConstantDefinition def : constants) { + final Map<String, ConstantDefinition> constantMap = new LinkedHashMap<String, ConstantDefinition>(); + for (final ConstantDefinition def : constants) { constantMap.put(def.getName(), def); } - Map<String, FunctionSymbol> functionMap = new LinkedHashMap<String, FunctionSymbol>(); - for (FunctionSymbol sym : functions) { + final Map<String, FunctionSymbol> functionMap = new LinkedHashMap<String, FunctionSymbol>(); + for (final FunctionSymbol sym : functions) { functionMap.put(sym.getName(), sym); } @@ -172,15 +175,15 @@ public class GLEmitter extends ProcAddressEmitter { // that doesn't support the core version of these APIs, the runtime // will take care of looking up the extension version of these entry // points. - Set<String> extensionNames = glInfo.getExtensions(); + final Set<String> extensionNames = glInfo.getExtensions(); - for (String extension : extensionNames) { - Set<String> declarations = glInfo.getDeclarations(extension); + for (final String extension : extensionNames) { + final Set<String> declarations = glInfo.getDeclarations(extension); boolean isExtension = true; boolean shouldUnify = true; String cause = null; - for (String decl : declarations) { - boolean isFunc = !decl.startsWith("GL_"); + for (final String decl : declarations) { + final boolean isFunc = !decl.startsWith("GL_"); if (!GLNameResolver.isExtension(decl, isFunc)) { isExtension = false; break; @@ -199,7 +202,7 @@ public class GLEmitter extends ProcAddressEmitter { } } cause = decl; - String unifiedName = GLNameResolver.normalize(decl, isFunc); + final String unifiedName = GLNameResolver.normalize(decl, isFunc); // NOTE that we look up the unified name in the // BuildStaticGLInfo's notion of the APIs -- since // we might not be emitting glue code for the @@ -222,8 +225,8 @@ public class GLEmitter extends ProcAddressEmitter { } if (isExtension) { if (shouldUnify) { - for (String decl : declarations) { - boolean isFunc = !decl.startsWith("GL_"); + for (final String decl : declarations) { + final boolean isFunc = !decl.startsWith("GL_"); if (isFunc) { functionMap.remove(decl); } else { @@ -241,7 +244,7 @@ public class GLEmitter extends ProcAddressEmitter { } } - private void unifyExtensions(GlueEmitterControls controls) { + private void unifyExtensions(final GlueEmitterControls controls) { controls.runSymbolFilter(new ExtensionUnifier()); } @@ -261,19 +264,24 @@ public class GLEmitter extends ProcAddressEmitter { inform the CMethodBindingEmitter that it is overloaded in this case (though we default to true currently). */ @Override - protected List<MethodBinding> expandMethodBinding(MethodBinding binding) { - List<MethodBinding> bindings = super.expandMethodBinding(binding); + protected List<MethodBinding> expandMethodBinding(final MethodBinding binding) { + final GLConfiguration glConfig = getGLConfig(); + final List<MethodBinding> bindings = super.expandMethodBinding(binding); - if (!getGLConfig().isBufferObjectFunction(binding.getName())) { + if ( !glConfig.isBufferObjectFunction(binding.getName()) ) { return bindings; } + final boolean bufferObjectOnly = glConfig.isBufferObjectOnly(binding.getName()); - List<MethodBinding> newBindings = new ArrayList<MethodBinding>(bindings); + final List<MethodBinding> newBindings = new ArrayList<MethodBinding>(); // Need to expand each one of the generated bindings to take a // Java long instead of a Buffer for each void* argument - for (MethodBinding cur : bindings) { + // for (MethodBinding cur : bindings) { + int j=0; + while( j < bindings.size() ) { + final MethodBinding cur = bindings.get(j); // Some of these routines (glBitmap) take strongly-typed // primitive pointers as arguments which are expanded into @@ -281,6 +289,7 @@ public class GLEmitter extends ProcAddressEmitter { // This test (rather than !signatureUsesNIO) is used to catch // more unexpected situations if (cur.signatureUsesJavaPrimitiveArrays()) { + j++; continue; } @@ -300,13 +309,20 @@ public class GLEmitter extends ProcAddressEmitter { // Now need to flag this MethodBinding so that we generate the // correct flags in the emitters later bufferObjectMethodBindings.put(result, result); + + if( bufferObjectOnly ) { + bindings.remove(j); + } else { + j++; + } } + bindings.addAll(newBindings); - return newBindings; + return bindings; } @Override - protected boolean needsModifiedEmitters(FunctionSymbol sym) { + protected boolean needsModifiedEmitters(final FunctionSymbol sym) { if ((!needsProcAddressWrapper(sym) && !needsBufferObjectVariant(sym)) || getConfig().isUnimplemented(sym.getName())) { return false; @@ -315,22 +331,22 @@ public class GLEmitter extends ProcAddressEmitter { return true; } - public boolean isBufferObjectMethodBinding(MethodBinding binding) { + public boolean isBufferObjectMethodBinding(final MethodBinding binding) { return bufferObjectMethodBindings.containsKey(binding); } @Override - public void emitDefine(ConstantDefinition def, String optionalComment) throws Exception { - BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); + public void emitDefine(final ConstantDefinition def, final String optionalComment) throws Exception { + final BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); if (null == glInfo) { throw new Exception("No GLInfo for: " + def); } - String symbolRenamed = def.getName(); - StringBuilder newComment = new StringBuilder(); + final String symbolRenamed = def.getName(); + final StringBuilder newComment = new StringBuilder(); newComment.append("Part of "); if (0 == addExtensionsOfSymbols2Buffer(newComment, ", ", "; ", symbolRenamed, def.getAliasedNames())) { if (def.isEnum()) { - String enumName = def.getEnumName(); + final String enumName = def.getEnumName(); if (null != enumName) { newComment.append(enumName); } else { @@ -346,7 +362,7 @@ public class GLEmitter extends ProcAddressEmitter { // ... // #endif if (JavaConfiguration.DEBUG_IGNORES) { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); JavaEmitter.addStrings2Buffer(sb, ", ", symbolRenamed, def.getAliasedNames()); System.err.println("Dropping marker: " + sb.toString()); } @@ -363,12 +379,12 @@ public class GLEmitter extends ProcAddressEmitter { super.emitDefine(def, newComment.toString()); } - private int addExtensionListOfSymbol2Buffer(BuildStaticGLInfo glInfo, StringBuilder buf, String sep1, String name) { + private int addExtensionListOfSymbol2Buffer(final BuildStaticGLInfo glInfo, final StringBuilder buf, final String sep1, final String name) { int num = 0; - Set<String> extensionNames = glInfo.getExtension(name); + final Set<String> extensionNames = glInfo.getExtension(name); if(null!=extensionNames) { - for(Iterator<String> i=extensionNames.iterator(); i.hasNext(); ) { - String extensionName = i.next(); + for(final Iterator<String> i=extensionNames.iterator(); i.hasNext(); ) { + final String extensionName = i.next(); if (null != extensionName) { buf.append("<code>"); buf.append(extensionName); @@ -382,19 +398,19 @@ public class GLEmitter extends ProcAddressEmitter { } return num; } - private int addExtensionListOfAliasedSymbols2Buffer(BuildStaticGLInfo glInfo, StringBuilder buf, String sep1, String sep2, String name, Collection<String> exclude) { + private int addExtensionListOfAliasedSymbols2Buffer(final BuildStaticGLInfo glInfo, final StringBuilder buf, final String sep1, final String sep2, final String name, final Collection<String> exclude) { int num = 0; - if(null != name) { + if(null != name) { num += addExtensionListOfSymbol2Buffer(glInfo, buf, sep1, name); // extensions of given name boolean needsSep2 = 0<num; - Set<String> origNames = cfg.getRenamedJavaSymbols(name); + final Set<String> origNames = cfg.getRenamedJavaSymbols(name); if(null != origNames) { - for(String origName : origNames) { + for(final String origName : origNames) { if(!exclude.contains(origName)) { if (needsSep2) { buf.append(sep2); // diff-name seperator - } - int num2 = addExtensionListOfSymbol2Buffer(glInfo, buf, sep1, origName); // extensions of orig-name + } + final int num2 = addExtensionListOfSymbol2Buffer(glInfo, buf, sep1, origName); // extensions of orig-name needsSep2 = num<num2; num += num2; } @@ -403,9 +419,9 @@ public class GLEmitter extends ProcAddressEmitter { } return num; } - - public int addExtensionsOfSymbols2Buffer(StringBuilder buf, String sep1, String sep2, String first, Collection<String> col) { - BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); + + public int addExtensionsOfSymbols2Buffer(StringBuilder buf, final String sep1, final String sep2, final String first, final Collection<String> col) { + final BuildStaticGLInfo glInfo = getGLConfig().getGLInfo(); if (null == glInfo) { throw new RuntimeException("No GLInfo for: " + first); } @@ -416,11 +432,11 @@ public class GLEmitter extends ProcAddressEmitter { num += addExtensionListOfAliasedSymbols2Buffer(glInfo, buf, sep1, sep2, first, col); boolean needsSep2 = 0<num; - for(Iterator<String> iter = col.iterator(); iter.hasNext(); ) { + for(final Iterator<String> iter = col.iterator(); iter.hasNext(); ) { if(needsSep2) { buf.append(sep2); // diff-name seperator } - int num2 = addExtensionListOfAliasedSymbols2Buffer(glInfo, buf, sep1, sep2, iter.next(), col); + final int num2 = addExtensionListOfAliasedSymbols2Buffer(glInfo, buf, sep1, sep2, iter.next(), col); needsSep2 = num<num2; num += num2; } @@ -431,12 +447,12 @@ public class GLEmitter extends ProcAddressEmitter { // Internals only below this point // @Override - protected void generateModifiedEmitters(JavaMethodBindingEmitter baseJavaEmitter, List<FunctionEmitter> emitters) { - List<FunctionEmitter> superEmitters = new ArrayList<FunctionEmitter>(); + protected void generateModifiedEmitters(final JavaMethodBindingEmitter baseJavaEmitter, final List<FunctionEmitter> emitters) { + final List<FunctionEmitter> superEmitters = new ArrayList<FunctionEmitter>(); super.generateModifiedEmitters(baseJavaEmitter, superEmitters); // See whether this is one of the Buffer Object variants - boolean bufferObjectVariant = bufferObjectMethodBindings.containsKey(baseJavaEmitter.getBinding()); + final boolean bufferObjectVariant = bufferObjectMethodBindings.containsKey(baseJavaEmitter.getBinding()); for (FunctionEmitter emitter : superEmitters) { if (emitter instanceof ProcAddressJavaMethodBindingEmitter) { @@ -446,7 +462,7 @@ public class GLEmitter extends ProcAddressEmitter { } } - protected boolean needsBufferObjectVariant(FunctionSymbol sym) { + protected boolean needsBufferObjectVariant(final FunctionSymbol sym) { return getGLConfig().isBufferObjectFunction(sym.getName()); } @@ -454,34 +470,69 @@ public class GLEmitter extends ProcAddressEmitter { return (GLConfiguration) getConfig(); } + /** + * {@inheritDoc} + */ @Override protected void endProcAddressTable() throws Exception { - PrintWriter w = tableWriter; - - w.println(" /**"); - w.println(" * This is a convenience method to get (by name) the native function"); - w.println(" * pointer for a given function. It lets you avoid having to"); - w.println(" * manually compute the "" + PROCADDRESS_VAR_PREFIX + " + "); - w.println(" * <functionName>" member variable name and look it up via"); - w.println(" * reflection; it also will throw an exception if you try to get the"); - w.println(" * address of an unknown function, or one that is statically linked"); - w.println(" * and therefore does not have a function pointer in this table."); - w.println(" *"); - w.println(" * @throws RuntimeException if the function pointer was not found in"); - w.println(" * this table, either because the function was unknown or because"); - w.println(" * it was statically linked."); - w.println(" */"); - w.println(" public long getAddressFor(String functionNameUsr) {"); - w.println(" String functionNameBase = "+GLNameResolver.class.getName()+".normalizeVEN(com.jogamp.gluegen.runtime.opengl.GLNameResolver.normalizeARB(functionNameUsr, true), true);"); - w.println(" String addressFieldNameBase = PROCADDRESS_VAR_PREFIX + functionNameBase;"); - w.println(" java.lang.reflect.Field addressField = null;"); - w.println(" int funcNamePermNum = "+GLNameResolver.class.getName()+".getFuncNamePermutationNumber(functionNameBase);"); - w.println(" for(int i = 0; null==addressField && i < funcNamePermNum; i++) {"); - w.println(" String addressFieldName = "+GLNameResolver.class.getName()+".getFuncNamePermutation(addressFieldNameBase, i);"); - w.println(" try {"); - w.println(" addressField = getClass().getField(addressFieldName);"); - w.println(" } catch (Exception e) { }"); + final PrintWriter w = tableWriter; + + w.println(" @Override"); + w.println(" protected boolean isFunctionAvailableImpl(String functionNameUsr) throws IllegalArgumentException {"); + w.println(" final String functionNameBase = "+GLNameResolver.class.getName()+".normalizeVEN(com.jogamp.gluegen.runtime.opengl.GLNameResolver.normalizeARB(functionNameUsr, true), true);"); + w.println(" final String addressFieldNameBase = \"" + PROCADDRESS_VAR_PREFIX + "\" + functionNameBase;"); + w.println(" final int funcNamePermNum = "+GLNameResolver.class.getName()+".getFuncNamePermutationNumber(functionNameBase);"); + w.println(" final java.lang.reflect.Field addressField = java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<java.lang.reflect.Field>() {"); + w.println(" public final java.lang.reflect.Field run() {"); + w.println(" java.lang.reflect.Field addressField = null;"); + w.println(" for(int i = 0; i < funcNamePermNum; i++) {"); + w.println(" final String addressFieldName = "+GLNameResolver.class.getName()+".getFuncNamePermutation(addressFieldNameBase, i);"); + w.println(" try {"); + w.println(" addressField = "+tableClassName+".class.getDeclaredField( addressFieldName );"); + w.println(" addressField.setAccessible(true); // we need to read the protected value!"); + w.println(" return addressField;"); + w.println(" } catch (NoSuchFieldException ex) { }"); + w.println(" }"); + w.println(" return null;"); + w.println(" } } );"); + w.println(); + w.println(" if(null==addressField) {"); + w.println(" // The user is calling a bogus function or one which is not"); + w.println(" // runtime linked"); + w.println(" throw new RuntimeException("); + w.println(" \"WARNING: Address field query failed for \\\"\" + functionNameBase + \"\\\"/\\\"\" + functionNameUsr +"); + w.println(" \"\\\"; it's either statically linked or address field is not a known \" +"); + w.println(" \"function\");"); + w.println(" } "); + w.println(" try {"); + w.println(" return 0 != addressField.getLong(this);"); + w.println(" } catch (Exception e) {"); + w.println(" throw new RuntimeException("); + w.println(" \"WARNING: Address query failed for \\\"\" + functionNameBase + \"\\\"/\\\"\" + functionNameUsr +"); + w.println(" \"\\\"; it's either statically linked or is not a known \" +"); + w.println(" \"function\", e);"); w.println(" }"); + w.println(" }"); + + w.println(" @Override"); + w.println(" public long getAddressFor(String functionNameUsr) throws SecurityException, IllegalArgumentException {"); + w.println(" SecurityUtil.checkAllLinkPermission();"); + w.println(" final String functionNameBase = "+GLNameResolver.class.getName()+".normalizeVEN(com.jogamp.gluegen.runtime.opengl.GLNameResolver.normalizeARB(functionNameUsr, true), true);"); + w.println(" final String addressFieldNameBase = \"" + PROCADDRESS_VAR_PREFIX + "\" + functionNameBase;"); + w.println(" final int funcNamePermNum = "+GLNameResolver.class.getName()+".getFuncNamePermutationNumber(functionNameBase);"); + w.println(" final java.lang.reflect.Field addressField = java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<java.lang.reflect.Field>() {"); + w.println(" public final java.lang.reflect.Field run() {"); + w.println(" java.lang.reflect.Field addressField = null;"); + w.println(" for(int i = 0; i < funcNamePermNum; i++) {"); + w.println(" final String addressFieldName = "+GLNameResolver.class.getName()+".getFuncNamePermutation(addressFieldNameBase, i);"); + w.println(" try {"); + w.println(" addressField = "+tableClassName+".class.getDeclaredField( addressFieldName );"); + w.println(" addressField.setAccessible(true); // we need to read the protected value!"); + w.println(" return addressField;"); + w.println(" } catch (NoSuchFieldException ex) { }"); + w.println(" }"); + w.println(" return null;"); + w.println(" } } );"); w.println(); w.println(" if(null==addressField) {"); w.println(" // The user is calling a bogus function or one which is not"); diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/GLJavaMethodBindingEmitter.java b/src/jogl/classes/com/jogamp/gluegen/opengl/GLJavaMethodBindingEmitter.java index 016674338..d491a3712 100755..100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/GLJavaMethodBindingEmitter.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/GLJavaMethodBindingEmitter.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -43,6 +43,7 @@ import com.jogamp.gluegen.CommentEmitter; import com.jogamp.gluegen.JavaEmitter; import com.jogamp.gluegen.JavaMethodBindingEmitter; import com.jogamp.gluegen.MethodBinding; +import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.cgram.types.Type; import com.jogamp.gluegen.procaddress.ProcAddressJavaMethodBindingEmitter; @@ -57,8 +58,8 @@ public class GLJavaMethodBindingEmitter extends ProcAddressJavaMethodBindingEmit protected GLEmitter glEmitter; protected CommentEmitter glCommentEmitter = new GLCommentEmitter(); - public GLJavaMethodBindingEmitter(JavaMethodBindingEmitter methodToWrap, boolean callThroughProcAddress, - String getProcAddressTableExpr, boolean changeNameAndArguments, boolean bufferObjectVariant, GLEmitter emitter) { + public GLJavaMethodBindingEmitter(final JavaMethodBindingEmitter methodToWrap, final boolean callThroughProcAddress, + final String getProcAddressTableExpr, final boolean changeNameAndArguments, final boolean bufferObjectVariant, final GLEmitter emitter) { super(methodToWrap, callThroughProcAddress, getProcAddressTableExpr, changeNameAndArguments, emitter); this.bufferObjectVariant = bufferObjectVariant; @@ -66,20 +67,20 @@ public class GLJavaMethodBindingEmitter extends ProcAddressJavaMethodBindingEmit setCommentEmitter(glCommentEmitter); } - public GLJavaMethodBindingEmitter(ProcAddressJavaMethodBindingEmitter methodToWrap, GLEmitter emitter, boolean bufferObjectVariant) { + public GLJavaMethodBindingEmitter(final ProcAddressJavaMethodBindingEmitter methodToWrap, final GLEmitter emitter, final boolean bufferObjectVariant) { super(methodToWrap); this.bufferObjectVariant = bufferObjectVariant; this.glEmitter = emitter; setCommentEmitter(glCommentEmitter); } - public GLJavaMethodBindingEmitter(GLJavaMethodBindingEmitter methodToWrap) { + public GLJavaMethodBindingEmitter(final GLJavaMethodBindingEmitter methodToWrap) { this(methodToWrap, methodToWrap.glEmitter, methodToWrap.bufferObjectVariant); } @Override - protected String getArgumentName(int i) { - String name = super.getArgumentName(i); + protected String getArgumentName(final int i) { + final String name = super.getArgumentName(i); if (!bufferObjectVariant) { return name; @@ -88,8 +89,8 @@ public class GLJavaMethodBindingEmitter extends ProcAddressJavaMethodBindingEmit // Emitters for VBO/PBO-related routines change the outgoing // argument name for the buffer if (binding.getJavaArgumentType(i).isLong()) { - Type cType = binding.getCArgumentType(i); - Type targetType = cType.asPointer().getTargetType(); + final Type cType = binding.getCArgumentType(i); + final Type targetType = cType.asPointer().getTargetType(); if (cType.isPointer() && (targetType.isVoid() || targetType.isPrimitive())) { return name + "_buffer_offset"; } @@ -101,21 +102,24 @@ public class GLJavaMethodBindingEmitter extends ProcAddressJavaMethodBindingEmit protected class GLCommentEmitter extends JavaMethodBindingEmitter.DefaultCommentEmitter { @Override - protected void emitBindingCSignature(MethodBinding binding, PrintWriter writer) { + protected void emitBindingCSignature(final MethodBinding binding, final PrintWriter writer) { - super.emitBindingCSignature(binding, writer); + final String symbolRenamed = binding.getName(); + final StringBuilder newComment = new StringBuilder(); - String symbolRenamed = binding.getName(); - StringBuilder newComment = new StringBuilder(); + final FunctionSymbol funcSym = binding.getCSymbol(); + writer.print("<code> "); + writer.print(funcSym.getType().toString(symbolRenamed, tagNativeBinding)); + writer.print(" </code> "); newComment.append("<br>Part of "); if (0 == glEmitter.addExtensionsOfSymbols2Buffer(newComment, ", ", "; ", symbolRenamed, binding.getAliasedNames())) { if (glEmitter.getGLConfig().getAllowNonGLExtensions()) { newComment.append("CORE FUNC"); } else { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); JavaEmitter.addStrings2Buffer(sb, ", ", symbolRenamed, binding.getAliasedNames()); - RuntimeException ex = new RuntimeException("Couldn't find extension to: " + binding + " ; " + sb.toString()); + final RuntimeException ex = new RuntimeException("Couldn't find extension to: " + binding + " ; " + sb.toString()); glEmitter.getGLConfig().getGLInfo().dump(); // glEmitter.getGLConfig().dumpRenames(); throw ex; diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/ant/StaticGLGenTask.java b/src/jogl/classes/com/jogamp/gluegen/opengl/ant/StaticGLGenTask.java index e3e7cb970..66819a83e 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/ant/StaticGLGenTask.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/ant/StaticGLGenTask.java @@ -55,13 +55,13 @@ import org.apache.tools.ant.util.JavaEnvUtils; /** * <p>An <a href="http://ant.apache.org">ANT</a> {@link org.apache.tools.ant.Task} * for using {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo}.</p> - * + * * <p>Usage:</p> * <pre> - <staticglgen package="[generated files package]" + <staticglgen package="[generated files package]" headers="[file pattern of GL headers]" outputdir="[directory to output the generated files]" /> - * </pre> + * </pre> * * @author Rob Grzywinski <a href="mailto:[email protected]">[email protected]</a> */ @@ -72,13 +72,13 @@ public class StaticGLGenTask extends Task * <p>The {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} classname.</p> */ private static final String GL_GEN = "com.jogamp.gluegen.opengl.BuildStaticGLInfo"; - + // ========================================================================= /** * <p>The {@link org.apache.tools.ant.types.CommandlineJava} that is used * to execute {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo}.</p> */ - private CommandlineJava glgenCommandline; + private final CommandlineJava glgenCommandline; // ========================================================================= /** @@ -90,12 +90,12 @@ public class StaticGLGenTask extends Task * <p>The output directory.</p> */ private String outputDirectory; - + /** * <p>The {@link org.apache.tools.ant.types.FileSet} of GL headers.</p> */ - private FileSet headerSet = new FileSet(); - + private final FileSet headerSet = new FileSet(); + // ========================================================================= /** * <p>Create and add the VM and classname to {@link org.apache.tools.ant.types.CommandlineJava}.</p> @@ -104,7 +104,7 @@ public class StaticGLGenTask extends Task { // create the CommandlineJava that will be used to call BuildStaticGLInfo glgenCommandline = new CommandlineJava(); - + // set the VM and classname in the commandline glgenCommandline.setVm(JavaEnvUtils.getJreExecutable("java")); glgenCommandline.setClassname(GL_GEN); @@ -114,10 +114,10 @@ public class StaticGLGenTask extends Task // ANT getters and setters /** * <p>Set the package name for the generated files. This is called by ANT.</p> - * + * * @param packageName the name of the package for the generated files */ - public void setPackage(String packageName) + public void setPackage(final String packageName) { log( ("Setting package name to: " + packageName), Project.MSG_VERBOSE); this.packageName = packageName; @@ -125,12 +125,12 @@ public class StaticGLGenTask extends Task /** * <p>Set the output directory. This is called by ANT.</p> - * + * * @param directory the output directory */ - public void setOutputDir(String directory) + public void setOutputDir(final String directory) { - log( ("Setting output directory to: " + directory), + log( ("Setting output directory to: " + directory), Project.MSG_VERBOSE); this.outputDirectory = directory; } @@ -138,7 +138,7 @@ public class StaticGLGenTask extends Task /** * <p>Add a header file to the list. This is called by ANT for a nested * element.</p> - * + * * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} */ public PatternSet.NameEntry createHeader() @@ -149,7 +149,7 @@ public class StaticGLGenTask extends Task /** * <p>Add a header file to the list. This is called by ANT for a nested * element.</p> - * + * * @return {@link org.apache.tools.ant.types.PatternSet.NameEntry} */ public PatternSet.NameEntry createHeadersFile() @@ -163,7 +163,7 @@ public class StaticGLGenTask extends Task * * @param headers the string containing the header patterns */ - public void setHeaders(String headers) + public void setHeaders(final String headers) { headerSet.setIncludes(headers); } @@ -171,7 +171,7 @@ public class StaticGLGenTask extends Task /** * <p>Add an optional classpath that defines the location of {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} * and <code>BuildStaticGLInfo</code>'s dependencies.</p> - * + * * @returns {@link org.apache.tools.ant.types.Path} */ public Path createClasspath() @@ -183,23 +183,24 @@ public class StaticGLGenTask extends Task /** * <p>Run the task. This involves validating the set attributes, creating * the command line to be executed and finally executing the command.</p> - * + * * @see org.apache.tools.ant.Task#execute() */ - public void execute() - throws BuildException + @Override + public void execute() + throws BuildException { // validate that all of the required attributes have been set validateAttributes(); - + // TODO: add logic to determine if the generated file needs to be // regenerated - + // add the attributes to the CommandlineJava addAttributes(); log(glgenCommandline.describeCommand(), Project.MSG_VERBOSE); - + // execute the command and throw on error final int error = execute(glgenCommandline.getCommandline()); if(error == 1) @@ -208,11 +209,11 @@ public class StaticGLGenTask extends Task /** * <p>Ensure that the user specified all required arguments.</p> - * - * @throws BuildException if there are required arguments that are not + * + * @throws BuildException if there are required arguments that are not * present or not valid */ - private void validateAttributes() + private void validateAttributes() throws BuildException { // validate that the package name is set @@ -223,29 +224,29 @@ public class StaticGLGenTask extends Task // TODO: switch to file and ensure that it exists if(!isValid(outputDirectory)) throw new BuildException("Invalid output directory name: " + outputDirectory); - + // TODO: validate that there are headers set } /** * <p>Is the specified string valid? A valid string is non-<code>null</code> * and has a non-zero length.</p> - * + * * @param string the string to be tested for validity * @return <code>true</code> if the string is valid. <code>false</code> - * otherwise. + * otherwise. */ - private boolean isValid(String string) + private boolean isValid(final String string) { // check for null if(string == null) return false; - + // ensure that the string has a non-zero length // NOTE: must trim() to remove leading and trailing whitespace if(string.trim().length() < 1) return false; - + // the string is valid return true; } @@ -258,44 +259,44 @@ public class StaticGLGenTask extends Task { // add the package name glgenCommandline.createArgument().setValue(packageName); - + // add the output directory name glgenCommandline.createArgument().setValue(outputDirectory); - + // add the header -files- from the FileSet headerSet.setDir(getProject().getBaseDir()); - DirectoryScanner directoryScanner = headerSet.getDirectoryScanner(getProject()); - String[] directoryFiles = directoryScanner.getIncludedFiles(); + final DirectoryScanner directoryScanner = headerSet.getDirectoryScanner(getProject()); + final String[] directoryFiles = directoryScanner.getIncludedFiles(); for(int i=0; i<directoryFiles.length; i++) { glgenCommandline.createArgument().setValue(directoryFiles[i]); } } - /** - * <p>Execute {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} in a + /** + * <p>Execute {@link com.jogamp.gluegen.opengl.BuildStaticGLInfo} in a * forked JVM.</p> - * + * * @throws BuildException */ - private int execute(String[] command) + private int execute(final String[] command) throws BuildException { // create the object that will perform the command execution - Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, - Project.MSG_WARN), + final Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, + Project.MSG_WARN), null); - + // set the project and command line execute.setAntRun(project); execute.setCommandline(command); execute.setWorkingDirectory( project.getBaseDir() ); - + // execute the command try { return execute.execute(); - } catch(IOException ioe) + } catch(final IOException ioe) { throw new BuildException(ioe, location); } diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureEmitter.java b/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureEmitter.java index adb1c2ae0..de9e3ed25 100755..100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureEmitter.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureEmitter.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -55,20 +55,20 @@ import java.util.Set; /** * Emitter producing NativeSignature attributes. - * - * Review: This Package/Class is not used and subject to be deleted. + * + * Review: This Package/Class is not used and subject to be deleted. */ public class NativeSignatureEmitter extends GLEmitter { @Override - protected List<? extends FunctionEmitter> generateMethodBindingEmitters(Set<MethodBinding> methodBindingSet, FunctionSymbol sym) throws Exception { + protected List<? extends FunctionEmitter> generateMethodBindingEmitters(final Set<MethodBinding> methodBindingSet, final FunctionSymbol sym) throws Exception { // Allow superclass to do most of the work for us - List<? extends FunctionEmitter> res = super.generateMethodBindingEmitters(methodBindingSet, sym); + final List<? extends FunctionEmitter> res = super.generateMethodBindingEmitters(methodBindingSet, sym); // Filter out all non-JavaMethodBindingEmitters - for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { - FunctionEmitter emitter = iter.next(); + for (final Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + final FunctionEmitter emitter = iter.next(); if (!(emitter instanceof JavaMethodBindingEmitter)) { iter.remove(); } @@ -78,13 +78,13 @@ public class NativeSignatureEmitter extends GLEmitter { return res; } - PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); + final PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); - List<FunctionEmitter> processed = new ArrayList<FunctionEmitter>(); + final List<FunctionEmitter> processed = new ArrayList<FunctionEmitter>(); // First, filter out all emitters going to the "other" (public) writer - for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { - FunctionEmitter emitter = iter.next(); + for (final Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + final FunctionEmitter emitter = iter.next(); if (emitter.getDefaultOutput() != writer) { processed.add(emitter); iter.remove(); @@ -93,12 +93,12 @@ public class NativeSignatureEmitter extends GLEmitter { // Now process all of the remaining emitters sorted by MethodBinding while (!res.isEmpty()) { - List<JavaMethodBindingEmitter> emittersForBinding = new ArrayList<JavaMethodBindingEmitter>(); - JavaMethodBindingEmitter emitter = (JavaMethodBindingEmitter) res.remove(0); + final List<JavaMethodBindingEmitter> emittersForBinding = new ArrayList<JavaMethodBindingEmitter>(); + final JavaMethodBindingEmitter emitter = (JavaMethodBindingEmitter) res.remove(0); emittersForBinding.add(emitter); - MethodBinding binding = emitter.getBinding(); - for (Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { - JavaMethodBindingEmitter emitter2 = (JavaMethodBindingEmitter) iter.next(); + final MethodBinding binding = emitter.getBinding(); + for (final Iterator<? extends FunctionEmitter> iter = res.iterator(); iter.hasNext();) { + final JavaMethodBindingEmitter emitter2 = (JavaMethodBindingEmitter) iter.next(); if (emitter2.getBinding() == binding) { emittersForBinding.add(emitter2); iter.remove(); @@ -111,17 +111,17 @@ public class NativeSignatureEmitter extends GLEmitter { return processed; } - protected void generateNativeSignatureEmitters(MethodBinding binding, List<JavaMethodBindingEmitter> allEmitters) { + protected void generateNativeSignatureEmitters(final MethodBinding binding, final List<JavaMethodBindingEmitter> allEmitters) { if (allEmitters.isEmpty()) { return; } - PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); + final PrintWriter writer = (getConfig().allStatic() ? javaWriter() : javaImplWriter()); // Give ourselves the chance to interpose on the generation of all code to keep things simple - List<JavaMethodBindingEmitter> newEmitters = new ArrayList<JavaMethodBindingEmitter>(); - for (JavaMethodBindingEmitter javaEmitter : allEmitters) { + final List<JavaMethodBindingEmitter> newEmitters = new ArrayList<JavaMethodBindingEmitter>(); + for (final JavaMethodBindingEmitter javaEmitter : allEmitters) { NativeSignatureJavaMethodBindingEmitter newEmitter = null; if (javaEmitter instanceof GLJavaMethodBindingEmitter) { newEmitter = new NativeSignatureJavaMethodBindingEmitter((GLJavaMethodBindingEmitter) javaEmitter); @@ -138,11 +138,11 @@ public class NativeSignatureEmitter extends GLEmitter { // been called with signatureOnly both true and false. if (signatureContainsStrings(binding) && !haveEmitterWithBody(allEmitters)) { // This basically handles glGetString but also any similar methods - NativeSignatureJavaMethodBindingEmitter javaEmitter = findEmitterWithWriter(allEmitters, writer); + final NativeSignatureJavaMethodBindingEmitter javaEmitter = findEmitterWithWriter(allEmitters, writer); // First, we need to clone this emitter to produce the native // entry point - NativeSignatureJavaMethodBindingEmitter emitter = new NativeSignatureJavaMethodBindingEmitter(javaEmitter); + final NativeSignatureJavaMethodBindingEmitter emitter = new NativeSignatureJavaMethodBindingEmitter(javaEmitter); emitter.removeModifier(JavaMethodBindingEmitter.PUBLIC); emitter.addModifier(JavaMethodBindingEmitter.PRIVATE); emitter.setForImplementingMethodCall(true); @@ -158,22 +158,22 @@ public class NativeSignatureEmitter extends GLEmitter { } } - protected boolean signatureContainsStrings(MethodBinding binding) { + protected boolean signatureContainsStrings(final MethodBinding binding) { for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); + final JavaType type = binding.getJavaArgumentType(i); if (type.isString() || type.isStringArray()) { return true; } } - JavaType retType = binding.getJavaReturnType(); + final JavaType retType = binding.getJavaReturnType(); if (retType.isString() || retType.isStringArray()) { return true; } return false; } - protected boolean haveEmitterWithBody(List<JavaMethodBindingEmitter> allEmitters) { - for (JavaMethodBindingEmitter emitter : allEmitters) { + protected boolean haveEmitterWithBody(final List<JavaMethodBindingEmitter> allEmitters) { + for (final JavaMethodBindingEmitter emitter : allEmitters) { if (!emitter.signatureOnly()) { return true; } @@ -181,9 +181,9 @@ public class NativeSignatureEmitter extends GLEmitter { return false; } - protected NativeSignatureJavaMethodBindingEmitter findEmitterWithWriter(List<JavaMethodBindingEmitter> allEmitters, PrintWriter writer) { - for (JavaMethodBindingEmitter jemitter : allEmitters) { - NativeSignatureJavaMethodBindingEmitter emitter = (NativeSignatureJavaMethodBindingEmitter)jemitter; + protected NativeSignatureJavaMethodBindingEmitter findEmitterWithWriter(final List<JavaMethodBindingEmitter> allEmitters, final PrintWriter writer) { + for (final JavaMethodBindingEmitter jemitter : allEmitters) { + final NativeSignatureJavaMethodBindingEmitter emitter = (NativeSignatureJavaMethodBindingEmitter)jemitter; if (emitter.getDefaultOutput() == writer) { return emitter; } diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureJavaMethodBindingEmitter.java b/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureJavaMethodBindingEmitter.java index e98478b6e..86080d84f 100755..100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureJavaMethodBindingEmitter.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/nativesig/NativeSignatureJavaMethodBindingEmitter.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -53,31 +53,31 @@ import java.io.PrintWriter; /** Review: This Package/Class is not used and subject to be deleted. */ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBindingEmitter { - public NativeSignatureJavaMethodBindingEmitter(GLJavaMethodBindingEmitter methodToWrap) { + public NativeSignatureJavaMethodBindingEmitter(final GLJavaMethodBindingEmitter methodToWrap) { super(methodToWrap); } - public NativeSignatureJavaMethodBindingEmitter(ProcAddressJavaMethodBindingEmitter methodToWrap, GLEmitter emitter, boolean bufferObjectVariant) { + public NativeSignatureJavaMethodBindingEmitter(final ProcAddressJavaMethodBindingEmitter methodToWrap, final GLEmitter emitter, final boolean bufferObjectVariant) { super(methodToWrap, emitter, bufferObjectVariant); } - public NativeSignatureJavaMethodBindingEmitter(JavaMethodBindingEmitter methodToWrap, NativeSignatureEmitter emitter) { + public NativeSignatureJavaMethodBindingEmitter(final JavaMethodBindingEmitter methodToWrap, final NativeSignatureEmitter emitter) { super(methodToWrap, false, null, false, false, emitter); } @Override - protected void emitSignature(PrintWriter writer) { + protected void emitSignature(final PrintWriter writer) { writer.print(getBaseIndentString()); emitNativeSignatureAnnotation(writer); super.emitSignature(writer); } - protected void emitNativeSignatureAnnotation(PrintWriter writer) { + protected void emitNativeSignatureAnnotation(final PrintWriter writer) { if (hasModifier(JavaMethodBindingEmitter.NATIVE)) { // Emit everything as a leaf for now // FIXME: make this configurable writer.print("@NativeSignature(\"l"); - MethodBinding binding = getBinding(); + final MethodBinding binding = getBinding(); if (callThroughProcAddress) { writer.print("p"); } @@ -94,7 +94,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } } - protected void emitNativeSignatureElement(PrintWriter writer, JavaType type, Type cType, int index) { + protected void emitNativeSignatureElement(final PrintWriter writer, final JavaType type, final Type cType, final int index) { if (type.isVoid()) { if (index > 0) { throw new InternalError("Error parsing arguments -- void should not be seen aside from argument 0"); @@ -107,7 +107,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } else if (type.isPrimitiveArray()) { writer.print("MO"); } else if (type.isPrimitive()) { - Class<?> clazz = type.getJavaClass(); + final Class<?> clazz = type.getJavaClass(); if (clazz == Byte.TYPE) { writer.print("B"); } else if (clazz == Character.TYPE) { writer.print("C"); } else if (clazz == Double.TYPE) { writer.print("D"); } @@ -131,9 +131,10 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } } - protected String getReturnTypeString(boolean skipArray) { + @Override + protected String getReturnTypeString(final boolean skipArray) { if (isForImplementingMethodCall()) { - JavaType returnType = getBinding().getJavaReturnType(); + final JavaType returnType = getBinding().getJavaReturnType(); if (returnType.isString() || returnType.isNIOByteBuffer()) { // Treat these as addresses return "long"; @@ -142,10 +143,11 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return super.getReturnTypeString(skipArray); } - protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { + @Override + protected void emitPreCallSetup(final MethodBinding binding, final PrintWriter writer) { super.emitPreCallSetup(binding, writer); for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); + final JavaType type = binding.getJavaArgumentType(i); if (type.isNIOBuffer() && !useNIODirectOnly ) { // Emit declarations for variables holding primitive arrays as type Object // We don't know 100% sure we're going to use these at this point in the code, though @@ -158,11 +160,12 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } } - protected String getNIOBufferArrayName(int argNumber) { + protected String getNIOBufferArrayName(final int argNumber) { return "__buffer_array_" + argNumber; } - protected int emitArguments(PrintWriter writer) + @Override + protected int emitArguments(final PrintWriter writer) { boolean needComma = false; int numEmitted = 0; @@ -182,14 +185,14 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding // Always emit outgoing "this" argument writer.print("long "); - writer.print(javaThisArgumentName()); + writer.print(javaThisArgumentName()); ++numEmitted; needComma = true; } for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); - if (type.isVoid()) { + final JavaType type = binding.getJavaArgumentType(i); + if (type.isVoid()) { // Make sure this is the only param to the method; if it isn't, // there's something wrong with our parsing of the headers. if (binding.getNumArguments() != 1) { @@ -198,7 +201,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding "multi-argument function \"" + binding + "\""); } continue; - } + } if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { // Don't need to expose these at the Java level @@ -229,7 +232,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding if (type.isNIOBuffer()) { writer.print(", int " + byteOffsetArgName(i)); } else if (type.isNIOBufferArray()) { - writer.print(", int[] " + + writer.print(", int[] " + byteOffsetArrayArgName(i)); } } @@ -242,11 +245,12 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return numEmitted; } - protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { + @Override + protected void emitReturnVariableSetupAndCall(final MethodBinding binding, final PrintWriter writer) { writer.print(" "); - JavaType returnType = binding.getJavaReturnType(); + final JavaType returnType = binding.getJavaReturnType(); boolean needsResultAssignment = false; - + if (!returnType.isVoid()) { if (returnType.isCompoundTypeWrapper() || returnType.isNIOByteBuffer()) { @@ -340,7 +344,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } } - protected int emitCallArguments(MethodBinding binding, PrintWriter writer, boolean direct) { + protected int emitCallArguments(final MethodBinding binding, final PrintWriter writer, final boolean direct) { // Note that we override this completely because we both need to // move the potential location of the outgoing proc address as // well as change the way we pass out Buffers, arrays, Strings, etc. @@ -364,7 +368,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding ++numArgsEmitted; } for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); + final JavaType type = binding.getJavaArgumentType(i); if (type.isJNIEnv() || binding.isArgumentThisPointer(i)) { // Don't need to expose these at the Java level continue; @@ -375,7 +379,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding // there's something wrong with our parsing of the headers. assert(binding.getNumArguments() == 1); continue; - } + } if (needComma) { writer.print(", "); @@ -455,9 +459,10 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return numArgsEmitted; } - protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { + @Override + protected void emitCallResultReturn(final MethodBinding binding, final PrintWriter writer) { for (int i = 0; i < binding.getNumArguments(); i++) { - JavaType type = binding.getJavaArgumentType(i); + final JavaType type = binding.getJavaArgumentType(i); if (type.isString()) { writer.println(";"); writer.println(" BuffersInternal.freeCString(" + binding.getArgumentName(i) + "_c_str);"); @@ -468,15 +473,16 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding super.emitCallResultReturn(binding, writer); } + @Override public String getName() { - String res = super.getName(); + final String res = super.getName(); if (forImplementingMethodCall && bufferObjectVariant) { return res + "BufObj"; } return res; } - protected String getImplMethodName(boolean direct) { + protected String getImplMethodName(final boolean direct) { String name = null; if (direct) { name = binding.getName() + "$0"; diff --git a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLNameResolver.java b/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLNameResolver.java index 92554776a..d400381c8 100644 --- a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLNameResolver.java +++ b/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLNameResolver.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * */ package com.jogamp.gluegen.runtime.opengl; @@ -43,48 +43,48 @@ public class GLNameResolver { //GL_XYZ : GL_XYZ, GL_GL2_XYZ, GL_ARB_XYZ, GL_OES_XYZ, GL_OML_XYZ // // Pass-1 Unify ARB extensions with the same value - // Pass-2 Unify vendor extensions, + // Pass-2 Unify vendor extensions, // if exist as an ARB extension with the same value. // Pass-3 Emit - public static final String[] extensionsARB = { "ARB", "GL2", "OES", "KHR", "OML" }; - public static final String[] extensionsVEN = { "3DFX", - "AMD", - "ANGLE", - "ARM", - "APPLE", - "ATI", - "EXT", - "HI", - "HP", - "IBM", - "IMG", - "MESA", - "MESAX", - "NV", - "QCOM", - "SGI", - "SGIS", - "SGIX", - "SUN", - "VIV", - "WIN" - }; + private static final String[] extensionsARB = { "ARB", "GL2", "OES", "KHR", "OML" }; + private static final String[] extensionsVEN = { "3DFX", + "AMD", + "ANGLE", + "ARM", + "APPLE", + "ATI", + "EXT", + "HI", + "HP", + "IBM", + "IMG", + "MESA", + "MESAX", + "NV", + "QCOM", + "SGI", + "SGIS", + "SGIX", + "SUN", + "VIV", + "WIN" + }; - public static final boolean isGLFunction(String str) { + public static final boolean isGLFunction(final String str) { return str.startsWith("gl") || /* str.startsWith("glu") || str.startsWith("glX") || */ str.startsWith("egl") || str.startsWith("wgl") || str.startsWith("agl") || str.startsWith("cgl") ; } - public static final boolean isGLEnumeration(String str) { + public static final boolean isGLEnumeration(final String str) { return str.startsWith("GL_") || str.startsWith("GLU_") || str.startsWith("GLX_") || str.startsWith("EGL_") || str.startsWith("WGL_") || str.startsWith("AGL_") || str.startsWith("CGL_") ; } - public static final int getExtensionIdx(String[] extensions, String str, boolean isGLFunc) { + public static final int getExtensionIdx(final String[] extensions, final String str, final boolean isGLFunc) { if(isGLFunc) { for(int i = extensions.length - 1 ; i>=0 ; i--) { if( str.endsWith(extensions[i]) ) { @@ -101,11 +101,11 @@ public class GLNameResolver { return -1; } - public static final boolean isExtension(String[] extensions, String str, boolean isGLFunc) { + public static final boolean isExtension(final String[] extensions, final String str, final boolean isGLFunc) { return getExtensionIdx(extensions, str, isGLFunc)>=0; } - public static final String getExtensionSuffix(String str, boolean isGLFunc) { + public static final String getExtensionSuffix(final String str, final boolean isGLFunc) { int idx = getExtensionIdx(extensionsARB, str, isGLFunc); if(idx>=0) { return extensionsARB[idx]; @@ -117,7 +117,7 @@ public class GLNameResolver { return null; } - public static final String normalize(String[] extensions, String str, boolean isGLFunc) { + public static final String normalize(final String[] extensions, String str, final boolean isGLFunc) { boolean touched = false; for(int i = extensions.length - 1 ; !touched && i>=0 ; i--) { if(isGLFunc) { @@ -136,19 +136,19 @@ public class GLNameResolver { } return str; } - public static final String normalizeARB(String str, boolean isGLFunc) { + public static final String normalizeARB(final String str, final boolean isGLFunc) { return normalize(extensionsARB, str, isGLFunc); } - public static final boolean isExtensionARB(String str, boolean isGLFunc) { + public static final boolean isExtensionARB(final String str, final boolean isGLFunc) { return isExtension(extensionsARB, str, isGLFunc); } - public static final String normalizeVEN(String str, boolean isGLFunc) { + public static final String normalizeVEN(final String str, final boolean isGLFunc) { return normalize(extensionsVEN, str, isGLFunc); } - public static final boolean isExtensionVEN(String str, boolean isGLFunc) { + public static final boolean isExtensionVEN(final String str, final boolean isGLFunc) { return isExtension(extensionsVEN, str, isGLFunc); } - public static final String normalize(String str, boolean isGLFunc) { + public static final String normalize(final String str, final boolean isGLFunc) { if (isExtensionARB(str, isGLFunc)) { return normalizeARB(str, isGLFunc); } @@ -157,12 +157,12 @@ public class GLNameResolver { } return str; } - public static final boolean isExtension(String str, boolean isGLFunc) { - return isExtension(extensionsARB, str, isGLFunc) || + public static final boolean isExtension(final String str, final boolean isGLFunc) { + return isExtension(extensionsARB, str, isGLFunc) || isExtension(extensionsVEN, str, isGLFunc); } - public static final int getFuncNamePermutationNumber(String name) { + public static final int getFuncNamePermutationNumber(final String name) { if(isExtensionARB(name, true) || isExtensionVEN(name, true)) { // no name permutation, if it's already a known extension return 1; @@ -170,7 +170,7 @@ public class GLNameResolver { return 1 + extensionsARB.length + extensionsVEN.length; } - public static final String getFuncNamePermutation(String name, int i) { + public static final String getFuncNamePermutation(final String name, int i) { // identity if(i==0) { return name; diff --git a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLProcAddressResolver.java b/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLProcAddressResolver.java index 9775de491..23d4fc3cf 100644 --- a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLProcAddressResolver.java +++ b/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLProcAddressResolver.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + /* * Created on Saturday, April 24 2010 16:44 */ @@ -42,16 +42,17 @@ public class GLProcAddressResolver implements FunctionAddressResolver { public static final boolean DEBUG = false; - public long resolve(String name, DynamicLookupHelper lookup) { + @Override + public long resolve(final String name, final DynamicLookupHelper lookup) { long newProcAddress = 0; - int permutations = GLNameResolver.getFuncNamePermutationNumber(name); + final int permutations = GLNameResolver.getFuncNamePermutationNumber(name); for (int i = 0; 0 == newProcAddress && i < permutations; i++) { - String funcName = GLNameResolver.getFuncNamePermutation(name, i); + final String funcName = GLNameResolver.getFuncNamePermutation(name, i); try { newProcAddress = lookup.dynamicLookupFunction(funcName); - } catch (Exception e) { + } catch (final Exception e) { if (DEBUG) { e.printStackTrace(); } diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index e60fba02b..bc500d87f 100755..100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -29,26 +29,31 @@ package com.jogamp.graph.curve; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; + +import jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.curve.tess.Triangulation; import com.jogamp.graph.curve.tess.Triangulator; -import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Outline; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; -import com.jogamp.graph.math.VectorUtil; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.math.geom.AABBox; -/** A Generic shape objects which is defined by a list of Outlines. - * This Shape can be transformed to Triangulations. +/** + * A Generic shape objects which is defined by a list of Outlines. + * This Shape can be transformed to triangulations. * The list of triangles generated are render-able by a Region object. - * The triangulation produced by this Shape will define the + * The triangulation produced by this Shape will define the * closed region defined by the outlines. - * + * * One or more OutlineShape Object can be associated to a region * this is left as a high-level representation of the Objects. For * optimizations, flexibility requirements for future features. - * + * * <br><br> * Example to creating an Outline Shape: * <pre> @@ -60,18 +65,18 @@ import com.jogamp.graph.math.VectorUtil; addVertex(...) addVertex(...) * </pre> - * - * The above will create two outlines each with three vertices. By adding these two outlines to + * + * The above will create two outlines each with three vertices. By adding these two outlines to * the OutlineShape, we are stating that the combination of the two outlines represent the shape. * <br> - * - * To specify that the shape is curved at a region, the on-curve flag should be set to false + * + * To specify that the shape is curved at a region, the on-curve flag should be set to false * for the vertex that is in the middle of the curved region (if the curved region is defined by 3 * vertices (quadratic curve). * <br> - * In case the curved region is defined by 4 or more vertices the middle vertices should both have + * In case the curved region is defined by 4 or more vertices the middle vertices should both have * the on-curve flag set to false. - * + * * <br>Example: <br> * <pre> addVertex(0,0, true); @@ -79,16 +84,16 @@ import com.jogamp.graph.math.VectorUtil; addVertex(1,1, false); addVertex(1,0, true); * </pre> - * - * The above snippet defines a cubic nurbs curve where (0,1 and 1,1) + * + * The above snippet defines a cubic nurbs curve where (0,1 and 1,1) * do not belong to the final rendered shape. - * + * * <i>Implementation Notes:</i><br> * <ul> * <li> The first vertex of any outline belonging to the shape should be on-curve</li> * <li> Intersections between off-curved parts of the outline is not handled</li> * </ul> - * + * * @see Outline * @see Region */ @@ -101,34 +106,80 @@ public class OutlineShape implements Comparable<OutlineShape> { public final int state; - VerticesState(int state){ + VerticesState(final int state){ this.state = state; } - } + } + + /** Initial {@link #getSharpness()} value, which can be modified via {@link #setSharpness(float)}. */ + public static final float DEFAULT_SHARPNESS = 0.5f; public static final int DIRTY_BOUNDS = 1 << 0; + /** + * Modified shape, requires to update the vertices and triangles, here: vertices. + */ + public static final int DIRTY_VERTICES = 1 << 1; + /** + * Modified shape, requires to update the vertices and triangles, here: triangulation. + */ + public static final int DIRTY_TRIANGLES = 1 << 2; private final Vertex.Factory<? extends Vertex> vertexFactory; - private VerticesState outlineState; - /** The list of {@link Outline}s that are part of this + /** The list of {@link Outline}s that are part of this * outline shape. */ - private ArrayList<Outline> outlines; - private AABBox bbox; + /* pp */ final ArrayList<Outline> outlines; + + private final AABBox bbox; + private final ArrayList<Triangle> triangles; + private final ArrayList<Vertex> vertices; + private int addedVerticeCount; + + private VerticesState outlineState; /** dirty bits DIRTY_BOUNDS */ - private int dirtyBits; + private int dirtyBits; + + private float sharpness; + + private final float[] tmpV1 = new float[3]; + private final float[] tmpV2 = new float[3]; + private final float[] tmpV3 = new float[3]; /** Create a new Outline based Shape */ - public OutlineShape(Vertex.Factory<? extends Vertex> factory) { + public OutlineShape(final Vertex.Factory<? extends Vertex> factory) { this.vertexFactory = factory; this.outlines = new ArrayList<Outline>(3); this.outlines.add(new Outline()); this.outlineState = VerticesState.UNDEFINED; this.bbox = new AABBox(); - this.dirtyBits = 0; + this.triangles = new ArrayList<Triangle>(); + this.vertices = new ArrayList<Vertex>(); + this.addedVerticeCount = 0; + this.dirtyBits = 0; + this.sharpness = DEFAULT_SHARPNESS; + } + + /** + * Return the number of newly added vertices during {@link #getTriangles(VerticesState)} + * while transforming the outlines to {@link VerticesState#QUADRATIC_NURBS} and triangulation. + * @see #setIsQuadraticNurbs() + */ + public int getAddedVerticeCount() { + return addedVerticeCount; + } + + /** Sharpness value, defaults to {@link #DEFAULT_SHARPNESS}. */ + public float getSharpness() { return sharpness; } + + /** Sets sharpness, defaults to {@link #DEFAULT_SHARPNESS}. */ + public void setSharpness(final float s) { + if( this.sharpness != s ) { + clearCache(); + sharpness=s; + } } /** Clears all data and reset all states as if this instance was newly created */ @@ -137,56 +188,70 @@ public class OutlineShape implements Comparable<OutlineShape> { outlines.add(new Outline()); outlineState = VerticesState.UNDEFINED; bbox.reset(); - dirtyBits = 0; + vertices.clear(); + triangles.clear(); + addedVerticeCount = 0; + dirtyBits = 0; } - /** Returns the associated vertex factory of this outline shape + /** Clears cached triangulated data, i.e. {@link #getTriangles(VerticesState)} and {@link #getVertices()}. */ + public void clearCache() { + vertices.clear(); + triangles.clear(); + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; + } + + /** + * Returns the associated vertex factory of this outline shape * @return Vertex.Factory object */ public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; } - public int getOutlineNumber() { + public final int getOutlineNumber() { return outlines.size(); } - /** Add a new empty {@link Outline} + /** + * Add a new empty {@link Outline} * to the end of this shape's outline list. * <p>If the {@link #getLastOutline()} is empty already, no new one will be added.</p> - * + * * After a call to this function all new vertices added * will belong to the new outline */ - public void addEmptyOutline() { + public final void addEmptyOutline() { if( !getLastOutline().isEmpty() ) { outlines.add(new Outline()); } } - /** Appends the {@link Outline} element to the end, + /** + * Appends the {@link Outline} element to the end, * ensuring a clean tail. - * + * * <p>A clean tail is ensured, no double empty Outlines are produced * and a pre-existing empty outline will be replaced with the given one. </p> - * + * * @param outline Outline object to be added - * @throws NullPointerException if the {@link Outline} element is null + * @throws NullPointerException if the {@link Outline} element is null */ - public void addOutline(Outline outline) throws NullPointerException { + public final void addOutline(final Outline outline) throws NullPointerException { addOutline(outlines.size(), outline); } - /** Insert the {@link Outline} element at the given {@code position}. - * + /** + * Insert the {@link Outline} element at the given {@code position}. + * * <p>If the {@code position} indicates the end of this list, * a clean tail is ensured, no double empty Outlines are produced * and a pre-existing empty outline will be replaced with the given one. </p> - * + * * @param position of the added Outline * @param outline Outline object to be added - * @throws NullPointerException if the {@link Outline} element is null + * @throws NullPointerException if the {@link Outline} element is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber()) */ - public void addOutline(int position, Outline outline) throws NullPointerException, IndexOutOfBoundsException { + public final void addOutline(final int position, final Outline outline) throws NullPointerException, IndexOutOfBoundsException { if (null == outline) { throw new NullPointerException("outline is null"); } @@ -200,6 +265,8 @@ public class OutlineShape implements Comparable<OutlineShape> { if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(outline.getBounds()); } + // vertices.addAll(outline.getVertices()); // FIXME: can do and remove DIRTY_VERTICES ? + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; return; } } @@ -207,53 +274,58 @@ public class OutlineShape implements Comparable<OutlineShape> { if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { bbox.resize(outline.getBounds()); } + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; } - /** Insert the {@link OutlineShape} elements of type {@link Outline}, .. at the end of this shape, + /** + * Insert the {@link OutlineShape} elements of type {@link Outline}, .. at the end of this shape, * using {@link #addOutline(Outline)} for each element. - * <p>Closes the current last outline via {@link #closeLastOutline()} before adding the new ones.</p> + * <p>Closes the current last outline via {@link #closeLastOutline(boolean)} before adding the new ones.</p> * @param outlineShape OutlineShape elements to be added. - * @throws NullPointerException if the {@link OutlineShape} is null + * @throws NullPointerException if the {@link OutlineShape} is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getOutlineNumber()) */ - public void addOutlineShape(OutlineShape outlineShape) throws NullPointerException { + public final void addOutlineShape(final OutlineShape outlineShape) throws NullPointerException { if (null == outlineShape) { throw new NullPointerException("OutlineShape is null"); } - closeLastOutline(); + closeLastOutline(true); for(int i=0; i<outlineShape.getOutlineNumber(); i++) { addOutline(outlineShape.getOutline(i)); } } - /** Replaces the {@link Outline} element at the given {@code position}. + /** + * Replaces the {@link Outline} element at the given {@code position}. * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p> - * + * * @param position of the replaced Outline - * @param outline replacement Outline object - * @throws NullPointerException if the {@link Outline} element is null + * @param outline replacement Outline object + * @throws NullPointerException if the {@link Outline} element is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber()) */ - public void setOutline(int position, Outline outline) throws NullPointerException, IndexOutOfBoundsException { + public final void setOutline(final int position, final Outline outline) throws NullPointerException, IndexOutOfBoundsException { if (null == outline) { throw new NullPointerException("outline is null"); } outlines.set(position, outline); - dirtyBits |= DIRTY_BOUNDS; + dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES; } - /** Removes the {@link Outline} element at the given {@code position}. + /** + * Removes the {@link Outline} element at the given {@code position}. * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p> - * + * * @param position of the to be removed Outline * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber()) */ - public final Outline removeOutline(int position) throws IndexOutOfBoundsException { - dirtyBits |= DIRTY_BOUNDS; + public final Outline removeOutline(final int position) throws IndexOutOfBoundsException { + dirtyBits |= DIRTY_BOUNDS | DIRTY_TRIANGLES | DIRTY_VERTICES; return outlines.remove(position); } - /** Get the last added outline to the list + /** + * Get the last added outline to the list * of outlines that define the shape * @return the last outline */ @@ -261,51 +333,57 @@ public class OutlineShape implements Comparable<OutlineShape> { return outlines.get(outlines.size()-1); } - /** @return the {@code Outline} at {@code position} + /** + * Returns the {@code Outline} at {@code position} * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getOutlineNumber()) */ - public Outline getOutline(int position) throws IndexOutOfBoundsException { + public final Outline getOutline(final int position) throws IndexOutOfBoundsException { return outlines.get(position); - } + } - /** Adds a vertex to the last open outline in the - * shape. + /** + * Adds a vertex to the last open outline to the shape's tail. * @param v the vertex to be added to the OutlineShape */ - public final void addVertex(Vertex v) { + public final void addVertex(final Vertex v) { final Outline lo = getLastOutline(); lo.addVertex(v); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { - bbox.resize(lo.getBounds()); + bbox.resize(v.getCoord()); } + // vertices.add(v); // FIXME: can do and remove DIRTY_VERTICES ? + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; } - /** Adds a vertex to the last open outline in the shape. - * at {@code position} - * @param position indx at which the vertex will be added + /** + * Adds a vertex to the last open outline to the shape at {@code position} + * @param position indx at which the vertex will be added * @param v the vertex to be added to the OutlineShape */ - public final void addVertex(int position, Vertex v) { + public final void addVertex(final int position, final Vertex v) { final Outline lo = getLastOutline(); lo.addVertex(position, v); if( 0 == ( dirtyBits & DIRTY_BOUNDS ) ) { - bbox.resize(lo.getBounds()); + bbox.resize(v.getCoord()); } + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; } - /** Add a 2D {@link Vertex} to the last outline by defining the coordniate attribute + /** + * Add a 2D {@link Vertex} to the last outline by defining the coordinate attribute * of the vertex. The 2D vertex will be represented as Z=0. - * + * * @param x the x coordinate * @param y the y coordniate * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ - public final void addVertex(float x, float y, boolean onCurve) { + public final void addVertex(final float x, final float y, final boolean onCurve) { addVertex(vertexFactory.create(x, y, 0f, onCurve)); } - /** Add a 3D {@link Vertex} to the last outline by defining the coordniate attribute + /** + * Add a 3D {@link Vertex} to the last outline by defining the coordniate attribute * of the vertex. * @param x the x coordinate * @param y the y coordinate @@ -313,14 +391,15 @@ public class OutlineShape implements Comparable<OutlineShape> { * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ - public final void addVertex(float x, float y, float z, boolean onCurve) { + public final void addVertex(final float x, final float y, final float z, final boolean onCurve) { addVertex(vertexFactory.create(x, y, z, onCurve)); } - /** Add a vertex to the last outline by passing a float array and specifying the - * offset and length in which. The attributes of the vertex are located. + /** + * Add a vertex to the last outline by passing a float array and specifying the + * offset and length in which. The attributes of the vertex are located. * The attributes should be continuous (stride = 0). - * Attributes which value are not set (when length less than 3) + * Attributes which value are not set (when length less than 3) * are set implicitly to zero. * @param coordsBuffer the coordinate array where the vertex attributes are to be picked from * @param offset the offset in the buffer to the x coordinate @@ -328,70 +407,72 @@ public class OutlineShape implements Comparable<OutlineShape> { * @param onCurve flag if this vertex is on the final curve or defines a curved region * of the shape around this vertex. */ - public final void addVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { + public final void addVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { addVertex(vertexFactory.create(coordsBuffer, offset, length, onCurve)); - } + } - /** Closes the last outline in the shape. - * <p>If last vertex is not equal to first vertex. - * A new temp vertex is added at the end which - * is equal to the first.</p> + /** + * Closes the last outline in the shape. + * <p> + * Checks whether the last vertex equals to the first of the last outline. + * If not equal, it either appends a clone of the first vertex + * or prepends a clone of the last vertex, depending on <code>closeTail</code>. + * </p> + * @param closeTail if true, a clone of the first vertex will be appended, + * otherwise a clone of the last vertex will be prepended. */ - public void closeLastOutline() { - getLastOutline().setClosed(true); + public final void closeLastOutline(final boolean closeTail) { + if( getLastOutline().setClosed(true) ) { + dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; + } } /** - * @return the outline's vertices state, {@link OutlineShape.VerticesState} + * Return the outline's vertices state, {@link OutlineShape.VerticesState} */ public final VerticesState getOutlineState() { return outlineState; } - /** Ensure the outlines represent - * the specified destinationType. - * and removes all overlaps in boundary triangles - * @param destinationType the target outline's vertices state. Currently only - * {@link OutlineShape.VerticesState#QUADRATIC_NURBS} are supported. + /** + * Claim this outline's vertices are all {@link OutlineShape.VerticesState#QUADRATIC_NURBS}, + * hence no cubic transformations will be performed. */ - public void transformOutlines(VerticesState destinationType) { - if(outlineState != destinationType){ - if(destinationType == VerticesState.QUADRATIC_NURBS){ - transformOutlines2Quadratic(); - checkOverlaps(); - } else { - throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")"); - } - } + public final void setIsQuadraticNurbs() { + outlineState = VerticesState.QUADRATIC_NURBS; + // checkPossibleOverlaps = false; } - private void subdivideTriangle(final Outline outline, Vertex a, Vertex b, Vertex c, int index){ - float[] v1 = VectorUtil.mid(a.getCoord(), b.getCoord()); - float[] v3 = VectorUtil.mid(b.getCoord(), c.getCoord()); - float[] v2 = VectorUtil.mid(v1, v3); + private void subdivideTriangle(final Outline outline, final Vertex a, final Vertex b, final Vertex c, final int index){ + VectorUtil.midVec3(tmpV1, a.getCoord(), b.getCoord()); + VectorUtil.midVec3(tmpV3, b.getCoord(), c.getCoord()); + VectorUtil.midVec3(tmpV2, tmpV1, tmpV3); //drop off-curve vertex to image on the curve - b.setCoord(v2, 0, 3); + b.setCoord(tmpV2, 0, 3); b.setOnCurve(true); - outline.addVertex(index, vertexFactory.create(v1, 0, 3, false)); - outline.addVertex(index+2, vertexFactory.create(v3, 0, 3, false)); + outline.addVertex(index, vertexFactory.create(tmpV1, 0, 3, false)); + outline.addVertex(index+2, vertexFactory.create(tmpV3, 0, 3, false)); + + addedVerticeCount += 2; } - /** Check overlaps between curved triangles - * first check if any vertex in triangle a is in triangle b - * second check if edges of triangle a intersect segments of triangle b - * if any of the two tests is true we divide current triangle - * and add the other to the list of overlaps - * - * Loop until overlap array is empty. (check only in first pass) + /** + * Check overlaps between curved triangles + * first check if any vertex in triangle a is in triangle b + * second check if edges of triangle a intersect segments of triangle b + * if any of the two tests is true we divide current triangle + * and add the other to the list of overlaps + * + * Loop until overlap array is empty. (check only in first pass) */ - private void checkOverlaps() { - ArrayList<Vertex> overlaps = new ArrayList<Vertex>(3); - int count = getOutlineNumber(); + private void checkOverlaps() { + final ArrayList<Vertex> overlaps = new ArrayList<Vertex>(3); + final int count = getOutlineNumber(); boolean firstpass = true; do { - for (int cc = 0; cc < count; cc++) { + for (int cc = 0; cc < count; cc++) { final Outline outline = getOutline(cc); int vertexCount = outline.getVertexCount(); for(int i=0; i < outline.getVertexCount(); i++) { @@ -399,39 +480,43 @@ public class OutlineShape implements Comparable<OutlineShape> { if ( !currentVertex.isOnCurve()) { final Vertex nextV = outline.getVertex((i+1)%vertexCount); final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount); - Vertex overlap =null; - - //check for overlap even if already set for subdivision - //ensuring both trianglur overlaps get divided - //for pref. only check in first pass - //second pass to clear the overlaps arrray(reduces precision errors) - if(firstpass) { - overlap = checkTriOverlaps(prevV, currentVertex, nextV); + final Vertex overlap; + + // check for overlap even if already set for subdivision + // ensuring both triangular overlaps get divided + // for pref. only check in first pass + // second pass to clear the overlaps array(reduces precision errors) + if( firstpass ) { + overlap = checkTriOverlaps0(prevV, currentVertex, nextV); + } else { + overlap = null; } - if(overlaps.contains(currentVertex) || overlap != null) { + if( overlaps.contains(currentVertex) || overlap != null ) { overlaps.remove(currentVertex); subdivideTriangle(outline, prevV, currentVertex, nextV, i); i+=3; vertexCount+=2; + addedVerticeCount+=2; if(overlap != null && !overlap.isOnCurve()) { - if(!overlaps.contains(overlap)) + if(!overlaps.contains(overlap)) { overlaps.add(overlap); + } } } } } } firstpass = false; - }while(!overlaps.isEmpty()); + } while( !overlaps.isEmpty() ); } - private Vertex checkTriOverlaps(Vertex a, Vertex b, Vertex c) { - int count = getOutlineNumber(); - for (int cc = 0; cc < count; cc++) { + private Vertex checkTriOverlaps0(final Vertex a, final Vertex b, final Vertex c) { + final int count = getOutlineNumber(); + for (int cc = 0; cc < count; cc++) { final Outline outline = getOutline(cc); - int vertexCount = outline.getVertexCount(); + final int vertexCount = outline.getVertexCount(); for(int i=0; i < vertexCount; i++) { final Vertex current = outline.getVertex(i); if(current.isOnCurve() || current == a || current == b || current == c) { @@ -445,15 +530,47 @@ public class OutlineShape implements Comparable<OutlineShape> { continue; } - if(VectorUtil.vertexInTriangle(a.getCoord(), b.getCoord(), c.getCoord(), current.getCoord()) - || VectorUtil.vertexInTriangle(a.getCoord(), b.getCoord(), c.getCoord(), nextV.getCoord()) - || VectorUtil.vertexInTriangle(a.getCoord(), b.getCoord(), c.getCoord(), prevV.getCoord())) { + if( VectorUtil.isVec3InTriangle3(a.getCoord(), b.getCoord(), c.getCoord(), + current.getCoord(), nextV.getCoord(), prevV.getCoord(), + tmpV1, tmpV2, tmpV3) ) { + return current; + } + if(VectorUtil.testTri2SegIntersection(a, b, c, prevV, current) || + VectorUtil.testTri2SegIntersection(a, b, c, current, nextV) || + VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV) ) { + return current; + } + } + } + return null; + } + @SuppressWarnings("unused") + private Vertex checkTriOverlaps1(final Vertex a, final Vertex b, final Vertex c) { + final int count = getOutlineNumber(); + for (int cc = 0; cc < count; cc++) { + final Outline outline = getOutline(cc); + final int vertexCount = outline.getVertexCount(); + for(int i=0; i < vertexCount; i++) { + final Vertex current = outline.getVertex(i); + if(current.isOnCurve() || current == a || current == b || current == c) { + continue; + } + final Vertex nextV = outline.getVertex((i+1)%vertexCount); + final Vertex prevV = outline.getVertex((i+vertexCount-1)%vertexCount); + + //skip neighboring triangles + if(prevV == c || nextV == a) { + continue; + } + if( VectorUtil.isVec3InTriangle3(a.getCoord(), b.getCoord(), c.getCoord(), + current.getCoord(), nextV.getCoord(), prevV.getCoord(), + tmpV1, tmpV2, tmpV3, FloatUtil.EPSILON) ) { return current; } - if(VectorUtil.tri2SegIntersection(a, b, c, prevV, current) - || VectorUtil.tri2SegIntersection(a, b, c, current, nextV) - || VectorUtil.tri2SegIntersection(a, b, c, prevV, nextV)) { + if(VectorUtil.testTri2SegIntersection(a, b, c, prevV, current, FloatUtil.EPSILON) || + VectorUtil.testTri2SegIntersection(a, b, c, current, nextV, FloatUtil.EPSILON) || + VectorUtil.testTri2SegIntersection(a, b, c, prevV, nextV, FloatUtil.EPSILON) ) { return current; } } @@ -461,112 +578,175 @@ public class OutlineShape implements Comparable<OutlineShape> { return null; } - private void transformOutlines2Quadratic() { + private void cleanupOutlines() { + final boolean transformOutlines2Quadratic = VerticesState.QUADRATIC_NURBS != outlineState; int count = getOutlineNumber(); - for (int cc = 0; cc < count; cc++) { + for (int cc = 0; cc < count; cc++) { final Outline outline = getOutline(cc); int vertexCount = outline.getVertexCount(); - for(int i=0; i < vertexCount; i++) { - final Vertex currentVertex = outline.getVertex(i); - final Vertex nextVertex = outline.getVertex((i+1)%vertexCount); - if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) { - final float[] newCoords = VectorUtil.mid(currentVertex.getCoord(), - nextVertex.getCoord()); - final Vertex v = vertexFactory.create(newCoords, 0, 3, true); - i++; - vertexCount++; - outline.addVertex(i, v); - } + if( transformOutlines2Quadratic ) { + for(int i=0; i < vertexCount; i++) { + final Vertex currentVertex = outline.getVertex(i); + final int j = (i+1)%vertexCount; + final Vertex nextVertex = outline.getVertex(j); + if ( !currentVertex.isOnCurve() && !nextVertex.isOnCurve() ) { + VectorUtil.midVec3(tmpV1, currentVertex.getCoord(), nextVertex.getCoord()); + System.err.println("XXX: Cubic: "+i+": "+currentVertex+", "+j+": "+nextVertex); + final Vertex v = vertexFactory.create(tmpV1, 0, 3, true); + i++; + vertexCount++; + addedVerticeCount++; + outline.addVertex(i, v); + } + } } - if(vertexCount <= 0) { + if( 0 >= vertexCount ) { outlines.remove(outline); cc--; count--; - continue; - } - - if( vertexCount > 0 ) { - if(VectorUtil.checkEquality(outline.getVertex(0).getCoord(), - outline.getLastVertex().getCoord())) { - outline.removeVertex(vertexCount-1); - } + } else if( 0 < vertexCount && + VectorUtil.isVec3Equal( outline.getVertex(0).getCoord(), 0, outline.getLastVertex().getCoord(), 0, FloatUtil.EPSILON )) { + outline.removeVertex(vertexCount-1); } } outlineState = VerticesState.QUADRATIC_NURBS; + checkOverlaps(); } - private void generateVertexIds() { + private int generateVertexIds() { int maxVertexId = 0; for(int i=0; i<outlines.size(); i++) { final ArrayList<Vertex> vertices = outlines.get(i).getVertices(); for(int pos=0; pos<vertices.size(); pos++) { - Vertex vert = vertices.get(pos); - vert.setId(maxVertexId); - maxVertexId++; + vertices.get(pos).setId(maxVertexId++); } } + return maxVertexId; } - /** @return the list of concatenated vertices associated with all - * {@code Outline}s of this object + /** + * Return list of concatenated vertices associated with all + * {@code Outline}s of this object. + * <p> + * Vertices are cached until marked dirty. + * </p> + * <p> + * Should always be called <i>after</i> {@link #getTriangles(VerticesState)}, + * since the latter will mark all cached vertices dirty! + * </p> */ - public ArrayList<Vertex> getVertices() { - ArrayList<Vertex> vertices = new ArrayList<Vertex>(); - for(int i=0; i<outlines.size(); i++) { - vertices.addAll(outlines.get(i).getVertices()); + public final ArrayList<Vertex> getVertices() { + final boolean updated; + if( 0 != ( DIRTY_VERTICES & dirtyBits ) ) { + vertices.clear(); + for(int i=0; i<outlines.size(); i++) { + vertices.addAll(outlines.get(i).getVertices()); + } + dirtyBits &= ~DIRTY_VERTICES; + updated = true; + } else { + updated = false; + } + if(Region.DEBUG_INSTANCE) { + System.err.println("OutlineShape.getVertices(): o "+outlines.size()+", v "+vertices.size()+", updated "+updated); } return vertices; } + private void triangulateImpl() { + if( 0 < outlines.size() ) { + sortOutlines(); + generateVertexIds(); + + triangles.clear(); + final Triangulator triangulator2d = Triangulation.create(); + for(int index = 0; index<outlines.size(); index++) { + triangulator2d.addCurve(triangles, outlines.get(index), sharpness); + } + triangulator2d.generate(triangles); + addedVerticeCount += triangulator2d.getAddedVerticeCount(); + triangulator2d.reset(); + } + } + /** - * Triangulate the {@link OutlineShape} generating a list of triangles + * Triangulate the {@link OutlineShape} generating a list of triangles, + * while {@link #transformOutlines(VerticesState)} beforehand. + * <p> + * Triangles are cached until marked dirty. + * </p> * @return an arraylist of triangles representing the filled region * which is produced by the combination of the outlines */ - public ArrayList<Triangle> triangulate() { - if(outlines.size() == 0){ - return null; + public ArrayList<Triangle> getTriangles(final VerticesState destinationType) { + final boolean updated; + if(destinationType != VerticesState.QUADRATIC_NURBS) { + throw new IllegalStateException("destinationType "+destinationType.name()+" not supported (currently "+outlineState.name()+")"); } - sortOutlines(); - generateVertexIds(); - - Triangulator triangulator2d = Triangulation.create(); - for(int index = 0; index<outlines.size(); index++) { - triangulator2d.addCurve(outlines.get(index)); + if( 0 != ( DIRTY_TRIANGLES & dirtyBits ) ) { + cleanupOutlines(); + triangulateImpl(); + updated = true; + dirtyBits |= DIRTY_VERTICES; + dirtyBits &= ~DIRTY_TRIANGLES; + } else { + updated = false; + } + if(Region.DEBUG_INSTANCE) { + System.err.println("OutlineShape.getTriangles().X: "+triangles.size()+", updated "+updated); } - - ArrayList<Triangle> triangles = triangulator2d.generate(); - triangulator2d.reset(); - return triangles; } - /** Sort the outlines from large - * to small depending on the AABox + /** + * Return a transformed instance with all {@link Outline}s are copied and transformed. + * <p> + * Note: Triangulated data is lost in returned instance! + * </p> + */ + public final OutlineShape transform(final AffineTransform t) { + final OutlineShape newOutlineShape = new OutlineShape(vertexFactory); + final int osize = outlines.size(); + for(int i=0; i<osize; i++) { + newOutlineShape.addOutline( outlines.get(i).transform(t, vertexFactory) ); + } + return newOutlineShape; + } + + /** + * Sort the outlines from large + * to small depending on the AABox */ private void sortOutlines() { - Collections.sort(outlines); - Collections.reverse(outlines); + Collections.sort(outlines, reversSizeComparator); } - /** Compare two outline shapes with Bounding Box area - * as criteria. + private static Comparator<Outline> reversSizeComparator = new Comparator<Outline>() { + @Override + public int compare(final Outline o1, final Outline o2) { + return o2.compareTo(o1); // reverse ! + } }; + + /** + * Compare two outline shape's Bounding Box size. + * @see AABBox#getSize() * @see java.lang.Comparable#compareTo(java.lang.Object) */ - public final int compareTo(OutlineShape outline) { - float size = getBounds().getSize(); - float newSize = outline.getBounds().getSize(); - if(size < newSize){ + @Override + public final int compareTo(final OutlineShape other) { + final float thisSize = getBounds().getSize(); + final float otherSize = other.getBounds().getSize(); + if( FloatUtil.isEqual(thisSize, otherSize, FloatUtil.EPSILON) ) { + return 0; + } else if( thisSize < otherSize ){ return -1; - } - else if(size > newSize){ + } else { return 1; } - return 0; } - private final void validateBoundingBox() { + private void validateBoundingBox() { dirtyBits &= ~DIRTY_BOUNDS; bbox.reset(); for (int i=0; i<outlines.size(); i++) { @@ -579,20 +759,21 @@ public class OutlineShape implements Comparable<OutlineShape> { validateBoundingBox(); } return bbox; - } + } /** * @param obj the Object to compare this OutlineShape with - * @return true if {@code obj} is an OutlineShape, not null, - * same outlineState, equal bounds and equal outlines in the same order + * @return true if {@code obj} is an OutlineShape, not null, + * same outlineState, equal bounds and equal outlines in the same order */ - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if( obj == this) { return true; } if( null == obj || !(obj instanceof OutlineShape) ) { return false; - } + } final OutlineShape o = (OutlineShape) obj; if(getOutlineState() != o.getOutlineState()) { return false; @@ -611,19 +792,14 @@ public class OutlineShape implements Comparable<OutlineShape> { return true; } - /** - * @return deep clone of this OutlineShape w/o Region - */ - public OutlineShape clone() { - OutlineShape o; - try { - o = (OutlineShape) super.clone(); - } catch (CloneNotSupportedException e) { throw new InternalError(); } - o.bbox = bbox.clone(); - o.outlines = new ArrayList<Outline>(outlines.size()); - for(int i=0; i<outlines.size(); i++) { - o.outlines.add(outlines.get(i).clone()); - } - return o; - } + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + + @Override + public String toString() { + // Avoid calling this.hashCode() ! + return getClass().getName() + "@" + Integer.toHexString(super.hashCode()); + } } diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java new file mode 100644 index 000000000..cf4d38450 --- /dev/null +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShapeXForm.java @@ -0,0 +1,16 @@ +package com.jogamp.graph.curve; + +import jogamp.graph.geom.plane.AffineTransform; + +public class OutlineShapeXForm { + public final OutlineShape shape; + private AffineTransform t; + + public OutlineShapeXForm(final OutlineShape shape, final AffineTransform t) { + this.shape = shape; + this.t = t; + } + + public final AffineTransform getTransform() { return t; } + public final void setTransform(final AffineTransform t) { this.t = t; } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java index af15f9dc4..023ca8373 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/Region.java +++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java @@ -28,164 +28,384 @@ package com.jogamp.graph.curve; import java.util.ArrayList; +import java.util.List; +import jogamp.graph.geom.plane.AffineTransform; import jogamp.opengl.Debug; -import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.curve.opengl.GLRegion; +import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.math.geom.Frustum; +import com.jogamp.opengl.util.texture.TextureSequence; -/** Abstract Outline shape GL representation - * define the method an OutlineShape(s) is - * binded rendered. - * - * @see GLRegion +/** + * Abstract Outline shape representation define the method an OutlineShape(s) + * is bound and rendered. + * + * @see GLRegion */ public abstract class Region { - - /** Debug flag for region impl (graph.curve) - */ + + /** Debug flag for region impl (graph.curve) */ public static final boolean DEBUG = Debug.debug("graph.curve"); - - public static final boolean DEBUG_INSTANCE = false; + public static final boolean DEBUG_INSTANCE = Debug.debug("graph.curve.Instance"); - /** View based Anti-Aliasing, A Two pass region rendering, slower - * and more resource hungry (FBO), but AA is perfect. - * Otherwise the default fast one pass MSAA region rendering is being used. + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> + * MSAA based Anti-Aliasing, a two pass region rendering, slower and more + * resource hungry (FBO), but providing fast MSAA in case + * the whole scene is not rendered with MSAA. + * </p> */ - public static final int VBAA_RENDERING_BIT = 1 << 0; + public static final int MSAA_RENDERING_BIT = 1 << 0; - /** Use non uniform weights [0.0 .. 1.9] for curve region rendering. - * Otherwise the default weight 1.0 for uniform curve region rendering is being applied. + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> + * View based Anti-Aliasing, a two pass region rendering, slower and more + * resource hungry (FBO), but AA is perfect. Otherwise the default fast one + * pass MSAA region rendering is being used. + * </p> + */ + public static final int VBAA_RENDERING_BIT = 1 << 1; + + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> + * Use non uniform weights [0.0 .. 1.9] for curve region rendering. + * Otherwise the default weight 1.0 for uniform curve region rendering is + * being applied. + * </p> */ - public static final int VARIABLE_CURVE_WEIGHT_BIT = 1 << 1; + public static final int VARWEIGHT_RENDERING_BIT = 1 << 8; + + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> + * If set, a color channel attribute per vertex is added to the stream, + * otherwise only the + * {@link com.jogamp.graph.curve.opengl.RegionRenderer#setColorStatic(javax.media.opengl.GL2ES2, float, float, float, float) static color} + * is being used. + * </p> + */ + public static final int COLORCHANNEL_RENDERING_BIT = 1 << 9; + + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> + * If set, a color texture is used to determine the color. + * </p> + */ + public static final int COLORTEXTURE_RENDERING_BIT = 1 << 10; + + /** Default maximum {@link #getQuality() quality}, {@value}. */ + public static final int MAX_QUALITY = 1; + + public static final int DEFAULT_TWO_PASS_TEXTURE_UNIT = 0; - public static final int TWO_PASS_DEFAULT_TEXTURE_UNIT = 0; + protected static final int DIRTY_SHAPE = 1 << 0 ; + protected static final int DIRTY_STATE = 1 << 1 ; private final int renderModes; - private boolean dirty = true; - protected int numVertices = 0; + private int quality; + private int dirty = DIRTY_SHAPE | DIRTY_STATE; + private int numVertices = 0; protected final AABBox box = new AABBox(); - protected ArrayList<Triangle> triangles = new ArrayList<Triangle>(); - protected ArrayList<Vertex> vertices = new ArrayList<Vertex>(); + protected Frustum frustum = null; - public static boolean isVBAA(int renderModes) { - return 0 != ( renderModes & Region.VBAA_RENDERING_BIT ); + public static boolean isVBAA(final int renderModes) { + return 0 != (renderModes & Region.VBAA_RENDERING_BIT); } - /** Check if render mode capable of non uniform weights - * @param renderModes bit-field of modes, e.g. {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, - * {@link Region#VBAA_RENDERING_BIT} - * @return true of capable of non uniform weights + public static boolean isMSAA(final int renderModes) { + return 0 != (renderModes & Region.MSAA_RENDERING_BIT); + } + + public static boolean isTwoPass(final int renderModes) { + return 0 != ( renderModes & ( Region.VBAA_RENDERING_BIT | Region.MSAA_RENDERING_BIT) ); + } + + /** + * Returns true if render mode capable of variable weights, + * i.e. the bit {@link #VARWEIGHT_RENDERING_BIT} is set, + * otherwise false. + */ + public static boolean hasVariableWeight(final int renderModes) { + return 0 != (renderModes & Region.VARWEIGHT_RENDERING_BIT); + } + + /** + * Returns true if render mode has a color channel, + * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, + * otherwise false. + */ + public static boolean hasColorChannel(final int renderModes) { + return 0 != (renderModes & Region.COLORCHANNEL_RENDERING_BIT); + } + + /** + * Returns true if render mode has a color texture, + * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, + * otherwise false. */ - public static boolean isNonUniformWeight(int renderModes) { - return 0 != ( renderModes & Region.VARIABLE_CURVE_WEIGHT_BIT ); + public static boolean hasColorTexture(final int renderModes) { + return 0 != (renderModes & Region.COLORTEXTURE_RENDERING_BIT); + } + + public static String getRenderModeString(final int renderModes) { + final String curveS = hasVariableWeight(renderModes) ? "-curve" : ""; + final String cChanS = hasColorChannel(renderModes) ? "-cols" : ""; + final String cTexS = hasColorTexture(renderModes) ? "-ctex" : ""; + if( Region.isVBAA(renderModes) ) { + return "vbaa"+curveS+cChanS+cTexS; + } else if( Region.isMSAA(renderModes) ) { + return "msaa"+curveS+cChanS+cTexS; + } else { + return "norm"+curveS+cChanS+cTexS; + } } - protected Region(int regionRenderModes) { + protected Region(final int regionRenderModes) { this.renderModes = regionRenderModes; + this.quality = MAX_QUALITY; } - /** Get current Models - * @return bit-field of render modes + // FIXME: Better handling of impl. buffer growth .. ! + // protected abstract void setupInitialComponentCount(int attributeCount, int indexCount); + + protected abstract void pushVertex(final float[] coords, final float[] texParams, float[] rgba); + protected abstract void pushIndex(int idx); + + /** + * Return bit-field of render modes, see {@link GLRegion#create(int, TextureSequence)}. */ - public final int getRenderModes() { - return renderModes; + public final int getRenderModes() { return renderModes; } + + /** See {@link #MAX_QUALITY} */ + public final int getQuality() { return quality; } + + /** See {@link #MAX_QUALITY} */ + public final void setQuality(final int q) { quality=q; } + + protected void clearImpl() { + dirty = DIRTY_SHAPE | DIRTY_STATE; + numVertices = 0; + box.reset(); } - /** Check if current Region is using VBAA - * @return true if capable of two pass rendering - VBAA + /** + * Returns true if capable of two pass rendering - VBAA, otherwise false. */ - public boolean isVBAA() { - return Region.isVBAA(renderModes); + public final boolean isVBAA() { + return Region.isVBAA(renderModes); } - /** Check if current instance uses non uniform weights - * @return true if capable of nonuniform weights + /** + * Returns true if capable of two pass rendering - MSAA, otherwise false. */ - public boolean isNonUniformWeight() { - return Region.isNonUniformWeight(renderModes); + public final boolean isMSAA() { + return Region.isMSAA(renderModes); } - /** Get the current number of vertices associated - * with this region. This number is not necessary equal to - * the OGL bound number of vertices. - * @return vertices count + /** + * Returns true if capable of variable weights, otherwise false. */ - public final int getNumVertices(){ - return numVertices; + public final boolean hasVariableWeight() { + return Region.hasVariableWeight(renderModes); } - /** Adds a {@link Triangle} object to the Region - * This triangle will be bound to OGL objects - * on the next call to {@code update} - * @param tri a triangle object - * - * @see update(GL2ES2) + /** + * Returns true if render mode has a color channel, + * i.e. the bit {@link #COLORCHANNEL_RENDERING_BIT} is set, + * otherwise false. */ - public void addTriangle(Triangle tri) { - triangles.add(tri); - setDirty(true); + public boolean hasColorChannel() { + return Region.hasColorChannel(renderModes); } - /** Adds a list of {@link Triangle} objects to the Region - * These triangles are to be binded to OGL objects - * on the next call to {@code update} - * @param tris an arraylist of triangle objects - * - * @see update(GL2ES2) + /** + * Returns true if render mode has a color texture, + * i.e. the bit {@link #COLORTEXTURE_RENDERING_BIT} is set, + * otherwise false. */ - public void addTriangles(ArrayList<Triangle> tris) { - triangles.addAll(tris); - setDirty(true); + public boolean hasColorTexture() { + return Region.hasColorTexture(renderModes); } - /** Adds a {@link Vertex} object to the Region - * This vertex will be bound to OGL objects - * on the next call to {@code update} - * @param vert a vertex objects - * - * @see update(GL2ES2) + + /** See {@link #setFrustum(Frustum)} */ + public final Frustum getFrustum() { return frustum; } + + /** + * Set {@link Frustum} culling for {@link #addOutlineShape(OutlineShape, AffineTransform, float[])}. */ - public void addVertex(Vertex vert) { - vertices.add(vert); + public final void setFrustum(final Frustum frustum) { + this.frustum = frustum; + } + + final float[] coordsEx = new float[3]; + + private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { + if( null != transform ) { + final float[] coordsIn = vertIn.getCoord(); + transform.transform(coordsIn, coordsEx); + coordsEx[2] = coordsIn[2]; + box.resize(coordsEx[0], coordsEx[1], coordsEx[2]); + pushVertex(coordsEx, vertIn.getTexCoord(), rgba); + } else { + box.resize(vertIn.getX(), vertIn.getY(), vertIn.getZ()); + pushVertex(vertIn.getCoord(), vertIn.getTexCoord(), rgba); + } numVertices++; - setDirty(true); } - /** Adds a list of {@link Vertex} objects to the Region - * These vertices are to be binded to OGL objects - * on the next call to {@code update} - * @param verts an arraylist of vertex objects - * - * @see update(GL2ES2) - */ - public void addVertices(ArrayList<Vertex> verts) { - vertices.addAll(verts); - numVertices = vertices.size(); - setDirty(true); + private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { + pushIndex(numVertices); + pushNewVertexImpl(vertIn, transform, rgba); } + private final AABBox tmpBox = new AABBox(); + /** - * @return the AxisAligned bounding box of - * current region + * Add the given {@link OutlineShape} to this region with the given optional {@link AffineTransform}. + * <p> + * In case {@link #setFrustum(Frustum) frustum culling is set}, the {@link OutlineShape} + * is dropped if it's {@link OutlineShape#getBounds() bounding-box} is fully outside of the frustum. + * The optional {@link AffineTransform} is applied to the bounding-box beforehand. + * </p> + * @param rgbaColor TODO */ - public final AABBox getBounds(){ + public final void addOutlineShape(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { + if( null != frustum ) { + final AABBox shapeBox = shape.getBounds(); + final AABBox shapeBoxT; + if( null != t ) { + t.transform(shapeBox, tmpBox); + shapeBoxT = tmpBox; + } else { + shapeBoxT = shapeBox; + } + if( frustum.isAABBoxOutside(shapeBoxT) ) { + if(DEBUG_INSTANCE) { + System.err.println("Region.addOutlineShape(): Dropping outside shapeBoxT: "+shapeBoxT); + } + return; + } + } + final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); + final ArrayList<Vertex> vertsIn = shape.getVertices(); + if(DEBUG_INSTANCE) { + final int addedVerticeCount = shape.getAddedVerticeCount(); + final int verticeCount = vertsIn.size() + addedVerticeCount; + final int indexCount = trisIn.size() * 3; + System.err.println("Region.addOutlineShape().0: tris: "+trisIn.size()+", verts "+vertsIn.size()+", transform "+t); + System.err.println("Region.addOutlineShape().0: VerticeCount "+vertsIn.size()+" + "+addedVerticeCount+" = "+verticeCount); + System.err.println("Region.addOutlineShape().0: IndexCount "+indexCount); + } + // setupInitialComponentCount(verticeCount, indexCount); // FIXME: Use it ? + + final int idxOffset = numVertices; + int vertsVNewIdxCount = 0, vertsTMovIdxCount = 0, vertsTNewIdxCount = 0, tris = 0; + final int vertsDupCountV = 0, vertsDupCountT = 0, vertsKnownMovedT = 0; + if( vertsIn.size() >= 3 ) { + if(DEBUG_INSTANCE) { + System.err.println("Region.addOutlineShape(): Processing Vertices"); + } + for(int i=0; i<vertsIn.size(); i++) { + pushNewVertexImpl(vertsIn.get(i), t, rgbaColor); + vertsVNewIdxCount++; + } + if(DEBUG_INSTANCE) { + System.err.println("Region.addOutlineShape(): Processing Triangles"); + } + for(int i=0; i<trisIn.size(); i++) { + final Triangle triIn = trisIn.get(i); + if(Region.DEBUG_INSTANCE) { + System.err.println("T["+i+"]: "+triIn); + } + // triEx.addVertexIndicesOffset(idxOffset); + // triangles.add( triEx ); + final Vertex[] triInVertices = triIn.getVertices(); + final int tv0Idx = triInVertices[0].getId(); + if( Integer.MAX_VALUE-idxOffset > tv0Idx ) { // Integer.MAX_VALUE != i0 // FIXME: renderer uses SHORT! + // valid 'known' idx - move by offset + if(Region.DEBUG_INSTANCE) { + System.err.println("T["+i+"]: Moved "+tv0Idx+" + "+idxOffset+" -> "+(tv0Idx+idxOffset)); + } + pushIndex(tv0Idx+idxOffset); + pushIndex(triInVertices[1].getId()+idxOffset); + pushIndex(triInVertices[2].getId()+idxOffset); + vertsTMovIdxCount+=3; + } else { + // invalid idx - generate new one + if(Region.DEBUG_INSTANCE) { + System.err.println("T["+i+"]: New Idx "+numVertices); + } + pushNewVertexIdxImpl(triInVertices[0], t, rgbaColor); + pushNewVertexIdxImpl(triInVertices[1], t, rgbaColor); + pushNewVertexIdxImpl(triInVertices[2], t, rgbaColor); + vertsTNewIdxCount+=3; + } + tris++; + } + } + if(DEBUG_INSTANCE) { + System.err.println("Region.addOutlineShape().X: idxOffset "+idxOffset+", tris: "+tris+", verts [idx "+vertsTNewIdxCount+", add "+vertsTNewIdxCount+" = "+(vertsVNewIdxCount+vertsTNewIdxCount)+"]"); + System.err.println("Region.addOutlineShape().X: verts: idx[v-new "+vertsVNewIdxCount+", t-new "+vertsTNewIdxCount+" = "+(vertsVNewIdxCount+vertsTNewIdxCount)+"]"); + System.err.println("Region.addOutlineShape().X: verts: idx t-moved "+vertsTMovIdxCount+", numVertices "+numVertices); + System.err.println("Region.addOutlineShape().X: verts: v-dups "+vertsDupCountV+", t-dups "+vertsDupCountT+", t-known "+vertsKnownMovedT); + // int vertsDupCountV = 0, vertsDupCountT = 0; + System.err.println("Region.addOutlineShape().X: box "+box); + } + markShapeDirty(); + } + + public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) { + for (int i = 0; i < shapes.size(); i++) { + addOutlineShape(shapes.get(i), transform, rgbaColor); + } + } + + /** @return the AxisAligned bounding box of current region */ + public final AABBox getBounds() { return box; } - /** Check if this region is dirty. A region is marked dirty - * when new Vertices, Triangles, and or Lines are added after a - * call to update() - * @return true if region is Dirty, false otherwise - * - * @see update(GL2ES2) + /** + * Mark this region's shape dirty, i.e. it's + * Vertices, Triangles, and or Lines changed. + */ + public final void markShapeDirty() { + dirty |= DIRTY_SHAPE; + } + /** Returns true if this region's shape are dirty, see {@link #markShapeDirty()}. */ + public final boolean isShapeDirty() { + return 0 != ( dirty & DIRTY_SHAPE ) ; + } + /** + * Mark this region's state dirty, i.e. + * it's render attributes or parameters changed. + */ + public final void markStateDirty() { + dirty |= DIRTY_STATE; + } + /** Returns true if this region's state is dirty, see {@link #markStateDirty()}. */ + public final boolean isStateDirty() { + return 0 != ( dirty & DIRTY_STATE ) ; + } + + /** + * See {@link #markShapeDirty()} and {@link #markStateDirty()}. */ - public final boolean isDirty() { - return dirty; + protected final void clearDirtyBits(final int v) { + dirty &= ~v; } + protected final int getDirtyBits() { return dirty; } - protected final void setDirty(boolean v) { - dirty = v; + public String toString() { + return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java index 63713887b..654f9a692 100755..100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -27,103 +27,125 @@ */
package com.jogamp.graph.curve.opengl;
+import javax.media.opengl.GL;
+import javax.media.opengl.GL2ES2;
-import java.util.ArrayList;
+import jogamp.graph.curve.opengl.VBORegion2PMSAAES2;
+import jogamp.graph.curve.opengl.VBORegion2PVBAAES2;
+import jogamp.graph.curve.opengl.VBORegionSPES2;
-import javax.media.opengl.GL2ES2;
import com.jogamp.opengl.util.PMVMatrix;
-
-import com.jogamp.graph.curve.OutlineShape;
+import com.jogamp.opengl.util.texture.TextureSequence;
import com.jogamp.graph.curve.Region;
-import com.jogamp.graph.geom.Triangle;
-import com.jogamp.graph.geom.Vertex;
-import jogamp.graph.curve.opengl.RegionFactory;
+import com.jogamp.graph.curve.OutlineShape;
/** A GLRegion is the OGL binding of one or more OutlineShapes
* Defined by its vertices and generated triangles. The Region
- * defines the final shape of the OutlineShape(s), which shall produced a shaded
+ * defines the final shape of the OutlineShape(s), which shall produced a shaded
* region on the screen.
- *
- * Implementations of the GLRegion shall take care of the OGL
+ *
+ * Implementations of the GLRegion shall take care of the OGL
* binding of the depending on its context, profile.
- *
- * @see Region, RegionFactory, OutlineShape
+ *
+ * @see Region
+ * @see OutlineShape
*/
-public abstract class GLRegion extends Region {
-
- /** Create an ogl {@link GLRegion} defining the list of {@link OutlineShape}.
- * Combining the Shapes into single buffers.
- * @return the resulting Region inclusive the generated region
+public abstract class GLRegion extends Region {
+
+ /**
+ * Create a GLRegion using the passed render mode
+ *
+ * <p> In case {@link Region#VBAA_RENDERING_BIT} is being requested the default texture unit
+ * {@link Region#DEFAULT_TWO_PASS_TEXTURE_UNIT} is being used.</p>
+ * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT}
+ * @param colorTexSeq optional {@link TextureSequence} for {@link Region#COLORTEXTURE_RENDERING_BIT} rendering mode.
*/
- public static GLRegion create(OutlineShape[] outlineShapes, int renderModes) {
- final GLRegion region = RegionFactory.create(renderModes);
-
- int numVertices = region.getNumVertices();
-
- for(int index=0; index<outlineShapes.length; index++) {
- OutlineShape outlineShape = outlineShapes[index];
- outlineShape.transformOutlines(OutlineShape.VerticesState.QUADRATIC_NURBS);
-
- ArrayList<Triangle> triangles = outlineShape.triangulate();
- region.addTriangles(triangles);
-
- ArrayList<Vertex> vertices = outlineShape.getVertices();
- for(int pos=0; pos < vertices.size(); pos++){
- Vertex vert = vertices.get(pos);
- vert.setId(numVertices++);
- }
- region.addVertices(vertices);
+ public static GLRegion create(int renderModes, final TextureSequence colorTexSeq) {
+ if( null != colorTexSeq ) {
+ renderModes |= Region.COLORTEXTURE_RENDERING_BIT;
+ } else if( Region.hasColorTexture(renderModes) ) {
+ throw new IllegalArgumentException("COLORTEXTURE_RENDERING_BIT set but null TextureSequence");
+ }
+ if( isVBAA(renderModes) ) {
+ return new VBORegion2PVBAAES2(renderModes, colorTexSeq, Region.DEFAULT_TWO_PASS_TEXTURE_UNIT);
+ } else if( isMSAA(renderModes) ) {
+ return new VBORegion2PMSAAES2(renderModes, colorTexSeq, Region.DEFAULT_TWO_PASS_TEXTURE_UNIT);
+ } else {
+ return new VBORegionSPES2(renderModes, colorTexSeq);
}
-
- return region;
}
- /**
- * Create an ogl {@link GLRegion} defining this {@link OutlineShape}
- * @return the resulting Region.
- */
- public static GLRegion create(OutlineShape outlineShape, int renderModes) {
- final GLRegion region = RegionFactory.create(renderModes);
-
- outlineShape.transformOutlines(OutlineShape.VerticesState.QUADRATIC_NURBS);
- ArrayList<Triangle> triangles = (ArrayList<Triangle>) outlineShape.triangulate();
- ArrayList<Vertex> vertices = (ArrayList<Vertex>) outlineShape.getVertices();
- region.addVertices(vertices);
- region.addTriangles(triangles);
- return region;
- }
-
- protected GLRegion(int renderModes) {
+ protected final TextureSequence colorTexSeq;
+
+ protected GLRegion(final int renderModes, final TextureSequence colorTexSeq) {
super(renderModes);
+ this.colorTexSeq = colorTexSeq;
}
-
- /** Updates a graph region by updating the ogl related
- * objects for use in rendering if {@link #isDirty()}.
- * <p>Allocates the ogl related data and initializes it the 1st time.<p>
- * <p>Called by {@link #draw(GL2ES2, RenderState, int, int, int)}.</p>
- * @param rs TODO
+
+ /**
+ * Updates a graph region by updating the ogl related
+ * objects for use in rendering if {@link #isShapeDirty()}.
+ * <p>Allocates the ogl related data and initializes it the 1st time.<p>
+ * <p>Called by {@link #draw(GL2ES2, RenderState, int, int, int)}.</p>
*/
- protected abstract void update(GL2ES2 gl, RenderState rs);
-
- /** Delete and clean the associated OGL
- * objects
+ protected abstract void updateImpl(final GL2ES2 gl);
+
+ protected abstract void destroyImpl(final GL2ES2 gl);
+
+ protected abstract void clearImpl(final GL2ES2 gl);
+
+ /**
+ * Clears all data, i.e. triangles, vertices etc.
*/
- public abstract void destroy(GL2ES2 gl, RenderState rs);
-
- /** Renders the associated OGL objects specifying
+ public void clear(final GL2ES2 gl) {
+ clearImpl(gl);
+ clearImpl();
+ }
+
+ /**
+ * Delete and clear the associated OGL objects.
+ */
+ public final void destroy(final GL2ES2 gl) {
+ clear(gl);
+ destroyImpl(gl);
+ }
+
+ /**
+ * Renders the associated OGL objects specifying
* current width/hight of window for multi pass rendering
* of the region.
+ * <p>
+ * User shall consider {@link RegionRenderer#enable(GL2ES2, boolean) enabling}
+ * the renderer beforehand and {@link RegionRenderer#enable(GL2ES2, boolean) disabling}
+ * it afterwards when used in conjunction with other renderer.
+ * </p>
+ * <p>
+ * Users shall also consider setting the {@link GL#glClearColor(float, float, float, float) clear-color}
+ * appropriately:
+ * <ul>
+ * <li>If {@link GL#GL_BLEND blending} is enabled, <i>RGB</i> shall be set to text color, otherwise
+ * blending will reduce the alpha seam's contrast and the font will appear thinner.</li>
+ * <li>If {@link GL#GL_BLEND blending} is disabled, <i>RGB</i> shall be set to the actual desired background.</li>
+ * </ul>
+ * The <i>alpha</i> component shall be set to zero.
+ * Note: If {@link GL#GL_BLEND blending} is enabled, the
+ * {@link RegionRenderer} might need to be
+ * {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) created}
+ * with the appropriate {@link RegionRenderer.GLCallback callbacks}.
+ * </p>
* @param matrix current {@link PMVMatrix}.
- * @param rs the RenderState to be used
- * @param vp_width current screen width
- * @param vp_height current screen height
- * @param texWidth desired texture width for multipass-rendering.
- * The actual used texture-width is written back when mp rendering is enabled, otherwise the store is untouched.
+ * @param renderer the {@link RegionRenderer} to be used
+ * @param sampleCount desired multisampling sample count for msaa-rendering.
+ * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched.
+ * @see RegionRenderer#enable(GL2ES2, boolean)
*/
- public final void draw(GL2ES2 gl, RenderState rs, int vp_width, int vp_height, int[/*1*/] texWidth) {
- update(gl, rs);
- drawImpl(gl, rs, vp_width, vp_height, texWidth);
+ public final void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount) {
+ if( isShapeDirty() ) {
+ updateImpl(gl);
+ }
+ drawImpl(gl, renderer, sampleCount);
+ clearDirtyBits(DIRTY_SHAPE|DIRTY_STATE);
}
-
- protected abstract void drawImpl(GL2ES2 gl, RenderState rs, int vp_width, int vp_height, int[/*1*/] texWidth);
+
+ protected abstract void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount);
}
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java index 2f078d7bb..a0f54d3b5 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java @@ -27,58 +27,543 @@ */ package com.jogamp.graph.curve.opengl; +import java.io.IOException; +import java.util.Iterator; + +import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLES2; import javax.media.opengl.GLException; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import jogamp.graph.curve.opengl.shader.AttributeNames; +import jogamp.graph.curve.opengl.shader.UniformNames; -import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; +import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.graph.curve.Region; -public abstract class RegionRenderer extends Renderer { +/** + * OpenGL {@link Region} renderer + * <p> + * All OpenGL related operations regarding {@link Region}s + * are passed through an instance of this class. + * </p> + */ +public class RegionRenderer { + protected static final boolean DEBUG = Region.DEBUG; + protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE; + + /** + * May be passed to + * {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) RegionRenderer ctor}, + * e.g. + * <ul> + * <li>{@link RegionRenderer#defaultBlendEnable}</li> + * <li>{@link RegionRenderer#defaultBlendDisable}</li> + * </ul> + */ + public interface GLCallback { + /** + * @param gl a current GL object + * @param renderer {@link RegionRenderer} calling this method. + */ + void run(GL gl, RegionRenderer renderer); + } + + /** + * Default {@link GL#GL_BLEND} <i>enable</i> {@link GLCallback}, + * turning-off depth writing via {@link GL#glDepthMask(boolean)} if {@link RenderState#BITHINT_GLOBAL_DEPTH_TEST_ENABLED} is set + * and turning-on the {@link GL#GL_BLEND} state. + * <p> + * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}, + * which will cause {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method} + * to set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function} + * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering. + * </p> + * @see #create(RenderState, GLCallback, GLCallback) + * @see #enable(GL2ES2, boolean) + */ + public static final GLCallback defaultBlendEnable = new GLCallback() { + @Override + public void run(final GL gl, final RegionRenderer renderer) { + if( renderer.rs.isHintMaskSet(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED) ) { + gl.glDepthMask(false); + // gl.glDisable(GL.GL_DEPTH_TEST); + // gl.glDepthFunc(GL.GL_ALWAYS); + } + gl.glEnable(GL.GL_BLEND); + gl.glBlendEquation(GL.GL_FUNC_ADD); // default + renderer.rs.setHintMask(RenderState.BITHINT_BLENDING_ENABLED); + } + }; + + /** + * Default {@link GL#GL_BLEND} <i>disable</i> {@link GLCallback}, + * simply turning-off the {@link GL#GL_BLEND} state + * and turning-on depth writing via {@link GL#glDepthMask(boolean)} if {@link RenderState#BITHINT_GLOBAL_DEPTH_TEST_ENABLED} is set. + * <p> + * Implementation also clears {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}. + * </p> + * @see #create(RenderState, GLCallback, GLCallback) + * @see #enable(GL2ES2, boolean) + */ + public static final GLCallback defaultBlendDisable = new GLCallback() { + @Override + public void run(final GL gl, final RegionRenderer renderer) { + renderer.rs.clearHintMask(RenderState.BITHINT_BLENDING_ENABLED); + gl.glDisable(GL.GL_BLEND); + if( renderer.rs.isHintMaskSet(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED) ) { + // gl.glEnable(GL.GL_DEPTH_TEST); + // gl.glDepthFunc(GL.GL_LESS); + gl.glDepthMask(true); + } + } + }; - /** + /** * Create a Hardware accelerated Region Renderer. - * @param rs the used {@link RenderState} - * @param renderModes bit-field of modes, e.g. {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, {@link Region#VBAA_RENDERING_BIT} + * <p> + * The optional {@link GLCallback}s <code>enableCallback</code> and <code>disableCallback</code> + * maybe used to issue certain tasks at {@link #enable(GL2ES2, boolean)}.<br/> + * For example, instances {@link #defaultBlendEnable} and {@link #defaultBlendDisable} + * can be utilized to enable and disable {@link GL#GL_BLEND}. + * </p> + * @param rs the used {@link RenderState} + * @param enableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at + * {@link #init(GL2ES2, int) init(gl)} and {@link #enable(GL2ES2, boolean) enable(gl, true)}. + * @param disableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at + * {@link #enable(GL2ES2, boolean) enable(gl, false)}. * @return an instance of Region Renderer + * @see #enable(GL2ES2, boolean) */ - public static RegionRenderer create(RenderState rs, int renderModes) { - return new jogamp.graph.curve.opengl.RegionRendererImpl01(rs, renderModes); + public static RegionRenderer create(final RenderState rs, final GLCallback enableCallback, + final GLCallback disableCallback) { + return new RegionRenderer(rs, enableCallback, disableCallback); } - - protected RegionRenderer(RenderState rs, int renderModes) { - super(rs, renderModes); + + private final RenderState rs; + + private final GLCallback enableCallback; + private final GLCallback disableCallback; + + private int vp_width; + private int vp_height; + private boolean initialized; + private boolean vboSupported = false; + + public final boolean isInitialized() { return initialized; } + + /** Return width of current viewport */ + public final int getWidth() { return vp_width; } + /** Return height of current viewport */ + public final int getHeight() { return vp_height; } + + public final PMVMatrix getMatrix() { return rs.getMatrix(); } + + ////////////////////////////////////// + + /** + * @param rs the used {@link RenderState} + */ + protected RegionRenderer(final RenderState rs, final GLCallback enableCallback, final GLCallback disableCallback) { + this.rs = rs; + this.enableCallback = enableCallback; + this.disableCallback = disableCallback; } - - - /** Render an {@link OutlineShape} in 3D space at the position provided - * the triangles of the shapes will be generated, if not yet generated - * @param region the OutlineShape to Render. - * @param position the initial translation of the outlineShape. - * @param texWidth desired texture width for multipass-rendering. - * The actual used texture-width is written back when mp rendering is enabled, otherwise the store is untouched. - * @throws Exception if HwRegionRenderer not initialized + + public final boolean isVBOSupported() { return vboSupported; } + + /** + * Initialize shader and bindings for GPU based rendering bound to the given GL object's GLContext + * if not initialized yet. + * <p>Leaves the renderer enabled, ie ShaderState.</p> + * <p>Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, int)}</p> + * + * @param gl referencing the current GLContext to which the ShaderState is bound to + * @param renderModes + * @throws GLException if initialization failed */ - public final void draw(GL2ES2 gl, Region region, float[] position, int[/*1*/] texWidth) { - if(!isInitialized()) { - throw new GLException("RegionRenderer: not initialized!"); - } - if( !areRenderModesCompatible(region) ) { - throw new GLException("Incompatible render modes, : region modes "+region.getRenderModes()+ - " doesn't contain renderer modes "+this.getRenderModes()); - } - drawImpl(gl, region, position, texWidth); + public final void init(final GL2ES2 gl, final int renderModes) throws GLException { + if(initialized){ + return; + } + vboSupported = gl.isFunctionAvailable("glGenBuffers") && + gl.isFunctionAvailable("glBindBuffer") && + gl.isFunctionAvailable("glBufferData") && + gl.isFunctionAvailable("glDrawElements") && + gl.isFunctionAvailable("glVertexAttribPointer") && + gl.isFunctionAvailable("glDeleteBuffers"); + + if(DEBUG) { + System.err.println("TextRendererImpl01: VBO Supported = " + isVBOSupported()); + } + + if(!vboSupported){ + throw new GLException("VBO not supported"); + } + + rs.attachTo(gl); + + if( null != enableCallback ) { + enableCallback.run(gl, this); + } + initialized = true; + } + + public final void destroy(final GL2ES2 gl) { + if(!initialized){ + if(DEBUG_INSTANCE) { + System.err.println("TextRenderer: Not initialized!"); + } + return; + } + for(final Iterator<IntObjectHashMap.Entry> i = shaderPrograms.iterator(); i.hasNext(); ) { + final ShaderProgram sp = (ShaderProgram) i.next().getValue(); + sp.destroy(gl); + } + shaderPrograms.clear(); + rs.destroy(gl); + initialized = false; } - + + public final RenderState getRenderState() { return rs; } + /** - * Usually just dispatched the draw call to the Region's draw implementation, - * e.g. {@link com.jogamp.graph.curve.opengl.GLRegion#draw(GL2ES2, RenderState, int, int, int[]) GLRegion#draw(GL2ES2, RenderState, int, int, int[])}. + * Enabling or disabling the {@link #getRenderState() RenderState}'s + * {@link RenderState#getShaderProgram() shader program}. + * <p> + * In case enable and disable {@link GLCallback}s are setup via {@link #create(RenderState, GLCallback, GLCallback)}, + * they will be called before toggling the shader program. + * </p> + * @see #create(RenderState, GLCallback, GLCallback) */ - protected abstract void drawImpl(GL2ES2 gl, Region region, float[] position, int[] texWidth); + public final void enable(final GL2ES2 gl, final boolean enable) { + if( enable ) { + if( null != enableCallback ) { + enableCallback.run(gl, this); + } + } else { + if( null != disableCallback ) { + disableCallback.run(gl, this); + } + } + if( !enable ) { + final ShaderProgram sp = rs.getShaderProgram(); + if( null != sp ) { + sp.useProgram(gl, false); + } + } + } + + /** No PMVMatrix operation is performed here. PMVMatrix is marked dirty. */ + public final void reshapeNotify(final int width, final int height) { + this.vp_width = width; + this.vp_height = height; + } + + public final void reshapePerspective(final float angle, final int width, final int height, final float near, final float far) { + this.vp_width = width; + this.vp_height = height; + final float ratio = (float)width/(float)height; + final PMVMatrix p = rs.getMatrix(); + p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + p.glLoadIdentity(); + p.gluPerspective(angle, ratio, near, far); + } + + public final void reshapeOrtho(final int width, final int height, final float near, final float far) { + this.vp_width = width; + this.vp_height = height; + final PMVMatrix p = rs.getMatrix(); + p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + p.glLoadIdentity(); + p.glOrthof(0, width, 0, height, near, far); + } + + // + // Shader Management + // + + private static final String SHADER_SRC_SUB = ""; + private static final String SHADER_BIN_SUB = "bin"; + + private static String GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n"; + private static String GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n"; + private static String GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT "; + private static String GLSL_CONST_SAMPLE_COUNT = "const float sample_count = "; + private static String GLSL_MAIN_BEGIN = "void main (void)\n{\n"; + private static final String gcuTexture2D = "gcuTexture2D"; - @Override - protected void destroyImpl(GL2ES2 gl) { - // nop + private String getVersionedShaderName() { + return "curverenderer01"; + } + + // FIXME: Really required to have sampler2D def. precision ? If not, we can drop getFragmentShaderPrecision(..) and use default ShaderCode .. + private static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n"; + + private final String getFragmentShaderPrecision(final GL2ES2 gl) { + if( gl.isGLES() ) { + return es2_precision_fp; + } + if( ShaderCode.requiresGL3DefaultPrecision(gl) ) { + return ShaderCode.gl3_default_precision_fp; + } + return null; + } + + private static enum ShaderModeSelector1 { + /** Pass-1: Curve Simple */ + PASS1_SIMPLE("curve", "_simple", 0), + /** Pass-1: Curve Varying Weight */ + PASS1_WEIGHT("curve", "_weight", 0), + /** Pass-2: MSAA */ + PASS2_MSAA("msaa", "", 0), + /** Pass-2: VBAA Flipquad3, 1 sample */ + PASS2_VBAA_QUAL0_SAMPLES1("vbaa", "_flipquad3", 1), + /** Pass-2: VBAA Flipquad3, 2 samples */ + PASS2_VBAA_QUAL0_SAMPLES2("vbaa", "_flipquad3", 2), + /** Pass-2: VBAA Flipquad3, 4 samples */ + PASS2_VBAA_QUAL0_SAMPLES4("vbaa", "_flipquad3", 4), + /** Pass-2: VBAA Flipquad3, 8 samples */ + PASS2_VBAA_QUAL0_SAMPLES8("vbaa", "_flipquad3", 8), + + /** Pass-2: VBAA Brute-Force, Odd, 1 samples */ + PASS2_VBAA_QUAL1_SAMPLES1("vbaa", "_bforce_odd", 1), + /** Pass-2: VBAA Brute-Force, Even, 2 samples */ + PASS2_VBAA_QUAL1_SAMPLES2("vbaa", "_bforce_even", 2), + /** Pass-2: VBAA Brute-Force, Odd, 3 samples */ + PASS2_VBAA_QUAL1_SAMPLES3("vbaa", "_bforce_odd", 3), + /** Pass-2: VBAA Brute-Force, Even, 4 samples */ + PASS2_VBAA_QUAL1_SAMPLES4("vbaa", "_bforce_even", 4), + /** Pass-2: VBAA Brute-Force, Odd, 5 samples */ + PASS2_VBAA_QUAL1_SAMPLES5("vbaa", "_bforce_odd", 5), + /** Pass-2: VBAA Brute-Force, Even, 6 samples */ + PASS2_VBAA_QUAL1_SAMPLES6("vbaa", "_bforce_even", 6), + /** Pass-2: VBAA Brute-Force, Odd, 7 samples */ + PASS2_VBAA_QUAL1_SAMPLES7("vbaa", "_bforce_odd", 7), + /** Pass-2: VBAA Brute-Force, Even, 8 samples */ + PASS2_VBAA_QUAL1_SAMPLES8("vbaa", "_bforce_even", 8); + + public final String tech; + public final String sub; + public final int sampleCount; + + ShaderModeSelector1(final String tech, final String sub, final int sampleCount) { + this.tech = tech; + this.sub= sub; + this.sampleCount = sampleCount; + } + + public static ShaderModeSelector1 selectPass1(final int renderModes) { + return Region.hasVariableWeight(renderModes) ? PASS1_WEIGHT : PASS1_SIMPLE; + } + + public static ShaderModeSelector1 selectPass2(final int renderModes, final int quality, final int sampleCount) { + if( Region.isMSAA(renderModes) ) { + return PASS2_MSAA; + } else if( Region.isVBAA(renderModes) ) { + if( 0 == quality ) { + if( sampleCount < 2 ) { + return PASS2_VBAA_QUAL0_SAMPLES1; + } else if( sampleCount < 4 ) { + return PASS2_VBAA_QUAL0_SAMPLES2; + } else if( sampleCount < 8 ) { + return PASS2_VBAA_QUAL0_SAMPLES4; + } else { + return PASS2_VBAA_QUAL0_SAMPLES8; + } + } else { + switch( sampleCount ) { + case 0: // Fall through intended + case 1: return PASS2_VBAA_QUAL1_SAMPLES1; + case 2: return PASS2_VBAA_QUAL1_SAMPLES2; + case 3: return PASS2_VBAA_QUAL1_SAMPLES3; + case 4: return PASS2_VBAA_QUAL1_SAMPLES4; + case 5: return PASS2_VBAA_QUAL1_SAMPLES5; + case 6: return PASS2_VBAA_QUAL1_SAMPLES6; + case 7: return PASS2_VBAA_QUAL1_SAMPLES7; + default: return PASS2_VBAA_QUAL1_SAMPLES8; + } + } + } else { + return null; + } + } + } + private final IntObjectHashMap shaderPrograms = new IntObjectHashMap(); + + private static final int HIGH_MASK = Region.COLORCHANNEL_RENDERING_BIT | Region.COLORTEXTURE_RENDERING_BIT; + private static final int TWO_PASS_BIT = 1 << 31; + + /** + * @param gl + * @param renderModes + * @param pass1 + * @param quality + * @param sampleCount + * @param colorTexSeq + * @return true if a new shader program is being used and hence external uniform-data and -location, + * as well as the attribute-location must be updated, otherwise false. + */ + public final boolean useShaderProgram(final GL2ES2 gl, final int renderModes, + final boolean pass1, final int quality, final int sampleCount, final TextureSequence colorTexSeq) { + final int colorTexSeqHash; + if( null != colorTexSeq ) { + colorTexSeqHash = colorTexSeq.getTextureFragmentShaderHashCode(); + } else { + colorTexSeqHash = 0; + } + final ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) : + ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount); + final boolean isTwoPass = Region.isTwoPass( renderModes ); + final boolean isPass1ColorTexSeq = pass1 && null != colorTexSeq; + final int shaderKey = ( (colorTexSeqHash << 5) - colorTexSeqHash ) + + ( sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 ) ); + + /** + if(DEBUG) { + System.err.printf("RegionRendererImpl01.useShaderProgram.0: renderModes %s, sel1 %s, key 0x%X (pass1 %b, q %d, samples %d) - Thread %s%n", + Region.getRenderModeString(renderModes), sel1, shaderKey, pass1, quality, sampleCount, Thread.currentThread()); + } */ + + ShaderProgram sp = (ShaderProgram) shaderPrograms.get( shaderKey ); + if( null != sp ) { + final boolean spChanged = getRenderState().setShaderProgram(gl, sp); + if(DEBUG) { + if( spChanged ) { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id()); + } else { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (keep)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id()); + } + } + return spChanged; + } + final String versionedBaseName = getVersionedShaderName(); + final String vertexShaderName; + if( isTwoPass ) { + vertexShaderName = versionedBaseName+"-pass"+(pass1?1:2); + } else { + vertexShaderName = versionedBaseName+"-single"; + } + final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, vertexShaderName, true); + final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, versionedBaseName+"-segment-head", true); + + if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) { + if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) { + throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available"); + } + } + boolean supressGLSLVersionES30 = false; + if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) { + if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) { + // Bug on Nexus 10, ES3 - Android 4.3, where + // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' ! + // P0003: Extension 'GL_OES_EGL_image_external' not supported + supressGLSLVersionES30 = true; + } + } + // + // GLSL customization at top + // + int posVp = rsVp.defaultShaderCustomization(gl, !supressGLSLVersionES30, true); + // rsFp.defaultShaderCustomization(gl, true, true); + int posFp = supressGLSLVersionES30 ? 0 : rsFp.addGLSLVersion(gl); + if( isPass1ColorTexSeq ) { + posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getRequiredExtensionsShaderStub()); + } + if( pass1 && supressGLSLVersionES30 || ( gl.isGLES2() && !gl.isGLES3() ) ) { + posFp = rsFp.insertShaderSource(0, posFp, ShaderCode.createExtensionDirective(GLExtensions.OES_standard_derivatives, ShaderCode.ENABLE)); + } + if( false ) { + final String rsFpDefPrecision = getFragmentShaderPrecision(gl); + if( null != rsFpDefPrecision ) { + posFp = rsFp.insertShaderSource(0, posFp, rsFpDefPrecision); + } + } else { + posFp = rsFp.addDefaultShaderPrecision(gl, posFp); + } + + // + // GLSL append from here on + posFp = -1; + + if( Region.hasColorChannel( renderModes ) ) { + posVp = rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_CHANNEL); + posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_CHANNEL); + } + if( Region.hasColorTexture( renderModes ) ) { + rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_TEXTURE); + posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_TEXTURE); + } + if( !pass1 ) { + posFp = rsFp.insertShaderSource(0, posFp, GLSL_DEF_SAMPLE_COUNT+sel1.sampleCount+"\n"); + posFp = rsFp.insertShaderSource(0, posFp, GLSL_CONST_SAMPLE_COUNT+sel1.sampleCount+".0;\n"); + } + + try { + posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, "uniforms.glsl"); + posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, "varyings.glsl"); + } catch (final IOException ioe) { + throw new RuntimeException("Failed to read: includes", ioe); + } + if( 0 > posFp ) { + throw new RuntimeException("Failed to read: includes"); + } + + final String texLookupFuncName; + if( isPass1ColorTexSeq ) { + posFp = rsFp.insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n"); + texLookupFuncName = colorTexSeq.getTextureLookupFunctionName(gcuTexture2D); + posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl()); + } else { + texLookupFuncName = null; + } + + posFp = rsFp.insertShaderSource(0, posFp, GLSL_MAIN_BEGIN); + + final String passS = pass1 ? "-pass1-" : "-pass2-"; + final String shaderSegment = versionedBaseName+passS+sel1.tech+sel1.sub+".glsl"; + if(DEBUG) { + System.err.printf("RegionRendererImpl01.useShaderProgram.1: segment %s%n", shaderSegment); + } + try { + posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, shaderSegment); + } catch (final IOException ioe) { + throw new RuntimeException("Failed to read: "+shaderSegment, ioe); + } + if( 0 > posFp ) { + throw new RuntimeException("Failed to read: "+shaderSegment); + } + posFp = rsFp.insertShaderSource(0, posFp, "}\n"); + + if( isPass1ColorTexSeq ) { + rsFp.replaceInShaderSource(gcuTexture2D, texLookupFuncName); + } + + sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + + if( !sp.init(gl) ) { + throw new GLException("RegionRenderer: Couldn't init program: "+sp); + } + + if( !sp.link(gl, System.err) ) { + throw new GLException("could not link program: "+sp); + } + getRenderState().setShaderProgram(gl, sp); + + shaderPrograms.put(shaderKey, sp); + if(DEBUG) { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n", + Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id()); + } + return true; } - - -} +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java index eb07142a3..6ff9bf9cd 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java @@ -27,85 +27,326 @@ */ package com.jogamp.graph.curve.opengl; +import java.nio.FloatBuffer; + +import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLException; import javax.media.opengl.GLUniformData; -import jogamp.graph.curve.opengl.RenderStateImpl; +import jogamp.common.os.PlatformPropsImpl; import jogamp.graph.curve.opengl.shader.UniformNames; import com.jogamp.common.os.Platform; +import com.jogamp.graph.curve.Region; import com.jogamp.graph.geom.Vertex; +import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.PMVMatrix; -import com.jogamp.opengl.util.glsl.ShaderState; +import com.jogamp.opengl.util.glsl.ShaderProgram; -public abstract class RenderState { +public class RenderState { private static final String thisKey = "jogamp.graph.curve.RenderState" ; - - public static RenderState createRenderState(ShaderState st, Vertex.Factory<? extends Vertex> pointFactory) { - return new RenderStateImpl(st, pointFactory); + + /** + * Bitfield hint, {@link #isHintMaskSet(int) if set} + * stating <i>enabled</i> {@link GL#GL_BLEND}, otherwise <i>disabled</i>. + * <p> + * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}. + * </p> + * <p> + * If set, {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method} + * will set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function} + * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering. + * </p> + * <p> + * Shall be set by custom code, e.g. via {@link RegionRenderer}'s + * enable and disable {@link RegionRenderer.GLCallback} as done in + * {@link RegionRenderer#defaultBlendEnable} and {@link RegionRenderer#defaultBlendDisable}. + * </p> + */ + public static final int BITHINT_BLENDING_ENABLED = 1 << 0 ; + + /** + * Bitfield hint, {@link #isHintMaskSet(int) if set} + * stating globally <i>enabled</i> {@link GL#GL_DEPTH_TEST}, otherwise <i>disabled</i>. + * <p> + * Shall be set via {@link #setHintMask(int)} and cleared via {@link #clearHintMask(int)}. + * </p> + * <p> + * {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method} + * may toggle depth test, and reset it's state according to this hint. + * </p> + * <p> + * Shall be set by custom code, e.g. after {@link RenderState} or {@link RegionRenderer} construction. + * </p> + */ + public static final int BITHINT_GLOBAL_DEPTH_TEST_ENABLED = 1 << 1 ; + + public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory) { + return new RenderState(pointFactory, null); } - public static RenderState createRenderState(ShaderState st, Vertex.Factory<? extends Vertex> pointFactory, PMVMatrix pmvMatrix) { - return new RenderStateImpl(st, pointFactory, pmvMatrix); + public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory, final PMVMatrix pmvMatrix) { + return new RenderState(pointFactory, pmvMatrix); } - - public static final RenderState getRenderState(GL2ES2 gl) { + + public static final RenderState getRenderState(final GL2ES2 gl) { return (RenderState) gl.getContext().getAttachedObject(thisKey); } - - protected final ShaderState st; - protected final Vertex.Factory<? extends Vertex> vertexFactory; - protected final PMVMatrix pmvMatrix; - protected final GLUniformData gcu_PMVMatrix; - - protected RenderState(ShaderState st, Vertex.Factory<? extends Vertex> vertexFactory, PMVMatrix pmvMatrix) { - this.st = st; + + private final Vertex.Factory<? extends Vertex> vertexFactory; + private final PMVMatrix pmvMatrix; + private final float[] weight; + private final FloatBuffer weightBuffer; + private final float[] colorStatic; + private final FloatBuffer colorStaticBuffer; + private ShaderProgram sp; + private int hintBitfield; + + private final int id; + private static synchronized int getNextID() { + return nextID++; + } + private static int nextID = 1; + + /** + * Representation of {@link RenderState} data for one {@link ShaderProgram} + * as {@link GLUniformData}. + * <p> + * FIXME: Utilize 'ARB_Uniform_Buffer_Object' where available! + * </p> + */ + public static class ProgramLocal { + public final GLUniformData gcu_PMVMatrix01; + public final GLUniformData gcu_Weight; + public final GLUniformData gcu_ColorStatic; + private int rsId = -1; + + public ProgramLocal() { + gcu_PMVMatrix01 = GLUniformData.creatEmptyMatrix(UniformNames.gcu_PMVMatrix01, 4, 4); + gcu_Weight = GLUniformData.creatEmptyVector(UniformNames.gcu_Weight, 1); + gcu_ColorStatic = GLUniformData.creatEmptyVector(UniformNames.gcu_ColorStatic, 4); + } + + public final int getRenderStateId() { return rsId; } + + /** + * <p> + * Since {@link RenderState} data is being used in multiple + * {@link ShaderProgram}s the data must always be written. + * </p> + * @param gl + * @param updateLocation + * @param renderModes + * @param throwOnError TODO + * @return true if no error occurred, i.e. all locations found, otherwise false. + */ + public final boolean update(final GL2ES2 gl, final RenderState rs, final boolean updateLocation, final int renderModes, final boolean pass1, final boolean throwOnError) { + if( rs.id() != rsId ) { + gcu_PMVMatrix01.setData(rs.pmvMatrix.glGetPMvMatrixf()); + gcu_Weight.setData(rs.weightBuffer); + gcu_ColorStatic.setData(rs.colorStaticBuffer); + rsId = rs.id(); + } + boolean res = true; + if( null != rs.sp && rs.sp.inUse() ) { + if( !Region.isTwoPass(renderModes) || !pass1 ) { + final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_PMVMatrix01, throwOnError); + res = res && r0; + } + if( pass1 ) { + if( Region.hasVariableWeight( renderModes ) ) { + final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_Weight, throwOnError); + res = res && r0; + } + { + final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_ColorStatic, throwOnError); + res = res && r0; + } + } + } + return res; + } + + public StringBuilder toString(StringBuilder sb, final boolean alsoUnlocated) { + if(null==sb) { + sb = new StringBuilder(); + } + sb.append("ProgramLocal[rsID ").append(rsId).append(PlatformPropsImpl.NEWLINE); + // pmvMatrix.toString(sb, "%.2f"); + sb.append(gcu_PMVMatrix01).append(", ").append(PlatformPropsImpl.NEWLINE); + sb.append(gcu_ColorStatic).append(", "); + sb.append(gcu_Weight).append("]"); + return sb; + } + + @Override + public String toString() { + return toString(null, false).toString(); + } + } + + protected RenderState(final Vertex.Factory<? extends Vertex> vertexFactory, final PMVMatrix pmvMatrix) { + this.id = getNextID(); + this.sp = null; this.vertexFactory = vertexFactory; - this.pmvMatrix = pmvMatrix; - this.gcu_PMVMatrix = new GLUniformData(UniformNames.gcu_PMVMatrix, 4, 4, pmvMatrix.glGetPMvMatrixf()); - st.ownUniform(gcu_PMVMatrix); + this.pmvMatrix = null != pmvMatrix ? pmvMatrix : new PMVMatrix(); + this.weight = new float[1]; + this.weightBuffer = FloatBuffer.wrap(weight); + this.colorStatic = new float[4]; + this.colorStaticBuffer = FloatBuffer.wrap(colorStatic); + this.hintBitfield = 0; + } + + public final int id() { return id; } + public final ShaderProgram getShaderProgram() { return sp; } + public final boolean isShaderProgramInUse() { return null != sp ? sp.inUse() : false; } + + /** + * Set a {@link ShaderProgram} and enable it. If the given {@link ShaderProgram} is new, + * method returns true, otherwise false. + * @param gl + * @param spNext + * @return true if a new shader program is being used and hence external uniform-data and -location, + * as well as the attribute-location must be updated, otherwise false. + */ + public final boolean setShaderProgram(final GL2ES2 gl, final ShaderProgram spNext) { + if( spNext.equals(this.sp) ) { + spNext.useProgram(gl, true); + return false; + } + if( null != this.sp ) { + this.sp.notifyNotInUse(); + } + this.sp = spNext; + spNext.useProgram(gl, true); + return true; } - - public final ShaderState getShaderState() { return st; } + public final Vertex.Factory<? extends Vertex> getVertexFactory() { return vertexFactory; } - public final PMVMatrix pmvMatrix() { return pmvMatrix; } - public final GLUniformData getPMVMatrix() { return gcu_PMVMatrix; } - - public void destroy(GL2ES2 gl) { - st.destroy(gl); - } - - public abstract GLUniformData getWeight(); - public abstract GLUniformData getAlpha(); - public abstract GLUniformData getColorStatic(); - // public abstract GLUniformData getStrength(); - - public final RenderState attachTo(GL2ES2 gl) { + + public final PMVMatrix getMatrix() { return pmvMatrix; } + + public static boolean isWeightValid(final float v) { + return 0.0f <= v && v <= 1.9f ; + } + public final float getWeight() { return weight[0]; } + public final void setWeight(final float v) { + if( !isWeightValid(v) ) { + throw new IllegalArgumentException("Weight out of range"); + } + weight[0] = v; + } + + + public final float[] getColorStatic(final float[] rgbaColor) { + System.arraycopy(colorStatic, 0, rgbaColor, 0, 4); + return rgbaColor; + } + public final void setColorStatic(final float r, final float g, final float b, final float a){ + colorStatic[0] = r; + colorStatic[1] = g; + colorStatic[2] = b; + colorStatic[3] = a; + } + + + /** + * + * @param gl + * @param updateLocation + * @param data + * @param throwOnError TODO + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data, final boolean throwOnError) { + if( updateLocation || 0 > data.getLocation() ) { + final boolean ok = 0 <= data.setLocation(gl, sp.program()); + if( throwOnError && !ok ) { + throw new GLException("Could not locate "+data); + } + return ok; + } else { + return true; + } + } + + /** + * + * @param gl + * @param updateLocation + * @param updateData TODO + * @param data + * @param throwOnError TODO + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data, final boolean throwOnError) { + updateLocation = updateLocation || 0 > data.getLocation(); + if( updateLocation ) { + updateData = 0 <= data.setLocation(gl, sp.program()); + if( throwOnError && !updateData ) { + throw new GLException("Could not locate "+data); + } + } + if( updateData ){ + gl.glUniform(data); + return true; + } else { + return !updateLocation; + } + } + + /** + * @param gl + * @param data + * @param throwOnError TODO + * @return true if no error occured, i.e. all locations found, otherwise false. + */ + public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data, final boolean throwOnError) { + if( updateLocation || 0 > data.getLocation() ) { + final boolean ok = 0 <= data.setLocation(gl, sp.program()); + if( throwOnError && !ok ) { + throw new GLException("Could not locate "+data); + } + return ok; + } else { + return true; + } + } + + + public final boolean isHintMaskSet(final int mask) { + return mask == ( hintBitfield & mask ); + } + public final void setHintMask(final int mask) { + hintBitfield |= mask; + } + public final void clearHintMask(final int mask) { + hintBitfield &= ~mask; + } + + public void destroy(final GL2ES2 gl) { + if( null != sp ) { + sp.destroy(gl); + sp = null; + } + } + + public final RenderState attachTo(final GL2ES2 gl) { return (RenderState) gl.getContext().attachObject(thisKey, this); } - - public final boolean detachFrom(GL2ES2 gl) { - RenderState _rs = (RenderState) gl.getContext().getAttachedObject(thisKey); + + public final boolean detachFrom(final GL2ES2 gl) { + final RenderState _rs = (RenderState) gl.getContext().getAttachedObject(thisKey); if(_rs == this) { gl.getContext().detachObject(thisKey); return true; } return false; - } - - public StringBuilder toString(StringBuilder sb) { - if(null==sb) { - sb = new StringBuilder(); - } - - sb.append("RenderState["); - st.toString(sb).append(Platform.getNewline()); - sb.append("]"); - - return sb; } - + + @Override public String toString() { - return toString(null).toString(); - } + return "RenderState["+sp+"]"; + } } diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java deleted file mode 100644 index 3c23733a5..000000000 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.curve.opengl; - -import java.nio.FloatBuffer; - -import javax.media.opengl.GL2ES2; -import javax.media.opengl.GLException; -import javax.media.opengl.fixedfunc.GLMatrixFunc; - -import com.jogamp.opengl.util.glsl.ShaderState; -import com.jogamp.opengl.util.PMVMatrix; - -import com.jogamp.graph.curve.Region; - -public abstract class Renderer { - protected static final boolean DEBUG = Region.DEBUG; - protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE; - - public static boolean isWeightValid(float v) { - return 0.0f <= v && v <= 1.9f ; - } - - protected final int renderModes; - protected int vp_width; - protected int vp_height; - protected boolean initialized; - protected final RenderState rs; - private boolean vboSupported = false; - - public final boolean isInitialized() { return initialized; } - - public final int getWidth() { return vp_width; } - public final int getHeight() { return vp_height; } - - public float getWeight() { return rs.getWeight().floatValue(); } - public float getAlpha() { return rs.getAlpha().floatValue(); } - public final PMVMatrix getMatrix() { return rs.pmvMatrix(); } - - /** - * Implementation shall load, compile and link the shader program and leave it active. - * @param gl referencing the current GLContext to which the ShaderState is bound to - * @return - */ - protected abstract boolean initShaderProgram(GL2ES2 gl); - - protected abstract void destroyImpl(GL2ES2 gl); - - /** - * @param rs the used {@link RenderState} - * @param renderModes bit-field of modes - */ - protected Renderer(RenderState rs, int renderModes) { - this.rs = rs; - this.renderModes = renderModes; - } - - public final int getRenderModes() { - return renderModes; - } - - public boolean usesVariableCurveWeight() { return Region.isNonUniformWeight(renderModes); } - - /** - * @return true if Region's renderModes contains all bits as this Renderer's renderModes - * except {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, otherwise false. - */ - public final boolean areRenderModesCompatible(Region region) { - final int cleanRenderModes = getRenderModes() & ( Region.VARIABLE_CURVE_WEIGHT_BIT ); - return cleanRenderModes == ( region.getRenderModes() & cleanRenderModes ); - } - - public final boolean isVBOSupported() { return vboSupported; } - - /** - * Initialize shader and bindings for GPU based rendering bound to the given GL object's GLContext - * if not initialized yet. - * <p>Leaves the renderer enabled, ie ShaderState.</p> - * <p>Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, float[], int)}</p> - * - * @param gl referencing the current GLContext to which the ShaderState is bound to - * @throws GLException if initialization failed - */ - public final void init(GL2ES2 gl) throws GLException { - if(initialized){ - return; - } - vboSupported = gl.isFunctionAvailable("glGenBuffers") && - gl.isFunctionAvailable("glBindBuffer") && - gl.isFunctionAvailable("glBufferData") && - gl.isFunctionAvailable("glDrawElements") && - gl.isFunctionAvailable("glVertexAttribPointer") && - gl.isFunctionAvailable("glDeleteBuffers"); - - if(DEBUG) { - System.err.println("TextRendererImpl01: VBO Supported = " + isVBOSupported()); - } - - if(!vboSupported){ - throw new GLException("VBO not supported"); - } - - rs.attachTo(gl); - - gl.glEnable(GL2ES2.GL_BLEND); - gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); // FIXME: alpha blending stage ? - - initialized = initShaderProgram(gl); - if(!initialized) { - throw new GLException("Shader initialization failed"); - } - - if(!rs.getShaderState().uniform(gl, rs.getPMVMatrix())) { - throw new GLException("Error setting PMVMatrix in shader: "+rs.getShaderState()); - } - - if(!rs.getShaderState().uniform(gl, rs.getWeight())) { - throw new GLException("Error setting weight in shader: "+rs.getShaderState()); - } - - if(!rs.getShaderState().uniform(gl, rs.getAlpha())) { - throw new GLException("Error setting global alpha in shader: "+rs.getShaderState()); - } - - if(!rs.getShaderState().uniform(gl, rs.getColorStatic())) { - throw new GLException("Error setting global color in shader: "+rs.getShaderState()); - } - } - - public final void flushCache(GL2ES2 gl) { - // FIXME: REMOVE ! - } - - public void destroy(GL2ES2 gl) { - if(!initialized){ - if(DEBUG_INSTANCE) { - System.err.println("TextRenderer: Not initialized!"); - } - return; - } - rs.getShaderState().useProgram(gl, false); - destroyImpl(gl); - rs.destroy(gl); - initialized = false; - } - - public final RenderState getRenderState() { return rs; } - public final ShaderState getShaderState() { return rs.getShaderState(); } - - public final void enable(GL2ES2 gl, boolean enable) { - rs.getShaderState().useProgram(gl, enable); - } - - public void setWeight(GL2ES2 gl, float v) { - if( !isWeightValid(v) ) { - throw new IllegalArgumentException("Weight out of range"); - } - rs.getWeight().setData(v); - if(null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getWeight()); - } - } - - public void setAlpha(GL2ES2 gl, float alpha_t) { - rs.getAlpha().setData(alpha_t); - if(null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getAlpha()); - } - - } - - public void getColorStatic(GL2ES2 gl, float[] rgb) { - FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer(); - rgb[0] = fb.get(0); - rgb[1] = fb.get(1); - rgb[2] = fb.get(2); - } - - public void setColorStatic(GL2ES2 gl, float r, float g, float b){ - FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer(); - fb.put(0, r); - fb.put(1, g); - fb.put(2, b); - if(null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getColorStatic()); - } - } - - public void rotate(GL2ES2 gl, float angle, float x, float y, float z) { - rs.pmvMatrix().glRotatef(angle, x, y, z); - updateMatrix(gl); - } - - public void translate(GL2ES2 gl, float x, float y, float z) { - rs.pmvMatrix().glTranslatef(x, y, z); - updateMatrix(gl); - } - - public void scale(GL2ES2 gl, float x, float y, float z) { - rs.pmvMatrix().glScalef(x, y, z); - updateMatrix(gl); - } - - public void resetModelview(GL2ES2 gl) { - rs.pmvMatrix().glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - rs.pmvMatrix().glLoadIdentity(); - updateMatrix(gl); - } - - public void updateMatrix(GL2ES2 gl) { - if(initialized && null != gl && rs.getShaderState().inUse()) { - rs.getShaderState().uniform(gl, rs.getPMVMatrix()); - } - } - - public boolean reshapePerspective(GL2ES2 gl, float angle, int width, int height, float near, float far) { - this.vp_width = width; - this.vp_height = height; - final float ratio = (float)width/(float)height; - final PMVMatrix p = rs.pmvMatrix(); - p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); - p.glLoadIdentity(); - p.gluPerspective(angle, ratio, near, far); - updateMatrix(gl); - return true; - } - - public boolean reshapeOrtho(GL2ES2 gl, int width, int height, float near, float far) { - this.vp_width = width; - this.vp_height = height; - final PMVMatrix p = rs.pmvMatrix(); - p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); - p.glLoadIdentity(); - p.glOrthof(0, width, 0, height, near, far); - updateMatrix(gl); - return true; - } - - protected String getVertexShaderName(GL2ES2 gl) { - return "curverenderer01" + getShaderGLVersionSuffix(gl); - } - - protected String getFragmentShaderName(GL2ES2 gl) { - final String type = "01" ; // Region.isNonUniformWeight(renderModes) ? "02" : "01" ; - final String pass = Region.isVBAA(renderModes) ? "b" : "a" ; - return "curverenderer" + type + pass + getShaderGLVersionSuffix(gl); - } - - protected String getShaderGLVersionSuffix(GL2ES2 gl) { - if(gl.isGLES2()) { - return "-es2"; - } - return "-gl2"; - } - -}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java new file mode 100644 index 000000000..0fa1f49f1 --- /dev/null +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java @@ -0,0 +1,351 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.graph.curve.opengl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLException; + +import jogamp.graph.geom.plane.AffineTransform; + +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.Font.Glyph; +import com.jogamp.graph.geom.Vertex; +import com.jogamp.graph.geom.Vertex.Factory; + +/** + * Text {@link GLRegion} Utility Class + */ +public class TextRegionUtil { + + public final int renderModes; + + public TextRegionUtil(final int renderModes) { + this.renderModes = renderModes; + } + + public static interface ShapeVisitor { + /** + * Visiting the given {@link OutlineShape} with it's corresponding {@link AffineTransform}. + * @param shape may be used as is, otherwise a copy shall be made if intended to be modified. + * @param t may be used immediately as is, otherwise a copy shall be made if stored. + */ + public void visit(final OutlineShape shape, final AffineTransform t); + } + + public static int getCharCount(final String s, final char c) { + final int sz = s.length(); + int count = 0; + for(int i=0; i<sz; i++) { + if( s.charAt(i) == c ) { + count++; + } + } + return count; + } + + /** + * Visit each {@link Font.Glyph}'s {@link OutlineShape} with the given {@link ShapeVisitor} + * additionally passing the progressed {@link AffineTransform}. + * The latter reflects the given font metric, pixelSize and hence character position. + * @param visitor + * @param transform optional given transform + * @param font the target {@link Font} + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param str string text + * @param temp1 temporary AffineTransform storage, mandatory, will be passed to {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} and can be modified. + * @param temp2 temporary AffineTransform storage, mandatory, can be re-used in {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} by user code. + */ + public static void processString(final ShapeVisitor visitor, final AffineTransform transform, + final Font font, final float pixelSize, final CharSequence str, + final AffineTransform temp1, final AffineTransform temp2) { + final int charCount = str.length(); + + // region.setFlipped(true); + final Font.Metrics metrics = font.getMetrics(); + final float lineHeight = font.getLineHeight(pixelSize); + + final float scale = metrics.getScale(pixelSize); + + float y = 0; + float advanceTotal = 0; + + for(int i=0; i< charCount; i++) { + final char character = str.charAt(i); + if( '\n' == character ) { + y -= lineHeight; + advanceTotal = 0; + } else if (character == ' ') { + advanceTotal += font.getAdvanceWidth(Glyph.ID_SPACE, pixelSize); + } else { + if(Region.DEBUG_INSTANCE) { + System.err.println("XXXXXXXXXXXXXXx char: "+character+", scale: "+scale+"; translate: "+advanceTotal+", "+y); + } + // reset transform + if( null != transform ) { + temp1.setTransform(transform); + } else { + temp1.setToIdentity(); + } + temp1.translate(advanceTotal, y, temp2); + temp1.scale(scale, scale, temp2); + + final Font.Glyph glyph = font.getGlyph(character); + final OutlineShape glyphShape = glyph.getShape(); + if( null == glyphShape ) { + continue; + } + visitor.visit(glyphShape, temp1); + + advanceTotal += glyph.getAdvance(pixelSize, true); + } + } + } + + /** + * Add the string in 3D space w.r.t. the font and pixelSize at the end of the {@link GLRegion}. + * @param region the {@link GLRegion} sink + * @param vertexFactory vertex impl factory {@link Factory} + * @param font the target {@link Font} + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param str string text + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. + * @param temp1 temporary AffineTransform storage, mandatory + * @param temp2 temporary AffineTransform storage, mandatory + */ + public static void addStringToRegion(final GLRegion region, final Factory<? extends Vertex> vertexFactory, + final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor, + final AffineTransform temp1, final AffineTransform temp2) { + final ShapeVisitor visitor = new ShapeVisitor() { + public final void visit(final OutlineShape shape, final AffineTransform t) { + region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null); + } }; + processString(visitor, null, font, pixelSize, str, temp1, temp2); + } + + /** + * Render the string in 3D space w.r.t. the font and pixelSize + * using a cached {@link GLRegion} for reuse. + * <p> + * Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory. + * </p> + * @param gl the current GL state + * @param renderer TODO + * @param font {@link Font} to be used + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. + * @param sampleCount desired multisampling sample count for msaa-rendering. + * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. + * @throws Exception if TextRenderer not initialized + */ + public void drawString3D(final GL2ES2 gl, + final RegionRenderer renderer, final Font font, final float pixelSize, + final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount) { + if( !renderer.isInitialized() ) { + throw new GLException("TextRendererImpl01: not initialized!"); + } + final int special = 0; + GLRegion region = getCachedRegion(font, str, pixelSize, special); + if(null == region) { + region = GLRegion.create(renderModes, null); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, tempT1, tempT2); + addCachedRegion(gl, font, str, pixelSize, special, region); + } + region.draw(gl, renderer, sampleCount); + } + + /** + * Render the string in 3D space w.r.t. the font and pixelSize + * using a temporary {@link GLRegion}, which will be destroyed afterwards. + * <p> + * In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion} + * is a huge performance impact. + * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, float, CharSequence, float[], int[], AffineTransform, AffineTransform)} + * instead. + * </p> + * @param gl the current GL state + * @param renderModes TODO + * @param font {@link Font} to be used + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. + * @param sampleCount desired multisampling sample count for msaa-rendering. + * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. + * @param temp1 temporary AffineTransform storage, mandatory + * @param temp2 temporary AffineTransform storage, mandatory + * @throws Exception if TextRenderer not initialized + */ + public static void drawString3D(final GL2ES2 gl, final int renderModes, + final RegionRenderer renderer, final Font font, final float pixelSize, + final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount, + final AffineTransform temp1, final AffineTransform temp2) { + if(!renderer.isInitialized()){ + throw new GLException("TextRendererImpl01: not initialized!"); + } + final GLRegion region = GLRegion.create(renderModes, null); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2); + region.draw(gl, renderer, sampleCount); + region.destroy(gl); + } + + /** + * Render the string in 3D space w.r.t. the font and pixelSize + * using the given {@link GLRegion}, which will {@link GLRegion#clear(GL2ES2) cleared} beforehand. + * @param gl the current GL state + * @param font {@link Font} to be used + * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param str text to be rendered + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. + * @param sampleCount desired multisampling sample count for msaa-rendering. + * The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched. + * @param temp1 temporary AffineTransform storage, mandatory + * @param temp2 temporary AffineTransform storage, mandatory + * @throws Exception if TextRenderer not initialized + */ + public static void drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer, + final Font font, final float pixelSize, final CharSequence str, + final float[] rgbaColor, final int[/*1*/] sampleCount, + final AffineTransform temp1, final AffineTransform temp2) { + if(!renderer.isInitialized()){ + throw new GLException("TextRendererImpl01: not initialized!"); + } + region.clear(gl); + addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2); + region.draw(gl, renderer, sampleCount); + } + + /** + * Clear all cached {@link GLRegions}. + */ + public void clear(final GL2ES2 gl) { + // fluchCache(gl) already called + final Iterator<GLRegion> iterator = stringCacheMap.values().iterator(); + while(iterator.hasNext()){ + final GLRegion region = iterator.next(); + region.destroy(gl); + } + stringCacheMap.clear(); + stringCacheArray.clear(); + } + + /** + * <p>Sets the cache limit for reusing GlyphString's and their Region. + * Default is {@link #DEFAULT_CACHE_LIMIT}, -1 unlimited, 0 turns cache off, >0 limited </p> + * + * <p>The cache will be validate when the next string rendering happens.</p> + * + * @param newLimit new cache size + * + * @see #DEFAULT_CACHE_LIMIT + */ + public final void setCacheLimit(final int newLimit ) { stringCacheLimit = newLimit; } + + /** + * Sets the cache limit, see {@link #setCacheLimit(int)} and validates the cache. + * + * @see #setCacheLimit(int) + * + * @param gl current GL used to remove cached objects if required + * @param newLimit new cache size + */ + public final void setCacheLimit(final GL2ES2 gl, final int newLimit ) { stringCacheLimit = newLimit; validateCache(gl, 0); } + + /** + * @return the current cache limit + */ + public final int getCacheLimit() { return stringCacheLimit; } + + /** + * @return the current utilized cache size, <= {@link #getCacheLimit()} + */ + public final int getCacheSize() { return stringCacheArray.size(); } + + protected final void validateCache(final GL2ES2 gl, final int space) { + if ( getCacheLimit() > 0 ) { + while ( getCacheSize() + space > getCacheLimit() ) { + removeCachedRegion(gl, 0); + } + } + } + + protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final float pixelSize, final int special) { + return stringCacheMap.get(getKey(font, str, pixelSize, special)); + } + + protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final float pixelSize, final int special, final GLRegion glyphString) { + if ( 0 != getCacheLimit() ) { + final String key = getKey(font, str, pixelSize, special); + final GLRegion oldRegion = stringCacheMap.put(key, glyphString); + if ( null == oldRegion ) { + // new entry .. + validateCache(gl, 1); + stringCacheArray.add(stringCacheArray.size(), key); + } /// else overwrite is nop .. + } + } + + protected final void removeCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int pixelSize, final int special) { + final String key = getKey(font, str, pixelSize, special); + final GLRegion region = stringCacheMap.remove(key); + if(null != region) { + region.destroy(gl); + } + stringCacheArray.remove(key); + } + + protected final void removeCachedRegion(final GL2ES2 gl, final int idx) { + final String key = stringCacheArray.remove(idx); + if( null != key ) { + final GLRegion region = stringCacheMap.remove(key); + if(null != region) { + region.destroy(gl); + } + } + } + + protected final String getKey(final Font font, final CharSequence str, final float pixelSize, final int special) { + final StringBuilder sb = new StringBuilder(); + return font.getName(sb, Font.NAME_UNIQUNAME) + .append(".").append(str.hashCode()).append(".").append(Float.floatToIntBits(pixelSize)).append(special).toString(); + } + + /** Default cache limit, see {@link #setCacheLimit(int)} */ + public static final int DEFAULT_CACHE_LIMIT = 256; + + public final AffineTransform tempT1 = new AffineTransform(); + public final AffineTransform tempT2 = new AffineTransform(); + private final HashMap<String, GLRegion> stringCacheMap = new HashMap<String, GLRegion>(DEFAULT_CACHE_LIMIT); + private final ArrayList<String> stringCacheArray = new ArrayList<String>(DEFAULT_CACHE_LIMIT); + private int stringCacheLimit = DEFAULT_CACHE_LIMIT; +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java deleted file mode 100644 index 8dc41b0c0..000000000 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.curve.opengl; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; - -import javax.media.opengl.GL2ES2; - -import jogamp.graph.curve.text.GlyphString; - -import com.jogamp.graph.font.Font; - -public abstract class TextRenderer extends Renderer { - /** - * Create a Hardware accelerated Text Renderer. - * @param rs the used {@link RenderState} - * @param renderModes either {@link com.jogamp.graph.curve.opengl.GLRegion#SINGLE_PASS} or {@link com.jogamp.graph.curve.Region#VBAA_RENDERING_BIT} - */ - public static TextRenderer create(RenderState rs, int renderModes) { - return new jogamp.graph.curve.opengl.TextRendererImpl01(rs, renderModes); - } - - protected TextRenderer(RenderState rs, int type) { - super(rs, type); - } - - - /** Render the String in 3D space wrt to the font provided at the position provided - * the outlines will be generated, if not yet generated - * @param gl the current GL state - * @param font {@link Font} to be used - * @param str text to be rendered - * @param position the lower left corner of the string - * @param fontSize font size - * @param texWidth desired texture width for multipass-rendering. - * The actual used texture-width is written back when mp rendering is enabled, otherwise the store is untouched. - * @throws Exception if TextRenderer not initialized - */ - public abstract void drawString3D(GL2ES2 gl, Font font, - String str, float[] position, int fontSize, int[/*1*/] texSize); - - /**Create the resulting {@link GlyphString} that represents - * the String wrt to the font. - * @param font {@link Font} to be used - * @param size font size - * @param str {@link String} to be created - * @return the resulting GlyphString inclusive the generated region - */ - public GlyphString createString(GL2ES2 gl, Font font, int size, String str) { - if(DEBUG_INSTANCE) { - System.err.println("createString: "+getCacheSize()+"/"+getCacheLimit()+" - "+Font.NAME_UNIQUNAME + " - " + str + " - " + size); - } - final GlyphString glyphString = GlyphString.createString(null, rs.getVertexFactory(), font, size, str); - glyphString.createRegion(gl, renderModes); - return glyphString; - } - - /** FIXME - public void flushCache(GL2ES2 gl) { - Iterator<GlyphString> iterator = stringCacheMap.values().iterator(); - while(iterator.hasNext()){ - GlyphString glyphString = iterator.next(); - glyphString.destroy(gl, rs); - } - stringCacheMap.clear(); - stringCacheArray.clear(); - } */ - - @Override - protected void destroyImpl(GL2ES2 gl) { - // fluchCache(gl) already called - Iterator<GlyphString> iterator = stringCacheMap.values().iterator(); - while(iterator.hasNext()){ - GlyphString glyphString = iterator.next(); - glyphString.destroy(gl, rs); - } - stringCacheMap.clear(); - stringCacheArray.clear(); - } - - /** - * <p>Sets the cache limit for reusing GlyphString's and their Region. - * Default is {@link #DEFAULT_CACHE_LIMIT}, -1 unlimited, 0 turns cache off, >0 limited </p> - * - * <p>The cache will be validate when the next string rendering happens.</p> - * - * @param newLimit new cache size - * - * @see #DEFAULT_CACHE_LIMIT - */ - public final void setCacheLimit(int newLimit ) { stringCacheLimit = newLimit; } - - /** - * Sets the cache limit, see {@link #setCacheLimit(int)} and validates the cache. - * - * @see #setCacheLimit(int) - * - * @param gl current GL used to remove cached objects if required - * @param newLimit new cache size - */ - public final void setCacheLimit(GL2ES2 gl, int newLimit ) { stringCacheLimit = newLimit; validateCache(gl, 0); } - - /** - * @return the current cache limit - */ - public final int getCacheLimit() { return stringCacheLimit; } - - /** - * @return the current utilized cache size, <= {@link #getCacheLimit()} - */ - public final int getCacheSize() { return stringCacheArray.size(); } - - protected final void validateCache(GL2ES2 gl, int space) { - if ( getCacheLimit() > 0 ) { - while ( getCacheSize() + space > getCacheLimit() ) { - removeCachedGlyphString(gl, 0); - } - } - } - - protected final GlyphString getCachedGlyphString(Font font, String str, int fontSize) { - return stringCacheMap.get(getKey(font, str, fontSize)); - } - - protected final void addCachedGlyphString(GL2ES2 gl, Font font, String str, int fontSize, GlyphString glyphString) { - if ( 0 != getCacheLimit() ) { - final String key = getKey(font, str, fontSize); - GlyphString oldGlyphString = stringCacheMap.put(key, glyphString); - if ( null == oldGlyphString ) { - // new entry .. - validateCache(gl, 1); - stringCacheArray.add(stringCacheArray.size(), key); - } /// else overwrite is nop .. - } - } - - protected final void removeCachedGlyphString(GL2ES2 gl, Font font, String str, int fontSize) { - final String key = getKey(font, str, fontSize); - GlyphString glyphString = stringCacheMap.remove(key); - if(null != glyphString) { - glyphString.destroy(gl, rs); - } - stringCacheArray.remove(key); - } - - protected final void removeCachedGlyphString(GL2ES2 gl, int idx) { - final String key = stringCacheArray.remove(idx); - final GlyphString glyphString = stringCacheMap.remove(key); - if(null != glyphString) { - glyphString.destroy(gl, rs); - } - } - - protected final String getKey(Font font, String str, int fontSize) { - final StringBuilder sb = new StringBuilder(); - return font.getName(sb, Font.NAME_UNIQUNAME) - .append(".").append(str.hashCode()).append(".").append(fontSize).toString(); - } - - /** Default cache limit, see {@link #setCacheLimit(int)} */ - public static final int DEFAULT_CACHE_LIMIT = 256; - - private HashMap<String, GlyphString> stringCacheMap = new HashMap<String, GlyphString>(DEFAULT_CACHE_LIMIT); - private ArrayList<String> stringCacheArray = new ArrayList<String>(DEFAULT_CACHE_LIMIT); - private int stringCacheLimit = DEFAULT_CACHE_LIMIT; -}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulation.java b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulation.java index 7728efcaf..ae2849536 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulation.java +++ b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulation.java @@ -33,7 +33,7 @@ import jogamp.graph.curve.tess.CDTriangulator2D; public class Triangulation { /** Create a new instance of a triangulation. - * Currently only a modified version of Constraint Delaunay + * Currently only a modified version of Constraint Delaunay * is implemented. * @return instance of a triangulator * @see Triangulator diff --git a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java index 1ffaccebc..20fe9bfd9 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java +++ b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java @@ -28,7 +28,7 @@ package com.jogamp.graph.curve.tess; -import java.util.ArrayList; +import java.util.List; import com.jogamp.graph.geom.Outline; import com.jogamp.graph.geom.Triangle; @@ -36,34 +36,42 @@ import com.jogamp.graph.geom.Triangle; /** Interface to the triangulation algorithms provided * A triangulation of 2D outlines where you can * provides an easy one or more outlines to be triangulated - * + * * example usage: * addCurve(o1); * addCurve(o2); * addCurve(o3); * generate(); * reset(); - * + * * @see Outline * @see Triangulation */ public interface Triangulator { - - /** Add a curve to the list of Outlines + + /** + * Add a curve to the list of Outlines * describing the shape + * @param sink list where the generated triangles will be added * @param outline a bounding {@link Outline} + * @param sharpness TODO */ - public void addCurve(Outline outline); - - /** Generate the triangulation of the provided + public void addCurve(List<Triangle> sink, Outline outline, float sharpness); + + /** Generate the triangulation of the provided * List of {@link Outline}s - * @return an arraylist of {@link Triangle}s resembling the - * final shape. + * @param sink list where the generated triangles will be added */ - public ArrayList<Triangle> generate(); - - /** Reset the triangulation to initial state - * Clearing cached data + public void generate(List<Triangle> sink); + + /** + * Reset the triangulation to initial state + * Clearing cached data */ public void reset(); + + /** + * Return the number of newly added vertices during {@link #addCurve(List, Outline, float)}. + */ + public int getAddedVerticeCount(); } diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index e2b3fe5d7..52ad4076d 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. +// * Copyright 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -27,16 +27,35 @@ */ package com.jogamp.graph.font; -import com.jogamp.graph.geom.AABBox; +import jogamp.graph.geom.plane.AffineTransform; + +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.opengl.math.geom.AABBox; /** * Interface wrapper for font implementation. - * + * <p> * TrueType Font Specification: - * http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html - * + * <ul> + * <li>http://www.freetype.org/freetype2/documentation.html</li> + * <li>http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html</li> + * <li>http://www.microsoft.com/typography/SpecificationsOverview.mspx</li> + * <li>http://www.microsoft.com/typography/otspec/</li> + * </ul> + * </p> + * <p> * TrueType Font Table Introduction: - * http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08 + * <ul> + * <li>http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08</li> + * </ul> + * </p> + * <p> + * Misc.: + * <ul> + * <li>Treatis on Font <code>Rasterization https://freddie.witherden.org/pages/font-rasterisation/</code></li> + * <li>Glyph Hell <code>http://walon.org/pub/ttf/ttf_glyphs.htm</code></li> + * </ul> + * </p> */ public interface Font { @@ -50,64 +69,158 @@ public interface Font { public static final int NAME_VERSION = 5; public static final int NAME_MANUFACTURER = 8; public static final int NAME_DESIGNER = 9; - - + + /** * Metrics for font - * + * * Depending on the font's direction, horizontal or vertical, * the following tables shall be used: - * + * * Vertical http://developer.apple.com/fonts/TTRefMan/RM06/Chap6vhea.html * Horizontal http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html */ - public interface Metrics { - float getAscent(float pixelSize); - float getDescent(float pixelSize); - float getLineGap(float pixelSize); - float getMaxExtend(float pixelSize); - float getScale(float pixelSize); - AABBox getBBox(float pixelSize); + public interface Metrics { + float getAscent(final float pixelSize); + float getDescent(final float pixelSize); + float getLineGap(final float pixelSize); + float getMaxExtend(final float pixelSize); + float getScale(final float pixelSize); + /** + * @param dest AABBox instance set to this metrics boundary w/ given pixelSize + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @param tmpV3 caller provided temporary 3-component vector + * @return the given and set AABBox 'dest' + */ + AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3); } /** * Glyph for font + * + * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6cmap.html + * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6glyf.html + * http://www.microsoft.com/typography/otspec/glyf.htm */ public interface Glyph { - // reserved special glyph IDs + // reserved special glyph IDs // http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#ba57949e public static final int ID_UNKNOWN = 0; public static final int ID_CR = 2; public static final int ID_SPACE = 3; - + public Font getFont(); public char getSymbol(); - public AABBox getBBox(float pixelSize); - public float getAdvance(float pixelSize, boolean useFrationalMetrics); + public short getID(); + public AABBox getBBox(); + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @return + */ + public float getScale(final float pixelSize); + /** + * @param dest AABBox instance set to this metrics boundary w/ given pixelSize + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @param tmpV3 caller provided temporary 3-component vector + * @return the given and set AABBox 'dest' + */ + public AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3); + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @param useFrationalMetrics + * @return + */ + public float getAdvance(final float pixelSize, boolean useFrationalMetrics); + public OutlineShape getShape(); + public int hashCode(); } - public String getName(int nameIndex); - public StringBuilder getName(StringBuilder string, int nameIndex); - + public String getName(final int nameIndex); + public StringBuilder getName(final StringBuilder string, final int nameIndex); + /** Shall return the family and subfamily name, separated a dash. * <p>{@link #getName(StringBuilder, int)} w/ {@link #NAME_FAMILY} and {@link #NAME_SUBFAMILY}</p> * <p>Example: "{@code Ubuntu-Regular}"</p> */ - public StringBuilder getFullFamilyName(StringBuilder buffer); - - public StringBuilder getAllNames(StringBuilder string, String separator); - - public float getAdvanceWidth(int i, float pixelSize); + public StringBuilder getFullFamilyName(final StringBuilder buffer); + + public StringBuilder getAllNames(final StringBuilder string, final String separator); + + /** + * <pre> + Font Scale Formula: + inch: 25.4 mm + pointSize: [point] = [1/72 inch] + + [1] Scale := pointSize * resolution / ( 72 points per inch * units_per_em ) + [2] PixelSize := pointSize * resolution / ( 72 points per inch ) + [3] Scale := PixelSize / units_per_em + * </pre> + * @param fontSize in point-per-inch + * @param resolution display resolution in dots-per-inch + * @return pixel-per-inch, pixelSize scale factor for font operations. + */ + public float getPixelSize(final float fontSize /* points per inch */, final float resolution); + + /** + * + * @param glyphID + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ + public float getAdvanceWidth(final int glyphID, final float pixelSize); public Metrics getMetrics(); - public Glyph getGlyph(char symbol); + public Glyph getGlyph(final char symbol); public int getNumGlyphs(); - - public float getStringWidth(CharSequence string, float pixelSize); - public float getStringHeight(CharSequence string, float pixelSize); - public AABBox getStringBounds(CharSequence string, float pixelSize); - - public boolean isPrintableChar( char c ); - + + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ + public float getLineHeight(final float pixelSize); + /** + * + * @param string + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ + public float getMetricWidth(final CharSequence string, final float pixelSize); + /** + * + * @param string + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @param tmp + * @return + */ + public float getMetricHeight(final CharSequence string, final float pixelSize, final AABBox tmp); + /** + * Return the <i>layout</i> bounding box as computed by each glyph's metrics. + * The result is not pixel correct, bit reflects layout specific metrics. + * <p> + * See {@link #getPointsBounds(AffineTransform, CharSequence, float, AffineTransform, AffineTransform)} for pixel correct results. + * </p> + * @param string string text + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + */ + public AABBox getMetricBounds(final CharSequence string, final float pixelSize); + + /** + * Return the bounding box by taking each glyph's point-based bounding box into account. + * @param transform optional given transform + * @param string string text + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @param temp1 temporary AffineTransform storage, mandatory + * @param temp2 temporary AffineTransform storage, mandatory + */ + public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize, + final AffineTransform temp1, final AffineTransform temp2); + + public boolean isPrintableChar(final char c); + /** Shall return {@link #getFullFamilyName()} */ + @Override public String toString(); -}
\ No newline at end of file +} diff --git a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java index 33d487355..be16e030f 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java @@ -27,25 +27,42 @@ */ package com.jogamp.graph.font; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; -import java.net.URLConnection; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import com.jogamp.common.net.Uri; +import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.common.util.SecurityUtil; +import com.jogamp.common.util.cache.TempJarCache; import jogamp.graph.font.FontConstructor; import jogamp.graph.font.JavaFontLoader; import jogamp.graph.font.UbuntuFontLoader; +/** + * The optional property <i>jogamp.graph.font.ctor</i> + * allows user to specify the {@link FontConstructor} implementation. + * <p> + * Default {@link FontConstructor} is {@link jogamp.graph.font.typecast.TypecastFontConstructor}, + * i.e. using our internal <i>typecast</i> branch. + * </p> + */ public class FontFactory { - /** Ubuntu is the default font family */ + private static final String FontConstructorPropKey = "jogamp.graph.font.ctor"; + private static final String DefaultFontConstructor = "jogamp.graph.font.typecast.TypecastFontConstructor"; + + /** Ubuntu is the default font family, {@value} */ public static final int UBUNTU = 0; - - /** Java fonts are optional */ + + /** Java fonts are optional, {@value} */ public static final int JAVA = 1; - + private static final FontConstructor fontConstr; static { @@ -53,19 +70,19 @@ public class FontFactory { * For example: * "jogamp.graph.font.typecast.TypecastFontFactory" (default) * "jogamp.graph.font.ttf.TTFFontImpl" - */ - String fontImplName = PropertyAccess.getProperty("FontImpl", true, SecurityUtil.getCommonAccessControlContext(FontFactory.class)); + */ + String fontImplName = PropertyAccess.getProperty(FontConstructorPropKey, true); if(null == fontImplName) { - fontImplName = "jogamp.graph.font.typecast.TypecastFontConstructor"; + fontImplName = DefaultFontConstructor; } fontConstr = (FontConstructor) ReflectionUtil.createInstance(fontImplName, FontFactory.class.getClassLoader()); } - + public static final FontSet getDefault() { return get(UBUNTU); } - - public static final FontSet get(int font) { + + public static final FontSet get(final int font) { switch (font) { case JAVA: return JavaFontLoader.get(); @@ -73,20 +90,131 @@ public class FontFactory { return UbuntuFontLoader.get(); } } - - public static final Font get(File file) throws IOException { + + /** + * Creates a Font instance. + * @param file font file + * @return the new Font instance + * @throws IOException + */ + public static final Font get(final File file) throws IOException { return fontConstr.create(file); } - public static final Font get(final URLConnection conn) throws IOException { - return fontConstr.create(conn); - } - - public static boolean isPrintableChar( char c ) { - Character.UnicodeBlock block = Character.UnicodeBlock.of( c ); - return (!Character.isISOControl(c)) && - c != 0 && - block != null && - block != Character.UnicodeBlock.SPECIALS; - } + /** + * Creates a Font instance based on a determinated font stream with its given length + * of the font segment. + * <p> + * No explicit stream copy is performed as in {@link #get(InputStream, boolean)} + * due to the known {@code streamLen}. + * </p> + * @param stream font stream + * @param streamLen length of the font segment within this font stream + * @param closeStream {@code true} to close the {@code stream} + * @return the new Font instance + * @throws IOException + */ + public static final Font get(final InputStream stream, final int streamLen, final boolean closeStream) throws IOException { + try { + return fontConstr.create(stream, streamLen); + } finally { + if( closeStream ) { + stream.close(); + } + } + } + + /** + * Creates a Font instance based on an undeterminated font stream length. + * <p> + * The font stream is temporarily copied into a temp file + * to gather it's size and to gain random access. + * The temporary file will be deleted at exit. + * </p> + * @param stream dedicated font stream + * @param closeStream {@code true} to close the {@code stream} + * @return the new Font instance + * @throws IOException + */ + public static final Font get(final InputStream stream, final boolean closeStream) throws IOException { + final IOException[] ioe = { null }; + final int[] streamLen = { 0 }; + final File tempFile[] = { null }; + + final InputStream bis = AccessController.doPrivileged(new PrivilegedAction<InputStream>() { + @Override + public InputStream run() { + InputStream bis = null; + try { + tempFile[0] = IOUtil.createTempFile( "jogl.font", ".ttf", false); + streamLen[0] = IOUtil.copyStream2File(stream, tempFile[0], -1); + if( 0 == streamLen[0] ) { + throw new IOException("Font stream has zero bytes"); + } + bis = new BufferedInputStream(new FileInputStream(tempFile[0]), streamLen[0]); + } catch (final IOException e) { + ioe[0] = e; + if( null != tempFile[0] ) { + tempFile[0].delete(); + tempFile[0] = null; + } + streamLen[0] = 0; + } finally { + if( closeStream ) { + IOUtil.close(stream, ioe, System.err); + } + } + return bis; + } }); + if( null != ioe[0] ) { + throw ioe[0]; + } + if( null == bis ) { + throw new IOException("Could not cache font stream"); // should not be reached + } + try { + return fontConstr.create(bis, streamLen[0]); + } finally { + if( null != bis ) { + bis.close(); + } + if( null != tempFile[0] ) { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + tempFile[0].delete(); + return null; + } } ); + } + } + } + + public static final Font get(final Class<?> context, final String fname, final boolean useTempJarCache) throws IOException { + InputStream stream = null; + if( useTempJarCache ) { + try { + final Uri uri = TempJarCache.getResourceUri(fname); + stream = null != uri ? uri.toURL().openConnection().getInputStream() : null; + } catch (final Exception e) { + throw new IOException(e); + } + } else { + stream = IOUtil.getResource(context, fname).getInputStream(); + } + if( null != stream ) { + return FontFactory.get ( stream, true ) ; + } + return null; + } + + public static boolean isPrintableChar( final char c ) { + if( Character.isWhitespace(c) ) { + return true; + } + if( 0 == c || Character.isISOControl(c) ) { + return false; + } + final Character.UnicodeBlock block = Character.UnicodeBlock.of( c ); + return block != null && block != Character.UnicodeBlock.SPECIALS; + } } diff --git a/src/jogl/classes/com/jogamp/graph/font/FontSet.java b/src/jogl/classes/com/jogamp/graph/font/FontSet.java index d376922ab..60a16b241 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontSet.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontSet.java @@ -32,31 +32,39 @@ import java.io.IOException; public interface FontSet { - /** Font family REGULAR **/ + /** Font family REGULAR, {@value} **/ public static final int FAMILY_REGULAR = 0; - - /** Font family LIGHT **/ + + /** Font family LIGHT, {@value} **/ public static final int FAMILY_LIGHT = 1; - - /** Font family MEDIUM **/ + + /** Font family MEDIUM, {@value} **/ public static final int FAMILY_MEDIUM = 2; - - /** Font family CONDENSED **/ + + /** Font family CONDENSED, {@value} **/ public static final int FAMILY_CONDENSED = 3; - - /** Font family MONO **/ + + /** Font family MONO, {@value} **/ public static final int FAMILY_MONOSPACED = 4; - - /** SERIF style/family bit flag. Fallback to Sans Serif. */ + + /** Zero style, {@value} */ + public static final int STYLE_NONE = 0; + + /** SERIF style/family bit flag. Fallback to Sans Serif, {@value} */ public static final int STYLE_SERIF = 1 << 1; - - /** BOLD style bit flag */ + + /** BOLD style bit flag, {@value} */ public static final int STYLE_BOLD = 1 << 2; - - /** ITALIC style bit flag */ + + /** ITALIC style bit flag, {@value} */ public static final int STYLE_ITALIC = 1 << 3; + /** + * Returns the family {@link #FAMILY_REGULAR} with {@link #STYLE_NONE} + * as retrieved with {@link #get(int, int)}. + * @throws IOException + */ Font getDefault() throws IOException ; - + Font get(int family, int stylebits) throws IOException ; } diff --git a/src/jogl/classes/com/jogamp/graph/geom/AABBox.java b/src/jogl/classes/com/jogamp/graph/geom/AABBox.java deleted file mode 100644 index 87f084919..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/AABBox.java +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.geom; - -import com.jogamp.graph.math.VectorUtil; - -/** - * Axis Aligned Bounding Box. Defined by two 3D coordinates (low and high) - * The low being the the lower left corner of the box, and the high being the upper - * right corner of the box. - * - */ -public class AABBox implements Cloneable { - private float[] low = new float[3]; - private float[] high = new float[3]; - private float[] center = new float[3]; - - /** Create a Axis Aligned bounding box (AABBox) - * where the low and and high MAX float Values. - */ - public AABBox() { - reset(); - } - - /** Create an AABBox specifying the coordinates - * of the low and high - * @param lx min x-coordinate - * @param ly min y-coordnate - * @param lz min z-coordinate - * @param hx max x-coordinate - * @param hy max y-coordinate - * @param hz max z-coordinate - */ - public AABBox(float lx, float ly, float lz, - float hx, float hy, float hz) - { - reset(); - resize(lx, ly, lz); - resize(hx, hy, hz); - - computeCenter(); - } - - /** Create a AABBox defining the low and high - * @param low min xyz-coordinates - * @param high max xyz-coordinates - */ - public AABBox(float[] low, float[] high) { - reset(); - resize(low[0],low[1],low[2]); - resize(high[0],high[1],high[2]); - - computeCenter(); - } - - /** resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. */ - public final void reset() { - setLow(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE); - setHigh(-1*Float.MAX_VALUE,-1*Float.MAX_VALUE,-1*Float.MAX_VALUE); - center[0] = 0f; - center[1] = 0f; - center[2] = 0f; - } - - /** Get the max xyz-coordinates - * @return a float array containing the max xyz coordinates - */ - public final float[] getHigh() { - return high; - } - - private final void setHigh(float hx, float hy, float hz) { - this.high[0] = hx; - this.high[1] = hy; - this.high[2] = hz; - } - - /** Get the min xyz-coordinates - * @return a float array containing the min xyz coordinates - */ - public final float[] getLow() { - return low; - } - - private final void setLow(float lx, float ly, float lz) { - this.low[0] = lx; - this.low[1] = ly; - this.low[2] = lz; - } - - /** Resize the AABBox to encapsulate another AABox - * @param newBox AABBox to be encapsulated in - */ - public final void resize(AABBox newBox) { - float[] newLow = newBox.getLow(); - float[] newHigh = newBox.getHigh(); - - /** test low */ - if (newLow[0] < low[0]) - low[0] = newLow[0]; - if (newLow[1] < low[1]) - low[1] = newLow[1]; - if (newLow[2] < low[2]) - low[2] = newLow[2]; - - /** test high */ - if (newHigh[0] > high[0]) - high[0] = newHigh[0]; - if (newHigh[1] > high[1]) - high[1] = newHigh[1]; - if (newHigh[2] > high[2]) - high[2] = newHigh[2]; - - computeCenter(); - } - - private final void computeCenter() { - center[0] = (high[0] + low[0])/2; - center[1] = (high[1] + low[1])/2; - center[2] = (high[2] + low[2])/2; - } - - /** Resize the AABBox to encapsulate the passed - * xyz-coordinates. - * @param x x-axis coordinate value - * @param y y-axis coordinate value - * @param z z-axis coordinate value - */ - public final void resize(float x, float y, float z) { - /** test low */ - if (x < low[0]) - low[0] = x; - if (y < low[1]) - low[1] = y; - if (z < low[2]) - low[2] = z; - - /** test high */ - if (x > high[0]) - high[0] = x; - if (y > high[1]) - high[1] = y; - if (z > high[2]) - high[2] = z; - - computeCenter(); - } - - /** Resize the AABBox to encapsulate the passed - * xyz-coordinates. - * @param xyz xyz-axis coordinate values - * @param offset of the array - */ - public final void resize(float[] xyz, int offset) { - resize(xyz[0+offset], xyz[1+offset], xyz[2+offset]); - } - - /** Check if the x & y coordinates are bounded/contained - * by this AABBox - * @param x x-axis coordinate value - * @param y y-axis coordinate value - * @return true if x belong to (low.x, high.x) and - * y belong to (low.y, high.y) - */ - public final boolean contains(float x, float y) { - if(x<low[0] || x>high[0]){ - return false; - } - if(y<low[1]|| y>high[1]){ - return false; - } - return true; - } - - /** Check if the xyz coordinates are bounded/contained - * by this AABBox. - * @param x x-axis coordinate value - * @param y y-axis coordinate value - * @param z z-axis coordinate value - * @return true if x belong to (low.x, high.x) and - * y belong to (low.y, high.y) and z belong to (low.z, high.z) - */ - public final boolean contains(float x, float y, float z) { - if(x<low[0] || x>high[0]){ - return false; - } - if(y<low[1]|| y>high[1]){ - return false; - } - if(z<low[2] || z>high[2]){ - return false; - } - return true; - } - - /** Check if there is a common region between this AABBox and the passed - * 2D region irrespective of z range - * @param x lower left x-coord - * @param y lower left y-coord - * @param w width - * @param h hight - * @return true if this AABBox might have a common region with this 2D region - */ - public final boolean intersects(float x, float y, float w, float h) { - if (w <= 0 || h <= 0) { - return false; - } - - final float _w = getWidth(); - final float _h = getHeight(); - if (_w <= 0 || _h <= 0) { - return false; - } - - final float x0 = getMinX(); - final float y0 = getMinY(); - return (x + w > x0 && - y + h > y0 && - x < x0 + _w && - y < y0 + _h); - } - - - /** Get the size of the Box where the size is represented by the - * length of the vector between low and high. - * @return a float representing the size of the AABBox - */ - public final float getSize() { - return VectorUtil.computeLength(low, high); - } - - /**Get the Center of the AABBox - * @return the xyz-coordinates of the center of the AABBox - */ - public final float[] getCenter() { - return center; - } - - /** Scale the AABBox by a constant - * @param size a constant float value - */ - public final void scale(float size) { - float[] diffH = new float[3]; - diffH[0] = high[0] - center[0]; - diffH[1] = high[1] - center[1]; - diffH[2] = high[2] - center[2]; - - diffH = VectorUtil.scale(diffH, size); - - float[] diffL = new float[3]; - diffL[0] = low[0] - center[0]; - diffL[1] = low[1] - center[1]; - diffL[2] = low[2] - center[2]; - - diffL = VectorUtil.scale(diffL, size); - - high = VectorUtil.vectorAdd(center, diffH); - low = VectorUtil.vectorAdd(center, diffL); - } - - public final float getMinX() { - return low[0]; - } - - public final float getMinY() { - return low[1]; - } - - public final float getWidth(){ - return high[0] - low[0]; - } - - public final float getHeight() { - return high[1] - low[1]; - } - - public final float getDepth() { - return high[2] - low[2]; - } - - public final AABBox clone() { - return new AABBox(this.low, this.high); - } - - public final boolean equals(Object obj) { - if( obj == this ) { - return true; - } - if( null == obj || !(obj instanceof AABBox) ) { - return false; - } - final AABBox other = (AABBox) obj; - return VectorUtil.checkEquality(low, other.low) && - VectorUtil.checkEquality(high, other.high) ; - } - - public final String toString() { - return "[ "+low[0]+"/"+low[1]+"/"+low[1]+" .. "+high[0]+"/"+high[0]+"/"+high[0]+", ctr "+ - center[0]+"/"+center[1]+"/"+center[1]+" ]"; - } -} diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 5030488cc..ec0225b37 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -29,71 +29,97 @@ package com.jogamp.graph.geom; import java.util.ArrayList; +import jogamp.graph.geom.plane.AffineTransform; + import com.jogamp.graph.geom.Vertex; -import com.jogamp.graph.math.VectorUtil; +import com.jogamp.graph.curve.OutlineShape; +import com.jogamp.graph.curve.Region; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.math.geom.AABBox; /** Define a single continuous stroke by control vertices. - * The vertices define the shape of the region defined by this + * The vertices define the shape of the region defined by this * outline. The Outline can contain a list of off-curve and on-curve * vertices which define curved regions. - * + * * Note: An outline should be closed to be rendered as a region. - * - * @see OutlineShape, Region + * + * @see OutlineShape + * @see Region */ -public class Outline implements Cloneable, Comparable<Outline> { +public class Outline implements Comparable<Outline> { - private ArrayList<Vertex> vertices = new ArrayList<Vertex>(3); - private boolean closed = false; - private AABBox bbox = new AABBox(); - private boolean dirtyBBox = false; + private ArrayList<Vertex> vertices; + private boolean closed; + private final AABBox bbox; + private boolean dirtyBBox; /**Create an outline defined by control vertices. * An outline can contain off Curve vertices which define curved * regions in the outline. */ - public Outline() { + public Outline() { + vertices = new ArrayList<Vertex>(3); + closed = false; + bbox = new AABBox(); + dirtyBBox = false; + } + + /** + * Copy ctor + */ + public Outline(final Outline src) { + vertices = new ArrayList<Vertex>(src.vertices.size()); + for(int i=0; i<vertices.size(); i++) { + vertices.add( src.vertices.get(i).clone() ); + } + closed = src.closed; + bbox = new AABBox(src.bbox); + dirtyBBox = src.dirtyBBox; } public final int getVertexCount() { return vertices.size(); } - /** Appends a vertex to the outline loop/strip. + /** + * Appends a vertex to the outline loop/strip. * @param vertex Vertex to be added - * @throws NullPointerException if the {@link Vertex} element is null + * @throws NullPointerException if the {@link Vertex} element is null */ - public final void addVertex(Vertex vertex) throws NullPointerException { + public final void addVertex(final Vertex vertex) throws NullPointerException { addVertex(vertices.size(), vertex); } - /** Insert the {@link Vertex} element at the given {@code position} to the outline loop/strip. + /** + * Insert the {@link Vertex} element at the given {@code position} to the outline loop/strip. * @param position of the added Vertex * @param vertex Vertex object to be added - * @throws NullPointerException if the {@link Vertex} element is null + * @throws NullPointerException if the {@link Vertex} element is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position > getVertexNumber()) */ - public final void addVertex(int position, Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { + public final void addVertex(final int position, final Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { if (null == vertex) { throw new NullPointerException("vertex is null"); } vertices.add(position, vertex); if(!dirtyBBox) { - bbox.resize(vertex.getX(), vertex.getY(), vertex.getZ()); + bbox.resize(vertex.getCoord()); } } /** Replaces the {@link Vertex} element at the given {@code position}. * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p> - * + * * @param position of the replaced Vertex - * @param vertex replacement Vertex object - * @throws NullPointerException if the {@link Outline} element is null + * @param vertex replacement Vertex object + * @throws NullPointerException if the {@link Outline} element is null * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getVertexNumber()) */ - public final void setVertex(int position, Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { + public final void setVertex(final int position, final Vertex vertex) throws NullPointerException, IndexOutOfBoundsException { if (null == vertex) { throw new NullPointerException("vertex is null"); } @@ -101,22 +127,22 @@ public class Outline implements Cloneable, Comparable<Outline> { dirtyBBox = true; } - public final Vertex getVertex(int index){ + public final Vertex getVertex(final int index){ return vertices.get(index); } - public int getVertexIndex(Vertex vertex){ + public int getVertexIndex(final Vertex vertex){ return vertices.indexOf(vertex); } /** Removes the {@link Vertex} element at the given {@code position}. * <p>Sets the bounding box dirty, hence a next call to {@link #getBounds()} will validate it.</p> - * + * * @param position of the to be removed Vertex * @throws IndexOutOfBoundsException if position is out of range (position < 0 || position >= getVertexNumber()) */ - public final Vertex removeVertex(int position) throws IndexOutOfBoundsException { - dirtyBBox = true; + public final Vertex removeVertex(final int position) throws IndexOutOfBoundsException { + dirtyBBox = true; return vertices.remove(position); } @@ -138,10 +164,10 @@ public class Outline implements Cloneable, Comparable<Outline> { /** * Use the given outline loop/strip. * <p>Validates the bounding box.</p> - * + * * @param vertices the new outline loop/strip */ - public final void setVertices(ArrayList<Vertex> vertices) { + public final void setVertices(final ArrayList<Vertex> vertices) { this.vertices = vertices; validateBoundingBox(); } @@ -150,45 +176,53 @@ public class Outline implements Cloneable, Comparable<Outline> { return closed; } - /** define if this outline is closed or not. - * if set to closed, checks if the last vertex is - * equal to the first vertex. If not Equal adds a - * vertex at the end to the list. - * @param closed + /** + * Ensure this outline is closed. + * <p> + * Checks whether the last vertex equals to the first. + * If not equal, it either appends a clone of the first vertex + * or prepends a clone of the last vertex, depending on <code>closeTail</code>. + * </p> + * @param closeTail if true, a clone of the first vertex will be appended, + * otherwise a clone of the last vertex will be prepended. + * @return true if closing performed, otherwise false for NOP */ - public final void setClosed(boolean closed) { - this.closed = closed; - if( closed && !isEmpty() ) { - Vertex first = vertices.get(0); - Vertex last = getLastVertex(); - if(!VectorUtil.checkEquality(first.getCoord(), last.getCoord())){ - Vertex v = first.clone(); - vertices.add(v); + public final boolean setClosed(final boolean closeTail) { + this.closed = true; + if( !isEmpty() ) { + final Vertex first = vertices.get(0); + final Vertex last = getLastVertex(); + if( !VectorUtil.isVec3Equal( first.getCoord(), 0, last.getCoord(), 0, FloatUtil.EPSILON ) ) { + if( closeTail ) { + vertices.add(first.clone()); + } else { + vertices.add(0, last.clone()); + } + return true; } } + return false; } - /** Compare two outlines with Bounding Box area - * as criteria. - * @see java.lang.Comparable#compareTo(java.lang.Object) + /** + * Return a transformed instance with all vertices are copied and transformed. */ - public final int compareTo(Outline outline) { - float size = getBounds().getSize(); - float newSize = outline.getBounds().getSize(); - if(size < newSize){ - return -1; - } - else if(size > newSize){ - return 1; + public final Outline transform(final AffineTransform t, final Vertex.Factory<? extends Vertex> vertexFactory) { + final Outline newOutline = new Outline(); + final int vsize = vertices.size(); + for(int i=0; i<vsize; i++) { + final Vertex v = vertices.get(i); + newOutline.addVertex(t.transform(v, vertexFactory.create())); } - return 0; + newOutline.closed = this.closed; + return newOutline; } private final void validateBoundingBox() { dirtyBBox = false; bbox.reset(); for (int i=0; i<vertices.size(); i++) { - bbox.resize(vertices.get(i).getCoord(), 0); + bbox.resize(vertices.get(i).getCoord()); } } @@ -197,19 +231,38 @@ public class Outline implements Cloneable, Comparable<Outline> { validateBoundingBox(); } return bbox; - } + } + + /** + * Compare two outline's Bounding Box size. + * @see AABBox#getSize() + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public final int compareTo(final Outline other) { + final float thisSize = getBounds().getSize(); + final float otherSize = other.getBounds().getSize(); + if( FloatUtil.isEqual(thisSize, otherSize, FloatUtil.EPSILON) ) { + return 0; + } else if(thisSize < otherSize){ + return -1; + } else { + return 1; + } + } /** * @param obj the Object to compare this Outline with - * @return true if {@code obj} is an Outline, not null, equals bounds and equal vertices in the same order + * @return true if {@code obj} is an Outline, not null, equals bounds and equal vertices in the same order */ - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if( obj == this) { return true; } if( null == obj || !(obj instanceof Outline) ) { return false; - } + } final Outline o = (Outline) obj; if(getVertexCount() != o.getVertexCount()) { return false; @@ -224,20 +277,13 @@ public class Outline implements Cloneable, Comparable<Outline> { } return true; } - - /** - * @return deep clone of this Outline - */ - public Outline clone() { - Outline o; - try { - o = (Outline) super.clone(); - } catch (CloneNotSupportedException e) { throw new InternalError(); } - o.bbox = bbox.clone(); - o.vertices = new ArrayList<Vertex>(vertices.size()); - for(int i=0; i<vertices.size(); i++) { - o.vertices.add(vertices.get(i).clone()); - } - return o; - } + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + @Override + public String toString() { + // Avoid calling this.hashCode() ! + return getClass().getName() + "@" + Integer.toHexString(super.hashCode()); + } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java b/src/jogl/classes/com/jogamp/graph/geom/SVertex.java index 9dade17e9..dc6982025 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/SVertex.java @@ -25,114 +25,154 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.graph.geom.opengl; +package com.jogamp.graph.geom; -import com.jogamp.graph.geom.Vertex; -import com.jogamp.graph.math.VectorUtil; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.VectorUtil; /** A Simple Vertex Implementation. Where the coordinates, and other attributes are * float based, and the coordinates and texture coordinates are saved in two float arrays. * */ public class SVertex implements Vertex { - private int id = Integer.MAX_VALUE; - protected float[] coord = new float[3]; + private int id; protected boolean onCurve; - private float[] texCoord = new float[2]; - + protected final float[] coord = new float[3]; + private final float[] texCoord = new float[3]; + static final Factory factory = new Factory(); - - public static Factory factory() { return factory; } - + + public static Factory factory() { return factory; } + public static class Factory implements Vertex.Factory<SVertex> { + @Override public SVertex create() { return new SVertex(); } - public SVertex create(float x, float y, float z, boolean onCurve) { + public SVertex create(final Vertex src) { + return new SVertex(src); + } + + @Override + public SVertex create(final int id, final boolean onCurve, final float[] texCoordsBuffer) { + return new SVertex(id, onCurve, texCoordsBuffer); + } + + @Override + public SVertex create(final float x, final float y, final float z, final boolean onCurve) { return new SVertex(x, y, z, onCurve); } - public SVertex create(float[] coordsBuffer, int offset, int length, boolean onCurve) { + @Override + public SVertex create(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { return new SVertex(coordsBuffer, offset, length, onCurve); - } + } } - + public SVertex() { + this.id = Integer.MAX_VALUE; } - public SVertex(float x, float y, float z, boolean onCurve) { - setCoord(x, y, z); - setOnCurve(onCurve); + public SVertex(final Vertex src) { + this.id = Integer.MAX_VALUE; + System.arraycopy(src.getCoord(), 0, coord, 0, 3); + System.arraycopy(src.getTexCoord(), 0, texCoord, 0, 3); + setOnCurve(src.isOnCurve()); } - - public SVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { - setCoord(coordsBuffer, offset, length); + + public SVertex(final int id, final boolean onCurve, final float[] texCoordsBuffer) { + this.id = id; + this.onCurve = onCurve; + System.arraycopy(texCoordsBuffer, 0, texCoord, 0, 3); + } + + public SVertex(final float x, final float y, final float z, final boolean onCurve) { + this.id = Integer.MAX_VALUE; + setCoord(x, y, z); setOnCurve(onCurve); } - - public SVertex(float[] coordsBuffer, int offset, int length, - float[] texCoordsBuffer, int offsetTC, int lengthTC, boolean onCurve) { + + public SVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { + this.id = Integer.MAX_VALUE; setCoord(coordsBuffer, offset, length); - setTexCoord(texCoordsBuffer, offsetTC, lengthTC); setOnCurve(onCurve); } - - public final void setCoord(float x, float y, float z) { - this.coord[0] = x; - this.coord[1] = y; - this.coord[2] = z; + + @Override + public final void setCoord(final float x, final float y, final float z) { + coord[0] = x; + coord[1] = y; + coord[2] = z; } - public final void setCoord(float[] coordsBuffer, int offset, int length) { + @Override + public final void setCoord(final float[] coordsBuffer, final int offset, final int length) { System.arraycopy(coordsBuffer, offset, coord, 0, length); } - + + @Override + public int getCoordCount() { + return 3; + } + + @Override public final float[] getCoord() { return coord; } - public final void setX(float x) { + @Override + public final void setX(final float x) { this.coord[0] = x; } - public final void setY(float y) { + @Override + public final void setY(final float y) { this.coord[1] = y; } - public final void setZ(float z) { + @Override + public final void setZ(final float z) { this.coord[2] = z; } + @Override public final float getX() { return this.coord[0]; } + @Override public final float getY() { return this.coord[1]; } + @Override public final float getZ() { return this.coord[2]; } + @Override public final boolean isOnCurve() { return onCurve; } - public final void setOnCurve(boolean onCurve) { + @Override + public final void setOnCurve(final boolean onCurve) { this.onCurve = onCurve; } + @Override public final int getId(){ return id; } - - public final void setId(int id){ + + @Override + public final void setId(final int id){ this.id = id; } - - public boolean equals(Object obj) { + + @Override + public boolean equals(final Object obj) { if( obj == this) { return true; } @@ -140,35 +180,45 @@ public class SVertex implements Vertex { return false; } final Vertex v = (Vertex) obj; - return this == v || - isOnCurve() == v.isOnCurve() && - VectorUtil.checkEqualityVec2(getTexCoord(), v.getTexCoord()) && - VectorUtil.checkEquality(getCoord(), v.getCoord()) ; + return this == v || + isOnCurve() == v.isOnCurve() && + VectorUtil.isVec3Equal(getTexCoord(), 0, v.getTexCoord(), 0, FloatUtil.EPSILON) && + VectorUtil.isVec3Equal(getCoord(), 0, v.getCoord(), 0, FloatUtil.EPSILON) ; } - + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + + @Override public final float[] getTexCoord() { return texCoord; } - public final void setTexCoord(float s, float t) { - this.texCoord[0] = s; - this.texCoord[1] = t; + @Override + public final void setTexCoord(final float s, final float t, final float p) { + texCoord[0] = s; + texCoord[1] = t; + texCoord[2] = p; } - public final void setTexCoord(float[] texCoordsBuffer, int offset, int length) { + @Override + public final void setTexCoord(final float[] texCoordsBuffer, final int offset, final int length) { System.arraycopy(texCoordsBuffer, offset, texCoord, 0, length); } - + /** - * @return deep clone of this Vertex, but keeping the id blank + * @return deep clone of this Vertex elements */ + @Override public SVertex clone(){ - return new SVertex(this.coord, 0, 3, this.texCoord, 0, 2, this.onCurve); + return new SVertex(this); // OK to not call super.clone(), using own copy-ctor } - + + @Override public String toString() { - return "[ID: " + id + ", onCurve: " + onCurve + + return "[ID: " + id + ", onCurve: " + onCurve + ": p " + coord[0] + ", " + coord[1] + ", " + coord[2] + - ", t " + texCoord[0] + ", " + texCoord[1] + "]"; + ", t " + texCoord[0] + ", " + texCoord[1] + ", " + texCoord[2] + "]"; } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java index fb34de221..fc345f2a7 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java @@ -27,53 +27,106 @@ */ package com.jogamp.graph.geom; +import com.jogamp.opengl.math.VectorUtil; + +import jogamp.graph.geom.plane.AffineTransform; + public class Triangle { - private int id = Integer.MAX_VALUE; - final private Vertex[] vertices; - private boolean[] boundaryEdges = new boolean[3]; + private final Vertex[] vertices = new Vertex[3]; + private final boolean[] boundaryEdges = new boolean[3]; private boolean[] boundaryVertices = null; + private int id; + + public Triangle(final Vertex v1, final Vertex v2, final Vertex v3, final boolean[] boundaryVertices) { + id = Integer.MAX_VALUE; + vertices[0] = v1; + vertices[1] = v2; + vertices[2] = v3; + this.boundaryVertices = boundaryVertices; + } + + public Triangle(final Triangle src) { + id = src.id; + vertices[0] = src.vertices[0].clone(); + vertices[1] = src.vertices[1].clone(); + vertices[2] = src.vertices[2].clone(); + System.arraycopy(src.boundaryEdges, 0, boundaryEdges, 0, 3); + boundaryVertices = new boolean[3]; + System.arraycopy(src.boundaryVertices, 0, boundaryVertices, 0, 3); + } + + private Triangle(final int id, final boolean[] boundaryEdges, final boolean[] boundaryVertices){ + this.id = id; + System.arraycopy(boundaryEdges, 0, this.boundaryEdges, 0, 3); + this.boundaryVertices = new boolean[3]; + System.arraycopy(boundaryVertices, 0, this.boundaryVertices, 0, 3); + } - public Triangle(Vertex ... v123){ - vertices = v123; + /** + * Returns a transformed a clone of this instance using the given AffineTransform. + */ + public Triangle transform(final AffineTransform t, final Vertex.Factory<? extends Vertex> vertexFactory) { + final Triangle tri = new Triangle(id, boundaryEdges, boundaryVertices); + tri.vertices[0] = t.transform(vertices[0], vertexFactory.create()); + tri.vertices[1] = t.transform(vertices[1], vertexFactory.create()); + tri.vertices[2] = t.transform(vertices[2], vertexFactory.create()); + return tri; + } + + /** + * Returns true if all vertices are on-curve, otherwise false. + */ + public final boolean isOnCurve() { + return vertices[0].isOnCurve() && vertices[1].isOnCurve() && vertices[2].isOnCurve(); + } + + /** + * Returns true if all vertices are lines, i.e. zero tex-coord, otherwise false. + */ + public final boolean isLine() { + return VectorUtil.isVec2Zero(vertices[0].getTexCoord(), 0) && + VectorUtil.isVec2Zero(vertices[1].getTexCoord(), 0) && + VectorUtil.isVec2Zero(vertices[2].getTexCoord(), 0) ; } public int getId() { return id; } - public void setId(int id) { + public void setId(final int id) { this.id = id; } + /** Returns array of 3 vertices, denominating the triangle. */ public Vertex[] getVertices() { return vertices; } - + public boolean isEdgesBoundary() { return boundaryEdges[0] || boundaryEdges[1] || boundaryEdges[2]; } - + public boolean isVerticesBoundary() { return boundaryVertices[0] || boundaryVertices[1] || boundaryVertices[2]; } - public void setEdgesBoundary(boolean[] boundary) { - this.boundaryEdges = boundary; - } - public boolean[] getEdgeBoundary() { return boundaryEdges; } - + public boolean[] getVerticesBoundary() { return boundaryVertices; } - public void setVerticesBoundary(boolean[] boundaryVertices) { + public void setVerticesBoundary(final boolean[] boundaryVertices) { this.boundaryVertices = boundaryVertices; } - + + @Override public String toString() { - return "Tri ID: " + id + "\n" + vertices[0] + "\n" + vertices[1] + "\n" + vertices[2]; + return "Tri ID: " + id + ", onCurve "+isOnCurve()+"\n\t" + + vertices[0] + ", bound "+boundaryVertices[0]+"\n\t" + + vertices[1] + ", bound "+boundaryVertices[1]+"\n\t" + + vertices[2] + ", bound "+boundaryVertices[2]; } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java index 3080f32cc..e9c8dd193 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java @@ -27,27 +27,31 @@ */ package com.jogamp.graph.geom; +import com.jogamp.opengl.math.Vert3fImmutable; + /** - * A Vertex with custom memory layout using custom factory. + * A Vertex with custom memory layout using custom factory. */ -public interface Vertex extends Cloneable { +public interface Vertex extends Vert3fImmutable, Cloneable { public static interface Factory <T extends Vertex> { T create(); + T create(Vertex src); + + T create(int id, boolean onCurve, float[] texCoordsBuffer); + T create(float x, float y, float z, boolean onCurve); - T create(float[] coordsBuffer, int offset, int length, boolean onCurve); + T create(float[] coordsBuffer, int offset, int length, boolean onCurve); } - + void setCoord(float x, float y, float z); /** * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException */ void setCoord(float[] coordsBuffer, int offset, int length); - - float[] getCoord(); void setX(float x); @@ -55,35 +59,30 @@ public interface Vertex extends Cloneable { void setZ(float z); - float getX(); - - float getY(); - - float getZ(); - boolean isOnCurve(); void setOnCurve(boolean onCurve); int getId(); - + void setId(int id); - + float[] getTexCoord(); - - void setTexCoord(float s, float t); - + + void setTexCoord(float s, float t, float p); + /** * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException */ void setTexCoord(float[] texCoordsBuffer, int offset, int length); - + /** * @param obj the Object to compare this Vertex with - * @return true if {@code obj} is a Vertex and not null, on-curve flag is equal and has same vertex- and tex-coords. + * @return true if {@code obj} is a Vertex and not null, on-curve flag is equal and has same vertex- and tex-coords. */ + @Override boolean equals(Object obj); - + /** * @return deep clone of this Vertex */ diff --git a/src/jogl/classes/com/jogamp/graph/math/Quaternion.java b/src/jogl/classes/com/jogamp/graph/math/Quaternion.java deleted file mode 100755 index adaf073e3..000000000 --- a/src/jogl/classes/com/jogamp/graph/math/Quaternion.java +++ /dev/null @@ -1,382 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.math; - -import jogamp.graph.math.MathFloat; - -public class Quaternion { - protected float x,y,z,w; - - public Quaternion(){ - - } - - public Quaternion(float x, float y, float z, float w) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; - } - - /** Constructor to create a rotation based quaternion from two vectors - * @param vector1 - * @param vector2 - */ - public Quaternion(float[] vector1, float[] vector2) - { - float theta = (float)MathFloat.acos(dot(vector1, vector2)); - float[] cross = cross(vector1,vector2); - cross = normalizeVec(cross); - - this.x = (float)MathFloat.sin(theta/2)*cross[0]; - this.y = (float)MathFloat.sin(theta/2)*cross[1]; - this.z = (float)MathFloat.sin(theta/2)*cross[2]; - this.w = (float)MathFloat.cos(theta/2); - this.normalize(); - } - - /** Transform the rotational quaternion to axis based rotation angles - * @return new float[4] with ,theta,Rx,Ry,Rz - */ - public float[] toAxis() - { - float[] vec = new float[4]; - float scale = (float)MathFloat.sqrt(x * x + y * y + z * z); - vec[0] =(float) MathFloat.acos(w) * 2.0f; - vec[1] = x / scale; - vec[2] = y / scale; - vec[3] = z / scale; - return vec; - } - - /** Normalize a vector - * @param vector input vector - * @return normalized vector - */ - private float[] normalizeVec(float[] vector) - { - float[] newVector = new float[3]; - - float d = MathFloat.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); - if(d> 0.0f) - { - newVector[0] = vector[0]/d; - newVector[1] = vector[1]/d; - newVector[2] = vector[2]/d; - } - return newVector; - } - /** compute the dot product of two points - * @param vec1 vector 1 - * @param vec2 vector 2 - * @return the dot product as float - */ - private float dot(float[] vec1, float[] vec2) - { - return (vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]); - } - /** cross product vec1 x vec2 - * @param vec1 vector 1 - * @param vec2 vecttor 2 - * @return the resulting vector - */ - private float[] cross(float[] vec1, float[] vec2) - { - float[] out = new float[3]; - - out[0] = vec2[2]*vec1[1] - vec2[1]*vec1[2]; - out[1] = vec2[0]*vec1[2] - vec2[2]*vec1[0]; - out[2] = vec2[1]*vec1[0] - vec2[0]*vec1[1]; - - return out; - } - public float getW() { - return w; - } - public void setW(float w) { - this.w = w; - } - public float getX() { - return x; - } - public void setX(float x) { - this.x = x; - } - public float getY() { - return y; - } - public void setY(float y) { - this.y = y; - } - public float getZ() { - return z; - } - public void setZ(float z) { - this.z = z; - } - - /** Add a quaternion - * @param q quaternion - */ - public void add(Quaternion q) - { - x+=q.x; - y+=q.y; - z+=q.z; - } - - /** Subtract a quaternion - * @param q quaternion - */ - public void subtract(Quaternion q) - { - x-=q.x; - y-=q.y; - z-=q.z; - } - - /** Divide a quaternion by a constant - * @param n a float to divide by - */ - public void divide(float n) - { - x/=n; - y/=n; - z/=n; - } - - /** Multiply this quaternion by - * the param quaternion - * @param q a quaternion to multiply with - */ - public void mult(Quaternion q) - { - float w1 = w*q.w - (x*q.x + y*q.y + z*q.z); - - float x1 = w*q.z + q.w*z + y*q.z - z*q.y; - float y1 = w*q.x + q.w*x + z*q.x - x*q.z; - float z1 = w*q.y + q.w*y + x*q.y - y*q.x; - - w = w1; - x = x1; - y = y1; - z = z1; - } - - /** Multiply a quaternion by a constant - * @param n a float constant - */ - public void mult(float n) - { - x*=n; - y*=n; - z*=n; - } - - /** Normalize a quaternion required if - * to be used as a rotational quaternion - */ - public void normalize() - { - float norme = (float)MathFloat.sqrt(w*w + x*x + y*y + z*z); - if (norme == 0.0f) - { - w = 1.0f; - x = y = z = 0.0f; - } - else - { - float recip = 1.0f/norme; - - w *= recip; - x *= recip; - y *= recip; - z *= recip; - } - } - - /** Invert the quaternion If rotational, - * will produce a the inverse rotation - */ - public void inverse() - { - float norm = w*w + x*x + y*y + z*z; - - float recip = 1.0f/norm; - - w *= recip; - x = -1*x*recip; - y = -1*y*recip; - z = -1*z*recip; - } - - /** Transform this quaternion to a - * 4x4 column matrix representing the rotation - * @return new float[16] column matrix 4x4 - */ - public float[] toMatrix() - { - float[] matrix = new float[16]; - matrix[0] = 1.0f - 2*y*y - 2*z*z; - matrix[1] = 2*x*y + 2*w*z; - matrix[2] = 2*x*z - 2*w*y; - matrix[3] = 0; - - matrix[4] = 2*x*y - 2*w*z; - matrix[5] = 1.0f - 2*x*x - 2*z*z; - matrix[6] = 2*y*z + 2*w*x; - matrix[7] = 0; - - matrix[8] = 2*x*z + 2*w*y; - matrix[9] = 2*y*z - 2*w*x; - matrix[10] = 1.0f - 2*x*x - 2*y*y; - matrix[11] = 0; - - matrix[12] = 0; - matrix[13] = 0; - matrix[14] = 0; - matrix[15] = 1; - return matrix; - } - - /** Set this quaternion from a Sphereical interpolation - * of two param quaternion, used mostly for rotational animation - * @param a initial quaternion - * @param b target quaternion - * @param t float between 0 and 1 representing interp. - */ - public void slerp(Quaternion a,Quaternion b, float t) - { - float omega, cosom, sinom, sclp, sclq; - cosom = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; - if ((1.0f+cosom) > MathFloat.E) { - if ((1.0f-cosom) > MathFloat.E) { - omega = (float)MathFloat.acos(cosom); - sinom = (float)MathFloat.sin(omega); - sclp = (float)MathFloat.sin((1.0f-t)*omega) / sinom; - sclq = (float)MathFloat.sin(t*omega) / sinom; - } - else { - sclp = 1.0f - t; - sclq = t; - } - x = sclp*a.x + sclq*b.x; - y = sclp*a.y + sclq*b.y; - z = sclp*a.z + sclq*b.z; - w = sclp*a.w + sclq*b.w; - } - else { - x =-a.y; - y = a.x; - z =-a.w; - w = a.z; - sclp = MathFloat.sin((1.0f-t) * MathFloat.PI * 0.5f); - sclq = MathFloat.sin(t * MathFloat.PI * 0.5f); - x = sclp*a.x + sclq*b.x; - y = sclp*a.y + sclq*b.y; - z = sclp*a.z + sclq*b.z; - } - } - - /** Check if this quaternion is empty, ie (0,0,0,1) - * @return true if empty, false otherwise - */ - public boolean isEmpty() - { - if (w==1 && x==0 && y==0 && z==0) - return true; - return false; - } - - /** Check if this quaternion represents an identity - * matrix, for rotation. - * @return true if it is an identity rep., false otherwise - */ - public boolean isIdentity() - { - if (w==0 && x==0 && y==0 && z==0) - return true; - return false; - } - - /** compute the quaternion from a 3x3 column matrix - * @param m 3x3 column matrix - */ - public void setFromMatrix(float[] m) { - float T= m[0] + m[4] + m[8] + 1; - if (T>0){ - float S = 0.5f / (float)MathFloat.sqrt(T); - w = 0.25f / S; - x = ( m[5] - m[7]) * S; - y = ( m[6] - m[2]) * S; - z = ( m[1] - m[3] ) * S; - } - else{ - if ((m[0] > m[4])&(m[0] > m[8])) { - float S = MathFloat.sqrt( 1.0f + m[0] - m[4] - m[8] ) * 2f; // S=4*qx - w = (m[7] - m[5]) / S; - x = 0.25f * S; - y = (m[3] + m[1]) / S; - z = (m[6] + m[2]) / S; - } - else if (m[4] > m[8]) { - float S = MathFloat.sqrt( 1.0f + m[4] - m[0] - m[8] ) * 2f; // S=4*qy - w = (m[6] - m[2]) / S; - x = (m[3] + m[1]) / S; - y = 0.25f * S; - z = (m[7] + m[5]) / S; - } - else { - float S = MathFloat.sqrt( 1.0f + m[8] - m[0] - m[4] ) * 2f; // S=4*qz - w = (m[3] - m[1]) / S; - x = (m[6] + m[2]) / S; - y = (m[7] + m[5]) / S; - z = 0.25f * S; - } - } - } - - /** Check if the the 3x3 matrix (param) is in fact - * an affine rotational matrix - * @param m 3x3 column matrix - * @return true if representing a rotational matrix, false otherwise - */ - public boolean isRotationMatrix(float[] m) { - double epsilon = 0.01; // margin to allow for rounding errors - if (MathFloat.abs(m[0]*m[3] + m[3]*m[4] + m[6]*m[7]) > epsilon) return false; - if (MathFloat.abs(m[0]*m[2] + m[3]*m[5] + m[6]*m[8]) > epsilon) return false; - if (MathFloat.abs(m[1]*m[2] + m[4]*m[5] + m[7]*m[8]) > epsilon) return false; - if (MathFloat.abs(m[0]*m[0] + m[3]*m[3] + m[6]*m[6] - 1) > epsilon) return false; - if (MathFloat.abs(m[1]*m[1] + m[4]*m[4] + m[7]*m[7] - 1) > epsilon) return false; - if (MathFloat.abs(m[2]*m[2] + m[5]*m[5] + m[8]*m[8] - 1) > epsilon) return false; - return (MathFloat.abs(determinant(m)-1) < epsilon); - } - private float determinant(float[] m) { - return m[0]*m[4]*m[8] + m[3]*m[7]*m[2] + m[6]*m[1]*m[5] - m[0]*m[7]*m[5] - m[3]*m[1]*m[8] - m[6]*m[4]*m[2]; - } -} diff --git a/src/jogl/classes/com/jogamp/graph/math/VectorUtil.java b/src/jogl/classes/com/jogamp/graph/math/VectorUtil.java deleted file mode 100755 index d51afcbab..000000000 --- a/src/jogl/classes/com/jogamp/graph/math/VectorUtil.java +++ /dev/null @@ -1,433 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.math; - -import java.util.ArrayList; - -import jogamp.graph.math.MathFloat; - -import com.jogamp.graph.geom.Vertex; - -public class VectorUtil { - - public enum Winding { - CW(-1), CCW(1); - - public final int dir; - - Winding(int dir) { - this.dir = dir; - } - } - - public static final int COLLINEAR = 0; - - /** compute the dot product of two points - * @param vec1 vector 1 - * @param vec2 vector 2 - * @return the dot product as float - */ - public static float dot(float[] vec1, float[] vec2) - { - return (vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]); - } - /** Normalize a vector - * @param vector input vector - * @return normalized vector - */ - public static float[] normalize(float[] vector) - { - float[] newVector = new float[3]; - - float d = MathFloat.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); - if(d> 0.0f) - { - newVector[0] = vector[0]/d; - newVector[1] = vector[1]/d; - newVector[2] = vector[2]/d; - } - return newVector; - } - - /** Scales a vector by param - * @param vector input vector - * @param scale constant to scale by - * @return scaled vector - */ - public static float[] scale(float[] vector, float scale) - { - float[] newVector = new float[3]; - - newVector[0] = vector[0]*scale; - newVector[1] = vector[1]*scale; - newVector[2] = vector[2]*scale; - return newVector; - } - - /** Adds to vectors - * @param v1 vector 1 - * @param v2 vector 2 - * @return v1 + v2 - */ - public static float[] vectorAdd(float[] v1, float[] v2) - { - float[] newVector = new float[3]; - - newVector[0] = v1[0] + v2[0]; - newVector[1] = v1[1] + v2[1]; - newVector[2] = v1[2] + v2[2]; - return newVector; - } - - /** cross product vec1 x vec2 - * @param vec1 vector 1 - * @param vec2 vecttor 2 - * @return the resulting vector - */ - public static float[] cross(float[] vec1, float[] vec2) - { - float[] out = new float[3]; - - out[0] = vec2[2]*vec1[1] - vec2[1]*vec1[2]; - out[1] = vec2[0]*vec1[2] - vec2[2]*vec1[0]; - out[2] = vec2[1]*vec1[0] - vec2[0]*vec1[1]; - - return out; - } - - /** Column Matrix Vector multiplication - * @param colMatrix column matrix (4x4) - * @param vec vector(x,y,z) - * @return result new float[3] - */ - public static float[] colMatrixVectorMult(float[] colMatrix, float[] vec) - { - float[] out = new float[3]; - - out[0] = vec[0]*colMatrix[0] + vec[1]*colMatrix[4] + vec[2]*colMatrix[8] + colMatrix[12]; - out[1] = vec[0]*colMatrix[1] + vec[1]*colMatrix[5] + vec[2]*colMatrix[9] + colMatrix[13]; - out[2] = vec[0]*colMatrix[2] + vec[1]*colMatrix[6] + vec[2]*colMatrix[10] + colMatrix[14]; - - return out; - } - - /** Matrix Vector multiplication - * @param rawMatrix column matrix (4x4) - * @param vec vector(x,y,z) - * @return result new float[3] - */ - public static float[] rowMatrixVectorMult(float[] rawMatrix, float[] vec) - { - float[] out = new float[3]; - - out[0] = vec[0]*rawMatrix[0] + vec[1]*rawMatrix[1] + vec[2]*rawMatrix[2] + rawMatrix[3]; - out[1] = vec[0]*rawMatrix[4] + vec[1]*rawMatrix[5] + vec[2]*rawMatrix[6] + rawMatrix[7]; - out[2] = vec[0]*rawMatrix[8] + vec[1]*rawMatrix[9] + vec[2]*rawMatrix[10] + rawMatrix[11]; - - return out; - } - - /** Calculate the midpoint of two values - * @param p1 first value - * @param p2 second vale - * @return midpoint - */ - public static float mid(float p1, float p2) - { - return (p1+p2)/2.0f; - } - /** Calculate the midpoint of two points - * @param p1 first point - * @param p2 second point - * @return midpoint - */ - public static float[] mid(float[] p1, float[] p2) - { - float[] midPoint = new float[3]; - midPoint[0] = (p1[0] + p2[0])*0.5f; - midPoint[1] = (p1[1] + p2[1])*0.5f; - midPoint[2] = (p1[2] + p2[2])*0.5f; - - return midPoint; - } - /** Compute the norm of a vector - * @param vec vector - * @return vorm - */ - public static float norm(float[] vec) - { - return MathFloat.sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]); - } - /** Compute distance between 2 points - * @param p0 a ref point on the line - * @param vec vector representing the direction of the line - * @param point the point to compute the relative distance of - * @return distance float - */ - public static float computeLength(float[] p0, float[] point) - { - float[] w = new float[]{point[0]-p0[0],point[1]-p0[1],point[2]-p0[2]}; - - float distance = MathFloat.sqrt(w[0]*w[0] + w[1]*w[1] + w[2]*w[2]); - - return distance; - } - - /**Check equality of 2 vec3 vectors - * @param v1 vertex 1 - * @param v2 vertex 2 - * @return - */ - public static boolean checkEquality(float[] v1, float[] v2) - { - if(Float.compare(v1[0], v2[0]) == 0 && - Float.compare(v1[1], v2[1]) == 0 && - Float.compare(v1[2], v2[2]) == 0 ) - return true; - return false; - } - - /**Check equality of 2 vec2 vectors - * @param v1 vertex 1 - * @param v2 vertex 2 - * @return - */ - public static boolean checkEqualityVec2(float[] v1, float[] v2) - { - if(Float.compare(v1[0], v2[0]) == 0 && - Float.compare(v1[1], v2[1]) == 0) - return true; - return false; - } - - /** Compute the determinant of 3 vectors - * @param a vector 1 - * @param b vector 2 - * @param c vector 3 - * @return the determinant value - */ - public static float computeDeterminant(float[] a, float[] b, float[] c) - { - float area = a[0]*b[1]*c[2] + a[1]*b[2]*c[0] + a[2]*b[0]*c[1] - a[0]*b[2]*c[1] - a[1]*b[0]*c[2] - a[2]*b[1]*c[0]; - return area; - } - - /** Check if three vertices are colliniear - * @param v1 vertex 1 - * @param v2 vertex 2 - * @param v3 vertex 3 - * @return true if collinear, false otherwise - */ - public static boolean checkCollinear(float[] v1, float[] v2, float[] v3) - { - return (computeDeterminant(v1, v2, v3) == VectorUtil.COLLINEAR); - } - - /** Compute Vector - * @param v1 vertex 1 - * @param v2 vertex2 2 - * @return Vector V1V2 - */ - public static float[] computeVector(float[] v1, float[] v2) - { - float[] vector = new float[3]; - vector[0] = v2[0] - v1[0]; - vector[1] = v2[1] - v1[1]; - vector[2] = v2[2] - v1[2]; - return vector; - } - - /** Check if vertices in triangle circumcircle - * @param a triangle vertex 1 - * @param b triangle vertex 2 - * @param c triangle vertex 3 - * @param d vertex in question - * @return true if the vertex d is inside the circle defined by the - * vertices a, b, c. from paper by Guibas and Stolfi (1985). - */ - public static boolean inCircle(Vertex a, Vertex b, Vertex c, Vertex d){ - return (a.getX() * a.getX() + a.getY() * a.getY()) * triArea(b, c, d) - - (b.getX() * b.getX() + b.getY() * b.getY()) * triArea(a, c, d) + - (c.getX() * c.getX() + c.getY() * c.getY()) * triArea(a, b, d) - - (d.getX() * d.getX() + d.getY() * d.getY()) * triArea(a, b, c) > 0; - } - - /** Computes oriented area of a triangle - * @param a first vertex - * @param b second vertex - * @param c third vertex - * @return compute twice the area of the oriented triangle (a,b,c), the area - * is positive if the triangle is oriented counterclockwise. - */ - public static float triArea(Vertex a, Vertex b, Vertex c){ - return (b.getX() - a.getX()) * (c.getY() - a.getY()) - (b.getY() - a.getY())*(c.getX() - a.getX()); - } - - /** Check if a vertex is in triangle using - * barycentric coordinates computation. - * @param a first triangle vertex - * @param b second triangle vertex - * @param c third triangle vertex - * @param p the vertex in question - * @return true if p is in triangle (a, b, c), false otherwise. - */ - public static boolean vertexInTriangle(float[] a, float[] b, float[] c, float[] p){ - // Compute vectors - float[] ac = computeVector(a, c); //v0 - float[] ab = computeVector(a, b); //v1 - float[] ap = computeVector(a, p); //v2 - - // Compute dot products - float dot00 = dot(ac, ac); - float dot01 = dot(ac, ab); - float dot02 = dot(ac, ap); - float dot11 = dot(ab, ab); - float dot12 = dot(ab, ap); - - // Compute barycentric coordinates - float invDenom = 1 / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; - float v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - // Check if point is in triangle - return (u >= 0) && (v >= 0) && (u + v < 1); - } - - /** Check if points are in ccw order - * @param a first vertex - * @param b second vertex - * @param c third vertex - * @return true if the points a,b,c are in a ccw order - */ - public static boolean ccw(Vertex a, Vertex b, Vertex c){ - return triArea(a,b,c) > 0; - } - - /** Compute the winding of given points - * @param a first vertex - * @param b second vertex - * @param c third vertex - * @return Winding - */ - public static Winding getWinding(Vertex a, Vertex b, Vertex c) { - return triArea(a,b,c) > 0 ? Winding.CCW : Winding.CW ; - } - - /** Computes the area of a list of vertices to check if ccw - * @param vertices - * @return positive area if ccw else negative area value - */ - public static float area(ArrayList<Vertex> vertices) { - int n = vertices.size(); - float area = 0.0f; - for (int p = n - 1, q = 0; q < n; p = q++) - { - float[] pCoord = vertices.get(p).getCoord(); - float[] qCoord = vertices.get(q).getCoord(); - area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1]; - } - return area; - } - - /** Compute the general winding of the vertices - * @param vertices array of Vertices - * @return CCW or CW {@link Winding} - */ - public static Winding getWinding(ArrayList<Vertex> vertices) { - return area(vertices) >= 0 ? Winding.CCW : Winding.CW ; - } - - - /** Compute intersection between two segments - * @param a vertex 1 of first segment - * @param b vertex 2 of first segment - * @param c vertex 1 of second segment - * @param d vertex 2 of second segment - * @return the intersection coordinates if the segments intersect, otherwise - * returns null - */ - public static float[] seg2SegIntersection(Vertex a, Vertex b, Vertex c, Vertex d) { - float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); - - if (determinant == 0) - return null; - - float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); - float beta = (c.getX()*d.getY()-c.getY()*d.getY()); - float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; - float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; - - float gamma = (xi - a.getX())/(b.getX() - a.getX()); - float gamma1 = (xi - c.getX())/(d.getX() - c.getX()); - if(gamma <= 0 || gamma >= 1) return null; - if(gamma1 <= 0 || gamma1 >= 1) return null; - - return new float[]{xi,yi,0}; - } - - /** Compute intersection between two lines - * @param a vertex 1 of first line - * @param b vertex 2 of first line - * @param c vertex 1 of second line - * @param d vertex 2 of second line - * @return the intersection coordinates if the lines intersect, otherwise - * returns null - */ - public static float[] line2lineIntersection(Vertex a, Vertex b, Vertex c, Vertex d) { - float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); - - if (determinant == 0) - return null; - - float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); - float beta = (c.getX()*d.getY()-c.getY()*d.getY()); - float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; - float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; - - return new float[]{xi,yi,0}; - } - - /** Check if a segment intersects with a triangle - * @param a vertex 1 of the triangle - * @param b vertex 2 of the triangle - * @param c vertex 3 of the triangle - * @param d vertex 1 of first segment - * @param e vertex 2 of first segment - * @return true if the segment intersects at least one segment of the triangle, false otherwise - */ - public static boolean tri2SegIntersection(Vertex a, Vertex b, Vertex c, Vertex d, Vertex e){ - if(seg2SegIntersection(a, b, d, e) != null) - return true; - if(seg2SegIntersection(b, c, d, e) != null) - return true; - if(seg2SegIntersection(a, c, d, e) != null) - return true; - - return false; - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/FBObject.java b/src/jogl/classes/com/jogamp/opengl/FBObject.java index cc0af29a9..03693a688 100644 --- a/src/jogl/classes/com/jogamp/opengl/FBObject.java +++ b/src/jogl/classes/com/jogamp/opengl/FBObject.java @@ -28,79 +28,120 @@ package com.jogamp.opengl; -import java.util.ArrayList; import java.util.Arrays; import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; import javax.media.opengl.GL3; import javax.media.opengl.GLBase; import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.FBObject.Attachment.Type; /** * Core utility class simplifying usage of framebuffer objects (FBO) - * with all {@link GLProfile}s. + * with all {@link GLProfile}s. * <p> - * Supports on-the-fly reconfiguration of dimension and multisample buffers via {@link #reset(GL, int, int, int)} + * Supports on-the-fly reconfiguration of dimension and multisample buffers via {@link #reset(GL, int, int, int, boolean)} * while preserving the {@link Attachment} references. * </p> * <p> * Integrates default read/write framebuffers via {@link GLContext#getDefaultReadFramebuffer()} and {@link GLContext#getDefaultReadFramebuffer()}, * which is being hooked at {@link GL#glBindFramebuffer(int, int)} when the default (<code>zero</code>) framebuffer is selected. * </p> - * + * * <p>FIXME: Implement support for {@link Type#DEPTH_TEXTURE}, {@link Type#STENCIL_TEXTURE} .</p> */ public class FBObject { - protected static final boolean DEBUG = Debug.debug("FBObject"); - private static final boolean forceMinimumFBOSupport = Debug.isPropertyDefined("jogl.fbo.force.min", true); - + protected static final boolean DEBUG; + private static final int USER_MAX_TEXTURE_SIZE; + private static final boolean FBOResizeQuirk = false; + + static { + Debug.initSingleton(); + DEBUG = Debug.debug("FBObject"); + USER_MAX_TEXTURE_SIZE = PropertyAccess.getIntProperty("jogl.debug.FBObject.MaxTextureSize", true, 0); + } + private static enum DetachAction { NONE, DISPOSE, RECREATE }; - - /** - * Marker interface, denotes a color buffer attachment. + + /** + * Generic color buffer FBO attachment, either of type {@link ColorAttachment} or {@link TextureAttachment}. * <p>Always an instance of {@link Attachment}.</p> - * <p>Either an instance of {@link ColorAttachment} or {@link TextureAttachment}.</b> */ - public static interface Colorbuffer { - /** + public static interface Colorbuffer { + /** * Initializes the color buffer and set it's parameter, if uninitialized, i.e. name is <code>zero</code>. * @return <code>true</code> if newly initialized, otherwise <code>false</code>. - * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. + * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. */ - public boolean initialize(GL gl) throws GLException; - - /** + boolean initialize(final GL gl) throws GLException; + + /** * Releases the color buffer if initialized, i.e. name is not <code>zero</code>. - * @throws GLException if buffer release fails. + * @throws GLException if buffer release fails. */ - public void free(GL gl) throws GLException; - + void free(final GL gl) throws GLException; + /** * Writes the internal format to the given GLCapabilities object. * @param caps the destination for format bits * @param rgba8Avail whether rgba8 is available */ - public void formatToGLCapabilities(GLCapabilities caps, boolean rgba8Avail); + void formatToGLCapabilities(final GLCapabilities caps, final boolean rgba8Avail); + + /** + * Returns <code>true</code> if instance is of type {@link TextureAttachment} + * and <code>false</code> if instance is of type {@link ColorAttachment}. + */ + boolean isTextureAttachment(); + + /** + * Casts this object to a {@link TextureAttachment} reference, see {@link #isTextureAttachment()}. + * @throws GLException if this object is not of type {@link TextureAttachment} + * @see #isTextureAttachment() + */ + TextureAttachment getTextureAttachment(); + + /** + * Casts this object to a {@link ColorAttachment} reference, see {@link #isTextureAttachment()}. + * @throws GLException if this object is not of type {@link ColorAttachment} + * @see #isTextureAttachment() + */ + ColorAttachment getColorAttachment(); + + /** internal format of colorbuffer */ + int getFormat(); + + /** width of colorbuffer */ + int getWidth(); + + /** height of colorbuffer */ + int getHeight(); + + /** colorbuffer name [1..max] */ + int getName(); } - - /** Common super class of all attachments */ + + /** Common super class of all FBO attachments */ public static abstract class Attachment { - public enum Type { + public enum Type { NONE, DEPTH, STENCIL, DEPTH_STENCIL, COLOR, COLOR_TEXTURE, DEPTH_TEXTURE, STENCIL_TEXTURE; - - /** + + /** * Returns {@link #COLOR}, {@link #DEPTH}, {@link #STENCIL} or {@link #DEPTH_STENCIL} - * @throws IllegalArgumentException if <code>format</code> cannot be handled. + * @throws IllegalArgumentException if <code>format</code> cannot be handled. */ - public static Type determine(int format) throws IllegalArgumentException { + public static Type determine(final int format) throws IllegalArgumentException { switch(format) { case GL.GL_RGBA4: case GL.GL_RGB5_A1: @@ -119,41 +160,43 @@ public class FBObject { case GL.GL_DEPTH24_STENCIL8: return Type.DEPTH_STENCIL; default: - throw new IllegalArgumentException("format invalid: 0x"+Integer.toHexString(format)); - } + throw new IllegalArgumentException("format invalid: "+toHexString(format)); + } } }; - + /** immutable type [{@link #COLOR}, {@link #DEPTH}, {@link #STENCIL}, {@link #COLOR_TEXTURE}, {@link #DEPTH_TEXTURE}, {@link #STENCIL_TEXTURE} ] */ public final Type type; - + /** immutable the internal format */ public final int format; - + private int width, height; - + private int name; - - protected Attachment(Type type, int iFormat, int width, int height, int name) { + + protected Attachment(final Type type, final int iFormat, final int width, final int height, final int name) { this.type = type; this.format = iFormat; this.width = width; this.height = height; this.name = name; } - + /** * Writes the internal format to the given GLCapabilities object. * @param caps the destination for format bits * @param rgba8Avail whether rgba8 is available */ - public final void formatToGLCapabilities(GLCapabilities caps, boolean rgba8Avail) { + public final void formatToGLCapabilities(final GLCapabilities caps, final boolean rgba8Avail) { final int _format; switch(format) { case GL.GL_RGBA: - _format = rgba8Avail ? GL.GL_RGBA8 : GL.GL_RGBA4; + case 4: + _format = rgba8Avail ? GL.GL_RGBA8 : GL.GL_RGBA4; break; case GL.GL_RGB: + case 3: _format = rgba8Avail ? GL.GL_RGB8 : GL.GL_RGB565; break; default: @@ -189,7 +232,7 @@ public class FBObject { caps.setGreenBits(8); caps.setBlueBits(8); caps.setAlphaBits(8); - break; + break; case GL.GL_DEPTH_COMPONENT16: caps.setDepthBits(16); break; @@ -213,21 +256,24 @@ public class FBObject { caps.setStencilBits(8); break; default: - throw new IllegalArgumentException("format invalid: 0x"+Integer.toHexString(format)); + throw new IllegalArgumentException("format invalid: "+toHexString(format)); } } - + + /** immutable internal format of attachment */ + public final int getFormat() { return format; } + /** width of attachment */ public final int getWidth() { return width; } /** height of attachment */ public final int getHeight() { return height; } - /* pp */ final void setSize(int w, int h) { width = w; height = h; } - + /* pp */ final void setSize(final int w, final int h) { width = w; height = h; } + /** buffer name [1..max], maybe a texture or renderbuffer name, depending on type. */ - public final int getName() { return name; } - /* pp */ final void setName(int n) { name = n; } - - /** + public final int getName() { return name; } + /* pp */ final void setName(final int n) { name = n; } + + /** * Initializes the attachment and set it's parameter, if uninitialized, i.e. name is <code>zero</code>. * <pre> final boolean init = 0 == name; @@ -237,11 +283,11 @@ public class FBObject { return init; * </pre> * @return <code>true</code> if newly initialized, otherwise <code>false</code>. - * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. + * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. */ - public abstract boolean initialize(GL gl) throws GLException; - - /** + public abstract boolean initialize(final GL gl) throws GLException; + + /** * Releases the attachment if initialized, i.e. name is not <code>zero</code>. * <pre> if(0 != name) { @@ -249,10 +295,10 @@ public class FBObject { name = 0; } * </pre> - * @throws GLException if buffer release fails. + * @throws GLException if buffer release fails. */ - public abstract void free(GL gl) throws GLException; - + public abstract void free(final GL gl) throws GLException; + /** * <p> * Comparison by {@link #type}, {@link #format}, {@link #width}, {@link #height} and {@link #name}. @@ -260,7 +306,7 @@ public class FBObject { * {@inheritDoc} */ @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if( this == o ) return true; if( ! ( o instanceof Attachment ) ) return false; final Attachment a = (Attachment)o; @@ -270,7 +316,7 @@ public class FBObject { height== a.height && name == a.name ; } - + /** * <p> * Hashed by {@link #type}, {@link #format}, {@link #width}, {@link #height} and {@link #name}. @@ -287,25 +333,26 @@ public class FBObject { hash = ((hash << 5) - hash) + name; return hash; } - + int objectHashCode() { return super.hashCode(); } - + + @Override public String toString() { - return getClass().getSimpleName()+"[type "+type+", format 0x"+Integer.toHexString(format)+", "+width+"x"+height+ - ", name 0x"+Integer.toHexString(name)+", obj 0x"+Integer.toHexString(objectHashCode())+"]"; + return getClass().getSimpleName()+"[type "+type+", format "+toHexString(format)+", "+width+"x"+height+ + "; name "+toHexString(name)+", obj "+toHexString(objectHashCode())+"]"; } - - public static Type getType(int attachmentPoint, int maxColorAttachments) { + + public static Type getType(final int attachmentPoint, final int maxColorAttachments) { if( GL.GL_COLOR_ATTACHMENT0 <= attachmentPoint && attachmentPoint < GL.GL_COLOR_ATTACHMENT0+maxColorAttachments ) { return Type.COLOR; } switch(attachmentPoint) { case GL.GL_DEPTH_ATTACHMENT: return Type.DEPTH; - case GL.GL_STENCIL_ATTACHMENT: + case GL.GL_STENCIL_ATTACHMENT: return Type.STENCIL; - default: - throw new IllegalArgumentException("Invalid attachment point 0x"+Integer.toHexString(attachmentPoint)); + default: + throw new IllegalArgumentException("Invalid attachment point "+toHexString(attachmentPoint)); } } } @@ -313,35 +360,36 @@ public class FBObject { /** Other renderbuffer attachment which maybe a colorbuffer, depth or stencil. */ public static class RenderAttachment extends Attachment { private int samples; - + /** - * @param type allowed types are {@link Type#DEPTH}, {@link Type#STENCIL} or {@link Type#COLOR} + * @param type allowed types are {@link Type#DEPTH_STENCIL} {@link Type#DEPTH}, {@link Type#STENCIL} or {@link Type#COLOR} * @param iFormat * @param samples * @param width * @param height * @param name */ - public RenderAttachment(Type type, int iFormat, int samples, int width, int height, int name) { + public RenderAttachment(final Type type, final int iFormat, final int samples, final int width, final int height, final int name) { super(validateType(type), iFormat, width, height, name); this.samples = samples; } - + /** number of samples, or zero for no multisampling */ public final int getSamples() { return samples; } - /* pp */ final void setSamples(int s) { samples = s; } - - private static Type validateType(Type type) { + /* pp */ final void setSamples(final int s) { samples = s; } + + private static Type validateType(final Type type) { switch(type) { + case DEPTH_STENCIL: case DEPTH: case STENCIL: case COLOR: return type; - default: + default: throw new IllegalArgumentException("Invalid type: "+type); } } - + /** * <p> * Comparison by {@link #type}, {@link #format}, {@link #samples}, {@link #width}, {@link #height} and {@link #name}. @@ -349,13 +397,13 @@ public class FBObject { * {@inheritDoc} */ @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if( this == o ) return true; if( ! ( o instanceof RenderAttachment ) ) return false; return super.equals(o) && samples == ((RenderAttachment)o).samples; } - + /** * <p> * Hashed by {@link #type}, {@link #format}, {@link #samples}, {@link #width}, {@link #height} and {@link #name}. @@ -371,60 +419,72 @@ public class FBObject { } @Override - public boolean initialize(GL gl) throws GLException { + public boolean initialize(final GL gl) throws GLException { final boolean init = 0 == getName(); if( init ) { - int glerr = checkPreGLError(gl); - + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } final int[] name = new int[] { -1 }; gl.glGenRenderbuffers(1, name, 0); setName(name[0]); - + gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, getName()); if( samples > 0 ) { - ((GL2GL3)gl).glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, format, getWidth(), getHeight()); + ((GL2ES3)gl).glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, format, getWidth(), getHeight()); } else { gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, format, getWidth(), getHeight()); } - glerr = gl.glGetError(); - if(GL.GL_NO_ERROR != glerr) { - gl.glDeleteRenderbuffers(1, name, 0); - setName(0); - throw new GLException("GL Error 0x"+Integer.toHexString(glerr)+" while creating "+this); + if( checkError ) { + final int glerr = gl.glGetError(); + if(GL.GL_NO_ERROR != glerr) { + gl.glDeleteRenderbuffers(1, name, 0); + setName(0); + throw new GLException("GL Error "+toHexString(glerr)+" while creating "+this); + } } if(DEBUG) { - System.err.println("Attachment.init: "+this); + System.err.println("Attachment.init.X: "+this); } } return init; } - + @Override - public void free(GL gl) { + public void free(final GL gl) { final int[] name = new int[] { getName() }; if( 0 != name[0] ) { - gl.glDeleteRenderbuffers(1, name, 0); - setName(0); if(DEBUG) { - System.err.println("Attachment.free: "+this); + System.err.println("Attachment.free.0: "+this); } + gl.glDeleteRenderbuffers(1, name, 0); + setName(0); } } - + + @Override public String toString() { - return getClass().getSimpleName()+"[type "+type+", format 0x"+Integer.toHexString(format)+", samples "+samples+", "+getWidth()+"x"+getHeight()+ - ", name 0x"+Integer.toHexString(getName())+", obj 0x"+Integer.toHexString(objectHashCode())+"]"; + return getClass().getSimpleName()+"[type "+type+", format "+toHexString(format)+", samples "+samples+", "+getWidth()+"x"+getHeight()+ + ", name "+toHexString(getName())+", obj "+toHexString(objectHashCode())+"]"; } } - - /** Color render buffer attachment */ + + /** Color render buffer FBO attachment */ public static class ColorAttachment extends RenderAttachment implements Colorbuffer { - public ColorAttachment(int iFormat, int samples, int width, int height, int name) { + public ColorAttachment(final int iFormat, final int samples, final int width, final int height, final int name) { super(Type.COLOR, iFormat, samples, width, height, name); - } + } + @Override + public final boolean isTextureAttachment() { return false; } + @Override + public final TextureAttachment getTextureAttachment() { throw new GLException("Not a TextureAttachment, but ColorAttachment"); } + @Override + public final ColorAttachment getColorAttachment() { return this; } + } - - /** Texture attachment */ + + /** Texture FBO attachment */ public static class TextureAttachment extends Attachment implements Colorbuffer { /** details of the texture setup */ public final int dataFormat, dataType, magFilter, minFilter, wrapS, wrapT; @@ -442,8 +502,8 @@ public class FBObject { * @param wrapT * @param name */ - public TextureAttachment(Type type, int iFormat, int width, int height, int dataFormat, int dataType, - int magFilter, int minFilter, int wrapS, int wrapT, int name) { + public TextureAttachment(final Type type, final int iFormat, final int width, final int height, final int dataFormat, final int dataType, + final int magFilter, final int minFilter, final int wrapS, final int wrapT, final int name) { super(validateType(type), iFormat, width, height, name); this.dataFormat = dataFormat; this.dataType = dataType; @@ -452,37 +512,38 @@ public class FBObject { this.wrapS = wrapS; this.wrapT = wrapT; } - - private static Type validateType(Type type) { + + private static Type validateType(final Type type) { switch(type) { case COLOR_TEXTURE: case DEPTH_TEXTURE: case STENCIL_TEXTURE: return type; - default: + default: throw new IllegalArgumentException("Invalid type: "+type); } } - - /** + + /** * Initializes the texture and set it's parameter, if uninitialized, i.e. name is <code>zero</code>. - * @throws GLException if texture generation and setup fails. The just created texture name will be deleted in this case. + * @throws GLException if texture generation and setup fails. The just created texture name will be deleted in this case. */ @Override - public boolean initialize(GL gl) throws GLException { + public boolean initialize(final GL gl) throws GLException { final boolean init = 0 == getName(); if( init ) { - int glerr = checkPreGLError(gl); - - final int[] name = new int[] { -1 }; + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } + final int[] name = new int[] { -1 }; gl.glGenTextures(1, name, 0); if(0 == name[0]) { throw new GLException("null texture, "+this); } setName(name[0]); - + gl.glBindTexture(GL.GL_TEXTURE_2D, name[0]); - gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, format, getWidth(), getHeight(), 0, dataFormat, dataType, null); if( 0 < magFilter ) { gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, magFilter); } @@ -493,100 +554,184 @@ public class FBObject { gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, wrapS); } if( 0 < wrapT ) { - gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrapT); - } - glerr = gl.glGetError(); - if(GL.GL_NO_ERROR != glerr) { - gl.glDeleteTextures(1, name, 0); - setName(0); - throw new GLException("GL Error 0x"+Integer.toHexString(glerr)+" while creating "+this); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrapT); + } + if( checkError ) { + boolean preTexImage2D = true; + int glerr = gl.glGetError(); + if(GL.GL_NO_ERROR == glerr) { + preTexImage2D = false; + gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, format, getWidth(), getHeight(), 0, dataFormat, dataType, null); + glerr = gl.glGetError(); + } + if(GL.GL_NO_ERROR != glerr) { + gl.glDeleteTextures(1, name, 0); + setName(0); + throw new GLException("GL Error "+toHexString(glerr)+" while creating (pre TexImage2D "+preTexImage2D+") "+this); + } + } else { + gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, format, getWidth(), getHeight(), 0, dataFormat, dataType, null); } if(DEBUG) { - System.err.println("Attachment.init: "+this); + System.err.println("Attachment.init.X: "+this); } } return init; } @Override - public void free(GL gl) { + public void free(final GL gl) { final int[] name = new int[] { getName() }; if( 0 != name[0] ) { - gl.glDeleteTextures(1, name, 0); - setName(0); if(DEBUG) { - System.err.println("Attachment.free: "+this); + System.err.println("Attachment.free.0: "+this); } + gl.glDeleteTextures(1, name, 0); + setName(0); } } + + @Override + public final boolean isTextureAttachment() { return true; } + @Override + public final TextureAttachment getTextureAttachment() { return this; } + @Override + public final ColorAttachment getColorAttachment() { throw new GLException("Not a ColorAttachment, but TextureAttachment"); } + + @Override + public String toString() { + return getClass().getSimpleName()+"[type "+type+", target GL_TEXTURE_2D, level 0, format "+toHexString(format)+ + ", "+getWidth()+"x"+getHeight()+", border 0, dataFormat "+toHexString(dataFormat)+ + ", dataType "+toHexString(dataType)+ + "; min/mag "+toHexString(minFilter)+"/"+toHexString(magFilter)+ + ", wrap S/T "+toHexString(wrapS)+"/"+toHexString(wrapT)+ + "; name "+toHexString(getName())+", obj "+toHexString(objectHashCode())+"]"; + } + } + static String toHexString(final int v) { + return "0x"+Integer.toHexString(v); } - + /** - * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}, + * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}, * selecting the texture data type and format automatically. - * + * * <p>Using default min/mag filter {@link GL#GL_NEAREST} and default wrapS/wrapT {@link GL#GL_CLAMP_TO_EDGE}.</p> - * - * @param glp the chosen {@link GLProfile} + * + * @param gl the used {@link GLContext}'s {@link GL} object * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; - * @param width texture width + * @param width texture width * @param height texture height * @return the created and uninitialized color {@link TextureAttachment} */ - public static final TextureAttachment createColorTextureAttachment(GLProfile glp, boolean alpha, int width, int height) { - return createColorTextureAttachment(glp, alpha, width, height, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + public static final TextureAttachment createColorTextureAttachment(final GL gl, final boolean alpha, final int width, final int height) { + return createColorTextureAttachment(gl, alpha, width, height, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); } - + /** - * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}, + * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}, * selecting the texture data type and format automatically. - * - * @param glp the chosen {@link GLProfile} + * <p> + * For GLES3, sampling-sink format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}, + * see details below. Implementation aligns w/ {@link #createColorAttachment(boolean)} + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. + * </p> + * <p> + * ES3 BlitFramebuffer Requirements: OpenGL ES 3.0.2 p194: 4.3.2 Copying Pixels + * <pre> + * If SAMPLE_BUFFERS for the read framebuffer is greater than zero, no copy + * is performed and an INVALID_OPERATION error is generated if the formats of + * the read and draw framebuffers are not identical or if the source and destination + * rectangles are not defined with the same (X0, Y 0) and (X1, Y 1) bounds. + * </pre> + * Texture and Renderbuffer format details: + * <pre> + * ES2 Base iFormat: OpenGL ES 2.0.24 p66: 3.7.1 Texture Image Specification, Table 3.8 + * ALPHA, LUMINANCE, LUMINANCE_ALPHA, RGB, RGBA + * + * ES3 Base iFormat: OpenGL ES 3.0.2 p125: 3.8.3 Texture Image Specification, Table 3.11 + * ALPHA, LUMINANCE, LUMINANCE_ALPHA, RGB, RGBA + * DEPTH_COMPONENT, STENCIL_COMPONENT, RED, RG + * + * ES3 Required Texture and Renderbuffer iFormat: OpenGL ES 3.0.2 p126: 3.8.3 Texture Image Specification + * - RGBA32I, RGBA32UI, RGBA16I, RGBA16UI, RGBA8, RGBA8I, + * RGBA8UI, SRGB8_ALPHA8, RGB10_A2, RGB10_A2UI, RGBA4, and + * RGB5_A1. + * - RGB8 and RGB565. + * - RG32I, RG32UI, RG16I, RG16UI, RG8, RG8I, and RG8UI. + * - R32I, R32UI, R16I, R16UI, R8, R8I, and R8UI. + * </pre> + * </p> + * + * @param gl the used {@link GLContext}'s {@link GL} object * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; - * @param width texture width + * @param width texture width * @param height texture height * @param magFilter if > 0 value for {@link GL#GL_TEXTURE_MAG_FILTER} - * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} + * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} * @param wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} * @return the created and uninitialized color {@link TextureAttachment} */ - public static final TextureAttachment createColorTextureAttachment(GLProfile glp, boolean alpha, int width, int height, - int magFilter, int minFilter, int wrapS, int wrapT) { - final int textureInternalFormat, textureDataFormat, textureDataType; - if(glp.isGLES()) { - textureInternalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; - textureDataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; - textureDataType = GL.GL_UNSIGNED_BYTE; - } else { - textureInternalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; - textureDataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; - textureDataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; - } - return createColorTextureAttachment(textureInternalFormat, width, height, textureDataFormat, textureDataType, magFilter, minFilter, wrapS, wrapT); - } - + public static final TextureAttachment createColorTextureAttachment(final GL gl, final boolean alpha, final int width, final int height, + final int magFilter, final int minFilter, final int wrapS, final int wrapT) { + final int internalFormat, dataFormat, dataType; + if(gl.isGLES3()) { + internalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else if(gl.isGLES()) { + internalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else { + internalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; + // textureInternalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + // textureInternalFormat = alpha ? 4 : 3; + dataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; + dataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; + } + return createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT); + } + + public static final TextureAttachment createColorTextureAttachment(final GL gl, final int internalFormat, final int width, final int height, + final int magFilter, final int minFilter, final int wrapS, final int wrapT) { + final int dataFormat, dataType; + final boolean alpha = hasAlpha(internalFormat); + if( gl.isGLES3() ) { + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else if( gl.isGLES() ) { + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else { + dataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; + dataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; + } + return createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT); + } + /** - * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}. + * Creates a color {@link TextureAttachment}, i.e. type {@link Type#COLOR_TEXTURE}. * * @param internalFormat internalFormat parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} - * @param width texture width + * @param width texture width * @param height texture height * @param dataFormat format parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} * @param dataType type parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} * @param magFilter if > 0 value for {@link GL#GL_TEXTURE_MAG_FILTER} - * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} + * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} * @param wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} * @return the created and uninitialized color {@link TextureAttachment} */ - public static final TextureAttachment createColorTextureAttachment(int internalFormat, int width, int height, int dataFormat, int dataType, - int magFilter, int minFilter, int wrapS, int wrapT) { - return new TextureAttachment(Type.COLOR_TEXTURE, internalFormat, width, height, dataFormat, dataType, + public static final TextureAttachment createColorTextureAttachment(final int internalFormat, final int width, final int height, final int dataFormat, final int dataType, + final int magFilter, final int minFilter, final int wrapS, final int wrapT) { + return new TextureAttachment(Type.COLOR_TEXTURE, internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT, 0 /* name */); } - - private static boolean hasAlpha(int format) { + + private static boolean hasAlpha(final int format) { switch(format) { case GL.GL_RGBA8: case GL.GL_RGBA4: @@ -598,7 +743,7 @@ public class FBObject { return false; } } - + private boolean initialized; private boolean fullFBOSupport; private boolean rgba8Avail; @@ -608,7 +753,7 @@ public class FBObject { private boolean stencil04Avail; private boolean stencil08Avail; private boolean stencil16Avail; - private boolean packedDepthStencilAvail; + private boolean packedDepthStencilAvail; private int maxColorAttachments, maxSamples, maxTextureSize, maxRenderbufferSize; private int width, height, samples; @@ -617,70 +762,93 @@ public class FBObject { private int fbName; private boolean bound; - private int colorAttachmentCount; - private Colorbuffer[] colorAttachmentPoints; // colorbuffer attachment points + private int colorbufferCount; + private int textureAttachmentCount; + private Colorbuffer[] colorbufferAttachments; // colorbuffer attachment points private RenderAttachment depth, stencil; // depth and stencil maybe equal in case of packed-depth-stencil + private boolean modified; // size, sampleCount, or any attachment modified - private final FBObject samplesSink; // MSAA sink - private TextureAttachment samplesSinkTexture; - private boolean samplesSinkDirty; + private FBObject samplingSink; // MSAA sink + private Colorbuffer samplingColorSink; + private boolean samplingSinkDirty; // // ColorAttachment helper .. // - - private final void validateColorAttachmentPointRange(int point) { + + private final void validateColorAttachmentPointRange(final int point) { if(!initialized) { throw new GLException("FBO not initialized"); - } - if(maxColorAttachments != colorAttachmentPoints.length) { - throw new InternalError("maxColorAttachments "+maxColorAttachments+", array.lenght "+colorAttachmentPoints); + } + if(maxColorAttachments != colorbufferAttachments.length) { + throw new InternalError(String.format("maxColorAttachments %d, array.length %d", + maxColorAttachments, colorbufferAttachments.length) ); } if(0 > point || point >= maxColorAttachments) { - throw new IllegalArgumentException("attachment point out of range: "+point+", should be within [0.."+(maxColorAttachments-1)+"]"); + throw new IllegalArgumentException(String.format("attachment point out of range: %d, should be within [0..%d], %s", + point, maxColorAttachments-1, this.toString() ) ); } } - - private final void validateAddColorAttachment(int point, Colorbuffer ca) { + + private final void validateAddColorAttachment(final int point, final Colorbuffer ca) { validateColorAttachmentPointRange(point); - if( null != colorAttachmentPoints[point] ) { - throw new IllegalArgumentException("Cannot attach "+ca+", attachment point already in use by "+colorAttachmentPoints[point]); - } + if( null != colorbufferAttachments[point] ) { + throw new IllegalStateException(String.format("Cannot attach %s at %d, attachment point already in use by %s, %s", + ca.toString(), point, colorbufferAttachments[point].toString(), this.toString() ) ); + } } - - private final void addColorAttachment(int point, Colorbuffer ca) { - validateColorAttachmentPointRange(point); - final Colorbuffer c = colorAttachmentPoints[point]; - if( null != c && c != ca ) { - throw new IllegalArgumentException("Add failed: requested to add "+ca+" at "+point+", but slot is holding "+c+"; "+this); + + private final void addColorAttachment(final int point, final Colorbuffer ca, final boolean validate) { + final Colorbuffer c = colorbufferAttachments[point]; + if( validate ) { + validateColorAttachmentPointRange(point); + if( null == ca ) { + throw new IllegalArgumentException("Colorbuffer is null"); + } + if( null != c ) { + throw new IllegalStateException(String.format("Cannot attach %s at %d, attachment point already in use by %s, %s", + ca.toString(), point, c.toString(), this.toString() ) ); + } + } + colorbufferAttachments[point] = ca; + colorbufferCount++; + if( ca.isTextureAttachment() ) { + textureAttachmentCount++; } - colorAttachmentPoints[point] = ca; - colorAttachmentCount++; + modified = true; } - - private final void removeColorAttachment(int point, Colorbuffer ca) { + + private final void removeColorAttachment(final int point, final Colorbuffer ca) { validateColorAttachmentPointRange(point); - final Colorbuffer c = colorAttachmentPoints[point]; - if( null != c && c != ca ) { - throw new IllegalArgumentException("Remove failed: requested to removed "+ca+" at "+point+", but slot is holding "+c+"; "+this); + if( null == ca ) { + throw new IllegalArgumentException("Colorbuffer is null"); } - colorAttachmentPoints[point] = null; - colorAttachmentCount--; + final Colorbuffer c = colorbufferAttachments[point]; + if( c != ca ) { + throw new IllegalStateException(String.format("Cannot detach %s at %d, slot is holding other: %s, %s", + ca.toString(), point, c.toString(), this.toString() ) ); + } + colorbufferAttachments[point] = null; + colorbufferCount--; + if( ca.isTextureAttachment() ) { + textureAttachmentCount--; + } + modified = true; } - + /** * Return the {@link Colorbuffer} attachment at <code>attachmentPoint</code> if it is attached to this FBO, otherwise null. - * + * * @see #attachColorbuffer(GL, boolean) * @see #attachColorbuffer(GL, boolean) * @see #attachTexture2D(GL, int, boolean, int, int, int, int) - * @see #attachTexture2D(GL, int, int, int, int, int, int, int, int) + * @see #attachTexture2D(GL, int, int, int, int, int, int, int, int) */ - public final Colorbuffer getColorbuffer(int attachmentPoint) { - validateColorAttachmentPointRange(attachmentPoint); - return colorAttachmentPoints[attachmentPoint]; + public final Colorbuffer getColorbuffer(final int attachmentPoint) { + validateColorAttachmentPointRange(attachmentPoint); + return colorbufferAttachments[attachmentPoint]; } - + /** * Finds the passed {@link Colorbuffer} within the valid range of attachment points * using <i>reference</i> comparison only. @@ -688,36 +856,56 @@ public class FBObject { * Note: Slow. Implementation uses a logN array search to save resources, i.e. not using a HashMap. * </p> * @param ca the {@link Colorbuffer} to look for. - * @return -1 if the {@link Colorbuffer} could not be found, otherwise [0..{@link #getMaxColorAttachments()}-1] + * @return -1 if the {@link Colorbuffer} could not be found, otherwise [0..{@link #getMaxColorAttachments()}-1] */ - public final int getColorbufferAttachmentPoint(Colorbuffer ca) { - for(int i=0; i<colorAttachmentPoints.length; i++) { - if( colorAttachmentPoints[i] == ca ) { - return i; + public final int getColorbufferAttachmentPoint(final Colorbuffer ca) { + for(int i=0; i<colorbufferAttachments.length; i++) { + if( colorbufferAttachments[i] == ca ) { + return i; } } return -1; } - + /** * Returns the passed {@link Colorbuffer} if it is attached to this FBO, otherwise null. * Implementation compares the <i>reference</i> only. - * + * * <p> * Note: Slow. Uses {@link #getColorbufferAttachmentPoint(Colorbuffer)} to determine it's attachment point * to be used for {@link #getColorbuffer(int)} * </p> - * + * * @see #attachColorbuffer(GL, boolean) * @see #attachColorbuffer(GL, boolean) * @see #attachTexture2D(GL, int, boolean, int, int, int, int) - * @see #attachTexture2D(GL, int, int, int, int, int, int, int, int) + * @see #attachTexture2D(GL, int, int, int, int, int, int, int, int) */ - public final Colorbuffer getColorbuffer(Colorbuffer ca) { + public final Colorbuffer getColorbuffer(final Colorbuffer ca) { final int p = getColorbufferAttachmentPoint(ca); return p>=0 ? getColorbuffer(p) : null; } - + + /** + * Returns true if any attached {@link Colorbuffer} uses alpha, + * otherwise false. + */ + public final boolean hasAttachmentUsingAlpha() { + final int caCount = getColorbufferCount(); + boolean hasAlpha = false; + for(int i=0; i<caCount; i++) { + final Attachment ca = (Attachment)getColorbuffer(i); + if( null == ca ) { + break; + } + if( hasAlpha(ca.format) ) { + hasAlpha = true; + break; + } + } + return hasAlpha; + } + /** * Creates an uninitialized FBObject instance. * <p> @@ -725,11 +913,8 @@ public class FBObject { * </p> */ public FBObject() { - this(false); - } - /* pp */ FBObject(boolean isSampleSink) { this.initialized = false; - + // TBD @ init this.fullFBOSupport = false; this.rgba8Avail = false; @@ -744,7 +929,7 @@ public class FBObject { this.maxSamples=-1; this.maxTextureSize = 0; this.maxRenderbufferSize = 0; - + this.width = 0; this.height = 0; this.samples = 0; @@ -752,86 +937,98 @@ public class FBObject { this.ignoreStatus = false; this.fbName = 0; this.bound = false; - - this.colorAttachmentPoints = null; // at init .. - this.colorAttachmentCount = 0; + + this.colorbufferAttachments = null; // at init .. + this.colorbufferCount = 0; + this.textureAttachmentCount = 0; this.depth = null; - this.stencil = null; - - this.samplesSink = isSampleSink ? null : new FBObject(true); - this.samplesSinkTexture = null; - this.samplesSinkDirty = true; + this.stencil = null; + this.modified = true; + + this.samplingSink = null; + this.samplingColorSink = null; + this.samplingSinkDirty = true; } - - private void init(GL gl, int width, int height, int samples) throws GLException { - if(initialized) { - throw new GLException("FBO already initialized"); + + /** + * Initializes this FBO's instance. + * <p> + * The sampling sink is not initializes, allowing manual assignment via {@link #setSamplingSink(FBObject)} + * if {@code newSamples > 0}. + * </p> + * + * <p>Leaves the FBO bound</p> + * + * @param gl the current GL context + * @param newWidth the initial width, it's minimum is capped to 1 + * @param newHeight the initial height, it's minimum is capped to 1 + * @param newSamples if > 0, MSAA will be used, otherwise no multisampling. Will be capped to {@link #getMaxSamples()}. + * @throws IllegalStateException if already initialized + * @throws GLException in case of an error, i.e. size too big, etc .. + */ + public void init(final GL gl, final int newWidth, final int newHeight, final int newSamples) throws IllegalStateException, GLException { + if( initialized ) { + throw new IllegalStateException("FBO already initialized"); } if( !gl.hasBasicFBOSupport() ) { throw new GLException("FBO not supported w/ context: "+gl.getContext()+", "+this); } - fullFBOSupport = gl.hasFullFBOSupport(); - - rgba8Avail = gl.isGL2GL3() || gl.isExtensionAvailable(GLExtensions.OES_rgb8_rgba8); + fullFBOSupport = gl.hasFullFBOSupport(); + + rgba8Avail = gl.isGL2ES3() || gl.isExtensionAvailable(GLExtensions.OES_rgb8_rgba8); depth24Avail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_depth24); depth32Avail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_depth32); stencil01Avail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_stencil1); stencil04Avail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_stencil4); stencil08Avail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_stencil8); stencil16Avail = fullFBOSupport; - - packedDepthStencilAvail = fullFBOSupport || gl.isExtensionAvailable(GLExtensions.OES_packed_depth_stencil); - + + packedDepthStencilAvail = fullFBOSupport || + gl.isExtensionAvailable(GLExtensions.OES_packed_depth_stencil) || + gl.isExtensionAvailable(GLExtensions.EXT_packed_depth_stencil) ; + final boolean NV_fbo_color_attachments = gl.isExtensionAvailable(GLExtensions.NV_fbo_color_attachments); - - int val[] = new int[1]; - - int glerr = checkPreGLError(gl); + + final int val[] = new int[1]; + + checkPreGLError(gl); int realMaxColorAttachments = 1; maxColorAttachments = 1; - if( null != samplesSink && fullFBOSupport || NV_fbo_color_attachments ) { + if( fullFBOSupport || NV_fbo_color_attachments ) { try { - gl.glGetIntegerv(GL2GL3.GL_MAX_COLOR_ATTACHMENTS, val, 0); - glerr = gl.glGetError(); - if(GL.GL_NO_ERROR == glerr) { - realMaxColorAttachments = 1 <= val[0] ? val[0] : 1; // cap minimum to 1 - } else if(DEBUG) { - System.err.println("FBObject.init-GL_MAX_COLOR_ATTACHMENTS query GL Error 0x"+Integer.toHexString(glerr)); - } - } catch (GLException gle) {} + val[0] = 0; + gl.glGetIntegerv(GL2ES2.GL_MAX_COLOR_ATTACHMENTS, val, 0); + realMaxColorAttachments = 1 <= val[0] ? val[0] : 1; // cap minimum to 1 + } catch (final GLException gle) { gle.printStackTrace(); } } maxColorAttachments = realMaxColorAttachments <= 8 ? realMaxColorAttachments : 8; // cap to limit array size - - colorAttachmentPoints = new Colorbuffer[maxColorAttachments]; - colorAttachmentCount = 0; - - maxSamples = gl.getMaxRenderbufferSamples(); - if(!forceMinimumFBOSupport) { - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, val, 0); - maxTextureSize = val[0]; - gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, val, 0); - maxRenderbufferSize = val[0]; + + colorbufferAttachments = new Colorbuffer[maxColorAttachments]; + colorbufferCount = 0; + textureAttachmentCount = 0; + + maxSamples = gl.getMaxRenderbufferSamples(); // if > 0 implies fullFBOSupport + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, val, 0); + final int _maxTextureSize = val[0]; + if( 0 < USER_MAX_TEXTURE_SIZE ) { + maxTextureSize = USER_MAX_TEXTURE_SIZE; } else { - maxTextureSize = 2048; - maxRenderbufferSize = 2048; + maxTextureSize = _maxTextureSize; } - - glerr = gl.glGetError(); - if(DEBUG && GL.GL_NO_ERROR != glerr) { - System.err.println("Info: FBObject.init: pre-existing GL error 0x"+Integer.toHexString(glerr)); - } - - this.width = width; - this.height = height; - this.samples = samples <= maxSamples ? samples : maxSamples; - + gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, val, 0); + maxRenderbufferSize = val[0]; + + this.width = 0 < newWidth ? newWidth : 1; + this.height = 0 < newHeight ? newHeight : 1; + this.samples = newSamples <= maxSamples ? newSamples : maxSamples; + if(DEBUG) { - System.err.println("FBObject "+width+"x"+height+", "+samples+" -> "+this.samples+" samples"); + System.err.println("FBObject.init() START: "+width+"x"+height+", "+newSamples+" -> "+this.samples+" samples"); System.err.println("fullFBOSupport: "+fullFBOSupport); System.err.println("maxColorAttachments: "+maxColorAttachments+"/"+realMaxColorAttachments+" [capped/real]"); System.err.println("maxSamples: "+maxSamples); - System.err.println("maxTextureSize: "+maxTextureSize); + System.err.println("maxTextureSize: "+_maxTextureSize+" -> "+maxTextureSize); System.err.println("maxRenderbufferSize: "+maxRenderbufferSize); System.err.println("rgba8: "+rgba8Avail); System.err.println("depth24: "+depth24Avail); @@ -843,19 +1040,18 @@ public class FBObject { System.err.println("packedDepthStencil: "+packedDepthStencilAvail); System.err.println("NV_fbo_color_attachments: "+NV_fbo_color_attachments); System.err.println(gl.getContext().getGLVersion()); - System.err.println(JoglVersion.getGLStrings(gl, null).toString()); - System.err.println(gl.getContext()); + System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); } - - checkNoError(null, gl.glGetError(), "FBObject Init.pre"); // throws GLException if error - - if(width > 2 + maxTextureSize || height> 2 + maxTextureSize || - width > maxRenderbufferSize || height> maxRenderbufferSize ) { - throw new GLException("size "+width+"x"+height+" exceeds on of the maxima [texture "+maxTextureSize+", renderbuffer "+maxRenderbufferSize+"]"); + + checkPreGLError(gl); + + if( width > maxRenderbufferSize || height > maxRenderbufferSize ) { + throw new GLException("Size "+width+"x"+height+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); } - resetMSAATexture2DSink(gl); - + modified = true; + samplingSinkDirty = true; + // generate fbo .. gl.glGenFramebuffers(1, val, 0); fbName = val[0]; @@ -864,125 +1060,154 @@ public class FBObject { } // bind fbo .. - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbName); - checkNoError(gl, gl.glGetError(), "FBObject Init.bindFB"); // throws GLException if error + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbName); + checkNoError(gl, gl.glGetError(), "FBObject Init.bindFB"); // throws GLException if error if(!gl.glIsFramebuffer(fbName)) { checkNoError(gl, GL.GL_INVALID_VALUE, "FBObject Init.isFB"); // throws GLException } bound = true; - samplesSinkDirty = true; initialized = true; - - updateStatus(gl); + + vStatus = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // always incomplete w/o attachments! if(DEBUG) { - System.err.println("FBObject.init(): "+this); + System.err.println("FBObject.init() END: "+this); + Thread.dumpStack(); } } /** - * Initializes or resets this FBO's instance. + * Resets this FBO's instance. * <p> * In case the new parameters are compatible with the current ones - * no action will be performed. Otherwise all attachments will be recreated + * no action will be performed and method returns immediately.<br> + * Otherwise all attachments will be recreated * to match the new given parameters. * </p> * <p> - * Incompatibility and hence recreation is forced if - * the size or sample count doesn't match for subsequent calls. - * </p> - * - * <p>Leaves the FBO bound state untouched</p> - * - * @param gl the current GL context - * @param newWidth - * @param newHeight - * @throws GLException in case of an error - */ - public final void reset(GL gl, int newWidth, int newHeight) { - reset(gl, newWidth, newHeight, 0); - } - - /** - * Initializes or resets this FBO's instance. - * <p> - * In case the new parameters are compatible with the current ones - * no action will be performed. Otherwise all attachments will be recreated - * to match the new given parameters. + * {@link #resetSamplingSink(GL)} is being issued immediately + * to match the new configuration. * </p> - * <p> - * Currently incompatibility and hence recreation of the attachments will be performed - * if the size or sample count doesn't match for subsequent calls. - * </p> - * + * * <p>Leaves the FBO bound state untouched</p> - * + * * @param gl the current GL context * @param newWidth the new width, it's minimum is capped to 1 * @param newHeight the new height, it's minimum is capped to 1 * @param newSamples if > 0, MSAA will be used, otherwise no multisampling. Will be capped to {@link #getMaxSamples()}. + * @return {@code true} if this instance has been modified, otherwise {@code false}. + * @throws IllegalStateException if not initialized via {@link #init(GL, int, int, int)}. * @throws GLException in case of an error, i.e. size too big, etc .. */ - public final void reset(GL gl, int newWidth, int newHeight, int newSamples) { - if(!initialized) { - init(gl, newWidth, newHeight, newSamples); - return; + public final boolean reset(final GL gl, int newWidth, int newHeight, int newSamples) throws GLException, IllegalStateException { + if( !initialized ) { + throw new IllegalStateException("FBO not initialized"); } - + newSamples = newSamples <= maxSamples ? newSamples : maxSamples; // clamp - + if( newWidth != width || newHeight != height || newSamples != samples ) { - if(0>=newWidth) { newWidth = 1; } - if(0>=newHeight) { newHeight = 1; } - if(newWidth > 2 + maxTextureSize || newHeight> 2 + maxTextureSize || - newWidth > maxRenderbufferSize || newHeight> maxRenderbufferSize ) { - throw new GLException("size "+width+"x"+height+" exceeds on of the maxima [texture "+maxTextureSize+", renderbuffer "+maxRenderbufferSize+"]"); + if( 0 >= newWidth ) { newWidth = 1; } + if( 0 >= newHeight ) { newHeight = 1; } + if( textureAttachmentCount > 0 && ( newWidth > 2 + maxTextureSize || newHeight > 2 + maxTextureSize ) ) { + throw new GLException("Size "+newWidth+"x"+newHeight+" exceeds on of the maximum texture size "+maxTextureSize+": \n\t"+this); } - + if( newWidth > maxRenderbufferSize || newHeight > maxRenderbufferSize ) { + throw new GLException("Size "+newWidth+"x"+newHeight+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); + } + if(DEBUG) { System.err.println("FBObject.reset - START - "+width+"x"+height+", "+samples+" -> "+newWidth+"x"+newHeight+", "+newSamples+"; "+this); - } - + } + final boolean wasBound = isBound(); - + + final int sampleCountChange; + if( 0 < samples && 0 < newSamples || 0 == samples && 0 == newSamples ) { + sampleCountChange = 0; // keep MSAA settings + } else if( 0 == samples && 0 < newSamples ) { + sampleCountChange = 1; // add MSAA + } else if( 0 < samples && 0 == newSamples ) { + sampleCountChange = -1; // remove MSAA + } else { + throw new IllegalArgumentException("Error in sampleCount change: "+samples+" -> "+newSamples); + } width = newWidth; height = newHeight; samples = newSamples; - detachAllImpl(gl, true , true); - - /** - * Postpone reset of samplesSink until syncFramebuffer, - * issued at use(..) method (swapBuffer usually initiates it). - * This allows another thread to still use the 'samplesSinkTexture' - * until swapBuffer happens and does not invalidate the GL_FRONT - * FBO (framebuffer & texture). - resetMSAATexture2DSink(gl); - */ - samplesSinkDirty = true; + + modified = true; + samplingSinkDirty = true; + + detachAllImpl(gl, true, true, sampleCountChange); + resetSamplingSink(gl); if(!wasBound) { unbind(gl); } - + if(DEBUG) { - System.err.println("FBObject.reset - END - "+this); + System.err.println("FBObject.reset - END - wasBound, "+wasBound+", "+this); } - } + return true; + } else { + return false; + } } - + + /** + * Simply resets this instance's size only, w/o validation. + * + * <p>Leaves the FBO bound</p> + * + * @param gl the current GL context + * @param newWidth the new width, it's minimum is capped to 1 + * @param newHeight the new height, it's minimum is capped to 1 + */ + private final void resetSizeImpl(final GL gl, final int newWidth, final int newHeight) { + if(DEBUG) { + System.err.println("FBObject.resetSize - START - "+width+"x"+height+", "+samples+" -> "+newWidth+"x"+newHeight); + } + + final int sampleCountChange = 0; // keep MSAA settings + width = newWidth; + height = newHeight; + + modified = true; + samplingSinkDirty = true; + + detachAllImpl(gl, true, true, sampleCountChange); + + if(DEBUG) { + System.err.println("FBObject.resetSize - END - "+this); + } + } + + private void validateAttachmentSize(final Attachment a) { + final int aWidth = a.getWidth(); + final int aHeight = a.getHeight(); + + if( a instanceof TextureAttachment && ( aWidth > 2 + maxTextureSize || aHeight > 2 + maxTextureSize ) ) { + throw new GLException("Size "+aWidth+"x"+aHeight+" of "+a+" exceeds on of the maximum texture size "+maxTextureSize+": \n\t"+this); + } + if( aWidth > maxRenderbufferSize || aHeight > maxRenderbufferSize ) { + throw new GLException("Size "+aWidth+"x"+aHeight+" of "+a+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); + } + } + /** * Writes the internal format of the attachments to the given GLCapabilities object. * @param caps the destination for format bits */ - public final void formatToGLCapabilities(GLCapabilities caps) { + public final void formatToGLCapabilities(final GLCapabilities caps) { caps.setSampleBuffers(samples > 0); caps.setNumSamples(samples); caps.setDepthBits(0); caps.setStencilBits(0); - + final Colorbuffer cb = samples > 0 ? getSamplingSink() : getColorbuffer(0); if(null != cb) { cb.formatToGLCapabilities(caps, rgba8Avail); - } + } if(null != depth) { depth.formatToGLCapabilities(caps, rgba8Avail); } @@ -990,11 +1215,11 @@ public class FBObject { stencil.formatToGLCapabilities(caps, rgba8Avail); } } - - /** + + /** * Note that the status may reflect an incomplete state during transition of attachments. * @return The FB status. {@link GL.GL_FRAMEBUFFER_COMPLETE} if ok, otherwise return GL FBO error state or -1 - * @see #validateStatus() + * @see #validateStatus() */ public final int getStatus() { return vStatus; @@ -1004,44 +1229,44 @@ public class FBObject { public final String getStatusString() { return getStatusString(vStatus); } - - public static final String getStatusString(int fbStatus) { + + public static final String getStatusString(final int fbStatus) { switch(fbStatus) { case -1: return "NOT A FBO"; - + case GL.GL_FRAMEBUFFER_COMPLETE: return "OK"; - + case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - return("GL FBO: incomplete, incomplete attachment\n"); + return("FBO incomplete attachment\n"); case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - return("GL FBO: incomplete, missing attachment"); + return("FBO missing attachment"); case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - return("GL FBO: incomplete, attached images must have same dimensions"); + return("FBO attached images must have same dimensions"); case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - return("GL FBO: incomplete, attached images must have same format"); + return("FBO attached images must have same format"); case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - return("GL FBO: incomplete, missing draw buffer"); + return("FBO missing draw buffer"); case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - return("GL FBO: incomplete, missing read buffer"); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - return("GL FBO: incomplete, missing multisample buffer"); - case GL3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: - return("GL FBO: incomplete, layer targets"); - + return("FBO missing read buffer"); + case GL2ES3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return("FBO missing multisample buffer"); + case GL3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + return("FBO missing layer targets"); + case GL.GL_FRAMEBUFFER_UNSUPPORTED: - return("GL FBO: Unsupported framebuffer format"); - case GL2GL3.GL_FRAMEBUFFER_UNDEFINED: - return("GL FBO: framebuffer undefined"); - + return("Unsupported FBO format"); + case GL2ES3.GL_FRAMEBUFFER_UNDEFINED: + return("FBO undefined"); + case 0: - return("GL FBO: incomplete, implementation fault"); + return("FBO implementation fault"); default: - return("GL FBO: incomplete, implementation ERROR 0x"+Integer.toHexString(fbStatus)); + return("FBO incomplete, implementation ERROR "+toHexString(fbStatus)); } } - + /** * The status may even be valid if incomplete during transition of attachments. * @see #getStatus() @@ -1050,47 +1275,49 @@ public class FBObject { switch(vStatus) { case GL.GL_FRAMEBUFFER_COMPLETE: return true; - + case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + case GL2ES3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: case GL3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: - if(0 == colorAttachmentCount || null == depth) { + if(0 == colorbufferCount || null == depth) { // we are in transition return true; } - + case GL.GL_FRAMEBUFFER_UNSUPPORTED: - case GL2GL3.GL_FRAMEBUFFER_UNDEFINED: - - case 0: + case GL2ES3.GL_FRAMEBUFFER_UNDEFINED: + + case 0: default: - System.out.println("Framebuffer " + fbName + " is incomplete: status = 0x" + Integer.toHexString(vStatus) + - " : " + getStatusString(vStatus)); + if(DEBUG) { + System.err.println("Framebuffer " + fbName + " is incomplete, status = " + toHexString(vStatus) + + " : " + getStatusString(vStatus)); + } return false; } } - - private static int checkPreGLError(GL gl) { - int glerr = gl.glGetError(); + + private static int checkPreGLError(final GL gl) { + final int glerr = gl.glGetError(); if(DEBUG && GL.GL_NO_ERROR != glerr) { - System.err.println("Pre-existing GL error: 0x"+Integer.toHexString(glerr)); + System.err.println("Pre-existing GL error: "+toHexString(glerr)); Thread.dumpStack(); } - return glerr; + return glerr; } - - private final boolean checkNoError(GL gl, int err, String exceptionMessage) throws GLException { + + private final boolean checkNoError(final GL gl, final int err, final String exceptionMessage) throws GLException { if(GL.GL_NO_ERROR != err) { if(null != gl) { destroy(gl); } if(null != exceptionMessage) { - throw new GLException(exceptionMessage+" GL Error 0x"+Integer.toHexString(err)); + throw new GLException(exceptionMessage+" GL Error "+toHexString(err)+" of "+this.toString()); } return false; } @@ -1100,17 +1327,17 @@ public class FBObject { private final void checkInitialized() throws GLException { if(!initialized) { throw new GLException("FBO not initialized, call init(GL) first."); - } + } } - + /** * Attaches a {@link Colorbuffer}, i.e. {@link TextureAttachment}, to this FBO's instance at the given attachment point, * selecting the texture data type and format automatically. - * + * * <p>Using default min/mag filter {@link GL#GL_NEAREST} and default wrapS/wrapT {@link GL#GL_CLAMP_TO_EDGE}.</p> - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; @@ -1118,80 +1345,108 @@ public class FBObject { * @throws GLException in case the texture colorbuffer couldn't be allocated or MSAA has been chosen * @see #createColorTextureAttachment(GLProfile, boolean, int, int) */ - public final TextureAttachment attachTexture2D(GL gl, int attachmentPoint, boolean alpha) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(gl.getGLProfile(), alpha, width, height)); + public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final boolean alpha) throws GLException { + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(gl, alpha, width, height)).getTextureAttachment(); } - + /** * Attaches a {@link Colorbuffer}, i.e. {@link TextureAttachment}, to this FBO's instance at the given attachment point, * selecting the texture data type and format automatically. - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; * @param magFilter if > 0 value for {@link GL#GL_TEXTURE_MAG_FILTER} - * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} + * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} * @param wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} * @return TextureAttachment instance describing the new attached texture colorbuffer if bound and configured successfully, otherwise GLException is thrown * @throws GLException in case the texture colorbuffer couldn't be allocated or MSAA has been chosen * @see #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int) */ - public final TextureAttachment attachTexture2D(GL gl, int attachmentPoint, boolean alpha, int magFilter, int minFilter, int wrapS, int wrapT) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(gl.getGLProfile(), alpha, width, height, magFilter, minFilter, wrapS, wrapT)); + public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final boolean alpha, final int magFilter, final int minFilter, final int wrapS, final int wrapT) throws GLException { + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(gl, alpha, width, height, magFilter, minFilter, wrapS, wrapT)).getTextureAttachment(); } - + /** * Attaches a {@link Colorbuffer}, i.e. {@link TextureAttachment}, to this FBO's instance at the given attachment point. - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] * @param internalFormat internalFormat parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} * @param dataFormat format parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} * @param dataType type parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} * @param magFilter if > 0 value for {@link GL#GL_TEXTURE_MAG_FILTER} - * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} + * @param minFilter if > 0 value for {@link GL#GL_TEXTURE_MIN_FILTER} * @param wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} * @return TextureAttachment instance describing the new attached texture colorbuffer if bound and configured successfully, otherwise GLException is thrown * @throws GLException in case the texture colorbuffer couldn't be allocated or MSAA has been chosen * @see #createColorTextureAttachment(int, int, int, int, int, int, int, int, int) */ - public final TextureAttachment attachTexture2D(GL gl, int attachmentPoint, - int internalFormat, int dataFormat, int dataType, - int magFilter, int minFilter, int wrapS, int wrapT) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT)); + public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, + final int internalFormat, final int dataFormat, final int dataType, + final int magFilter, final int minFilter, final int wrapS, final int wrapT) throws GLException { + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT)).getTextureAttachment(); } - + /** * Creates a {@link ColorAttachment}, selecting the format automatically. - * + * <p> + * For GLES3, sampling-sink {@link Colorbuffer} format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. + * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. + * </p> + * * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; * @return uninitialized ColorAttachment instance describing the new attached colorbuffer */ - public final ColorAttachment createColorAttachment(boolean alpha) { + public final ColorAttachment createColorAttachment(final boolean alpha) { final int internalFormat; + if( rgba8Avail ) { internalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8 ; } else { internalFormat = alpha ? GL.GL_RGBA4 : GL.GL_RGB565; } - return new ColorAttachment(internalFormat, samples, width, height, 0); + return createColorAttachment(internalFormat, samples, width, height); } - + /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, to this FBO's instance at the given attachment point, - * selecting the format automatically. - * + * Creates a {@link ColorAttachment}, selecting the format automatically. + * <p> + * For GLES3, sampling-sink {@link Colorbuffer} format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. + * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. + * </p> + * + * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; + * @return uninitialized ColorAttachment instance describing the new attached colorbuffer + */ + public static final ColorAttachment createColorAttachment(final int internalFormat, final int samples, final int width, final int height) { + return new ColorAttachment(internalFormat, samples, width, height, 0 /* name not yet determined */); + } + + public static final RenderAttachment createRenderAttachment(final Type type, final int internalFormat, final int samples, final int width, final int height) { + return new RenderAttachment(type, internalFormat, samples, width, height, 0 /* name not yet determined */); + } + + /** + * Attaches a newly created and {@link Colorbuffer#initialize(GL) initialized} {@link Colorbuffer}, i.e. a {@link ColorAttachment}, + * at the given attachment point. + * <p> + * The {@link ColorAttachment} is created using {@code alpha} if {@code true} and current {@code sample count} and {@code size}. + * </p> + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; @@ -1199,89 +1454,98 @@ public class FBObject { * @throws GLException in case the colorbuffer couldn't be allocated * @see #createColorAttachment(boolean) */ - public final ColorAttachment attachColorbuffer(GL gl, int attachmentPoint, boolean alpha) throws GLException { - return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, createColorAttachment(alpha)); + public final ColorAttachment attachColorbuffer(final GL gl, final int attachmentPoint, final boolean alpha) throws GLException { + return attachColorbuffer(gl, attachmentPoint, createColorAttachment(alpha)).getColorAttachment(); } - + /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, to this FBO's instance at the given attachment point. - * + * Attaches a newly created and {@link Colorbuffer#initialize(GL) initialized} {@link Colorbuffer}, i.e. a {@link ColorAttachment}, + * at the given attachment point. + * <p> + * The {@link ColorAttachment} is created using the given {@code internalFormat} and current {@code sample count} and {@code size}. + * </p> + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] - * @param internalFormat usually {@link GL#GL_RGBA4}, {@link GL#GL_RGB5_A1}, {@link GL#GL_RGB565}, {@link GL#GL_RGB8} or {@link GL#GL_RGBA8} + * @param internalFormat usually {@link GL#GL_RGBA4}, {@link GL#GL_RGB5_A1}, {@link GL#GL_RGB565}, {@link GL#GL_RGB8} or {@link GL#GL_RGBA8} * @return ColorAttachment instance describing the new attached colorbuffer if bound and configured successfully, otherwise GLException is thrown * @throws GLException in case the colorbuffer couldn't be allocated * @throws IllegalArgumentException if <code>internalFormat</code> doesn't reflect a colorbuffer */ - public final ColorAttachment attachColorbuffer(GL gl, int attachmentPoint, int internalFormat) throws GLException, IllegalArgumentException { + public final ColorAttachment attachColorbuffer(final GL gl, final int attachmentPoint, final int internalFormat) throws GLException, IllegalArgumentException { final Attachment.Type atype = Attachment.Type.determine(internalFormat); if( Attachment.Type.COLOR != atype ) { - throw new IllegalArgumentException("colorformat invalid: 0x"+Integer.toHexString(internalFormat)+", "+this); + throw new IllegalArgumentException("colorformat invalid: "+toHexString(internalFormat)+", "+this); } - - return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, new ColorAttachment(internalFormat, samples, width, height, 0)); + + return attachColorbuffer(gl, attachmentPoint, createColorAttachment(internalFormat, samples, width, height)).getColorAttachment(); } - + /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment} or {@link TextureAttachment}, - * to this FBO's instance at the given attachment point. - * + * Attaches a {@link Colorbuffer} at the given attachment point + * and {@link Colorbuffer#initialize(GL) initializes} it, if not done yet. + * <p> + * {@link Colorbuffer} may be a {@link ColorAttachment} or {@link TextureAttachment}. + * </p> * <p> * If {@link Colorbuffer} is a {@link TextureAttachment} and is uninitialized, i.e. it's texture name is <code>zero</code>, * a new texture name is generated and setup w/ the texture parameter.<br/> * Otherwise, i.e. texture name is not <code>zero</code>, the passed TextureAttachment <code>texA</code> is - * considered complete and assumed matching this FBO requirement. A GL error may occur is the latter is untrue. + * considered complete and assumed matching this FBO requirement. A GL error may occur is the latter is untrue. * </p> - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] - * @param colbuf the to be attached {@link Colorbuffer} - * @return newly attached {@link Colorbuffer} instance if bound and configured successfully, otherwise GLException is thrown - * @throws GLException in case the colorbuffer couldn't be allocated or MSAA has been chosen in case of a {@link TextureAttachment} + * @param colbuf the to be attached {@link Colorbuffer} + * @return given {@link Colorbuffer} instance if bound and configured successfully, otherwise GLException is thrown + * @throws GLException in case the colorbuffer couldn't be allocated or MSAA has been chosen in case of a {@link TextureAttachment} */ - public final Colorbuffer attachColorbuffer(GL gl, int attachmentPoint, Colorbuffer colbuf) throws GLException { + public final Colorbuffer attachColorbuffer(final GL gl, final int attachmentPoint, final Colorbuffer colbuf) throws GLException { + bind(gl); + return attachColorbufferImpl(gl, attachmentPoint, colbuf); + } + + private final Colorbuffer attachColorbufferImpl(final GL gl, final int attachmentPoint, final Colorbuffer colbuf) throws GLException { validateAddColorAttachment(attachmentPoint, colbuf); - + validateAttachmentSize((Attachment)colbuf); + final boolean initializedColorbuf = colbuf.initialize(gl); - addColorAttachment(attachmentPoint, colbuf); - - bind(gl); + addColorAttachment(attachmentPoint, colbuf, false); - if(colbuf instanceof TextureAttachment) { - final TextureAttachment texA = (TextureAttachment) colbuf; - - if(samples>0) { + if( colbuf.isTextureAttachment() ) { + final TextureAttachment texA = colbuf.getTextureAttachment(); + if( samples > 0 ) { removeColorAttachment(attachmentPoint, texA); if(initializedColorbuf) { texA.free(gl); } throw new GLException("Texture2D not supported w/ MSAA. If you have enabled MSAA with exisiting texture attachments, you may want to detach them via detachAllTexturebuffer(gl)."); } - + // Set up the color buffer for use as a renderable texture: gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, GL.GL_TEXTURE_2D, texA.getName(), 0); - + if(!ignoreStatus) { - updateStatus(gl); + updateStatus(gl); if(!isStatusValid()) { detachColorbuffer(gl, attachmentPoint, true); throw new GLException("attachTexture2D "+texA+" at "+attachmentPoint+" failed "+getStatusString()+", "+this); } } - } else if(colbuf instanceof ColorAttachment) { - final ColorAttachment colA = (ColorAttachment) colbuf; - + } else { + final ColorAttachment colA = colbuf.getColorAttachment(); + // Attach the color buffer - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, - GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, GL.GL_RENDERBUFFER, colA.getName()); - + if(!ignoreStatus) { updateStatus(gl); if(!isStatusValid()) { @@ -1291,11 +1555,73 @@ public class FBObject { } } if(DEBUG) { - System.err.println("FBObject.attachColorbuffer: [attachmentPoint "+attachmentPoint+", colbuf "+colbuf+"]: "+this); + System.err.println("FBObject.attachColorbuffer.X: [attachmentPoint "+attachmentPoint+", colbuf "+colbuf+"]: "+this); } return colbuf; } - + + private final int getDepthIFormat(final int reqBits) { + if( 32 <= reqBits && depth32Avail ) { + return GL.GL_DEPTH_COMPONENT32; + } else if( 24 <= reqBits && ( depth24Avail || depth32Avail ) ) { + if( depth24Avail ) { + return GL.GL_DEPTH_COMPONENT24; + } else { + return GL.GL_DEPTH_COMPONENT32; + } + } else { + return GL.GL_DEPTH_COMPONENT16; + } + } + private final int getStencilIFormat(final int reqBits) { + if( 16 <= reqBits && stencil16Avail ) { + return GL2GL3.GL_STENCIL_INDEX16; + } else if( 8 <= reqBits && ( stencil08Avail || stencil16Avail ) ) { + if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else if( 4 <= reqBits && ( stencil04Avail || stencil08Avail || stencil16Avail ) ) { + if( stencil04Avail ) { + return GL.GL_STENCIL_INDEX4; + } else if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else if( 1 <= reqBits && ( stencil01Avail || stencil04Avail || stencil08Avail || stencil16Avail ) ) { + if( stencil01Avail ) { + return GL.GL_STENCIL_INDEX1; + } else if( stencil04Avail ) { + return GL.GL_STENCIL_INDEX4; + } else if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else { + throw new GLException("stencil buffer n/a"); + } + } + + /** Request default bit count for depth- or stencil buffer (depth 24 bits, stencil 8 bits), value {@value} */ + public static final int DEFAULT_BITS = 0; + + /** + * Request current context drawable's <i>requested</i> + * {@link GLCapabilitiesImmutable#getDepthBits() depth-} or {@link GLCapabilitiesImmutable#getStencilBits() stencil-}bits; value {@value} */ + public static final int REQUESTED_BITS = -1; + + /** + * Request current context drawable's <i>chosen</i> + * {@link GLCapabilitiesImmutable#getDepthBits() depth-} or {@link GLCapabilitiesImmutable#getStencilBits() stencil-}bits; value {@value} */ + public static final int CHOSEN_BITS = -2; + + /** Request maximum bit count for depth- or stencil buffer (depth 32 bits, stencil 16 bits), value {@value} */ + public static final int MAXIMUM_BITS = -3; + + /** * Attaches one depth, stencil or packed-depth-stencil buffer to this FBO's instance, * selecting the internalFormat automatically. @@ -1303,90 +1629,81 @@ public class FBObject { * Stencil and depth buffer can be attached only once. * </p> * <p> - * In case the desired type or bit-number is not supported, the next available one is chosen. - * </p> + * In case the bit-count is not supported, + * the next available one is chosen, i.e. next higher (preferred) or lower bit-count. + * </p> * <p> * Use {@link #getDepthAttachment()} and/or {@link #getStencilAttachment()} to retrieve details * about the attached buffer. The details cannot be returned, since it's possible 2 buffers * are being created, depth and stencil. * </p> - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl - * @param atype either {@link Type#DEPTH}, {@link Type#STENCIL} or {@link Type#DEPTH_STENCIL} - * @param reqBits desired bits for depth or -1 for default (24 bits) + * @param atype either {@link Type#DEPTH}, {@link Type#STENCIL} or {@link Type#DEPTH_STENCIL} + * @param reqBits desired bits for depth or stencil, + * may use generic values {@link #DEFAULT_BITS}, {@link #REQUESTED_BITS}, {@link #CHOSEN_BITS} or {@link #MAXIMUM_BITS}. * @throws GLException in case the renderbuffer couldn't be allocated or one is already attached. * @throws IllegalArgumentException * @see #getDepthAttachment() * @see #getStencilAttachment() */ - public final void attachRenderbuffer(GL gl, Attachment.Type atype, int reqBits) throws GLException, IllegalArgumentException { - if( 0 > reqBits ) { - reqBits = 24; - } + public final void attachRenderbuffer(final GL gl, final Attachment.Type atype, final int reqBits) throws GLException, IllegalArgumentException { + final int reqDepth, reqStencil; + if( MAXIMUM_BITS > reqBits ) { + throw new IllegalArgumentException("reqBits out of range, shall be >= "+MAXIMUM_BITS); + } else if( MAXIMUM_BITS == reqBits ) { + reqDepth = 32; + reqStencil = 16; + } else if( CHOSEN_BITS == reqBits ) { + final GLCapabilitiesImmutable caps = gl.getContext().getGLDrawable().getChosenGLCapabilities(); + reqDepth = caps.getDepthBits(); + reqStencil = caps.getStencilBits(); + } else if( REQUESTED_BITS == reqBits ) { + final GLCapabilitiesImmutable caps = gl.getContext().getGLDrawable().getRequestedGLCapabilities(); + reqDepth = caps.getDepthBits(); + reqStencil = caps.getStencilBits(); + } else if( DEFAULT_BITS == reqBits ) { + reqDepth = 24; + reqStencil = 8; + } else { + reqDepth = reqBits; + reqStencil = reqBits; + } final int internalFormat; int internalStencilFormat = -1; - + switch ( atype ) { case DEPTH: - if( 32 <= reqBits && depth32Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT32; - } else if( 24 <= reqBits && depth24Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT24; - } else { - internalFormat = GL.GL_DEPTH_COMPONENT16; - } + internalFormat = getDepthIFormat(reqDepth); break; - + case STENCIL: - if( 16 <= reqBits && stencil16Avail ) { - internalFormat = GL2GL3.GL_STENCIL_INDEX16; - } else if( 8 <= reqBits && stencil08Avail ) { - internalFormat = GL.GL_STENCIL_INDEX8; - } else if( 4 <= reqBits && stencil04Avail ) { - internalFormat = GL.GL_STENCIL_INDEX4; - } else if( 1 <= reqBits && stencil01Avail ) { - internalFormat = GL.GL_STENCIL_INDEX1; - } else { - throw new GLException("stencil buffer n/a"); - } + internalFormat = getStencilIFormat(reqStencil); break; - + case DEPTH_STENCIL: if( packedDepthStencilAvail ) { internalFormat = GL.GL_DEPTH24_STENCIL8; } else { - if( 24 <= reqBits && depth24Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT24; - } else { - internalFormat = GL.GL_DEPTH_COMPONENT16; - } - if( stencil08Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX8; - } else if( stencil04Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX4; - } else if( stencil01Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX1; - } else { - throw new GLException("stencil buffer n/a"); - } + internalFormat = getDepthIFormat(reqDepth); + internalStencilFormat = getStencilIFormat(reqStencil); } break; default: throw new IllegalArgumentException("only depth/stencil types allowed, was "+atype+", "+this); } - attachRenderbufferImpl(gl, atype, internalFormat); - + if(0<=internalStencilFormat) { attachRenderbufferImpl(gl, Attachment.Type.STENCIL, internalStencilFormat); } } - + /** * Attaches one depth, stencil or packed-depth-stencil buffer to this FBO's instance, - * depending on the <code>internalFormat</code>. + * depending on the <code>internalFormat</code>. * <p> * Stencil and depth buffer can be attached only once. * </p> @@ -1395,9 +1712,9 @@ public class FBObject { * about the attached buffer. The details cannot be returned, since it's possible 2 buffers * are being created, depth and stencil. * </p> - * + * * <p>Leaves the FBO bound.</p> - * + * * @param gl the current GL context * @param internalFormat {@link GL#GL_DEPTH_COMPONENT16}, {@link GL#GL_DEPTH_COMPONENT24}, {@link GL#GL_DEPTH_COMPONENT32}, * {@link GL#GL_STENCIL_INDEX1}, {@link GL#GL_STENCIL_INDEX4}, {@link GL#GL_STENCIL_INDEX8} @@ -1407,376 +1724,478 @@ public class FBObject { * @see #getDepthAttachment() * @see #getStencilAttachment() */ - public final void attachRenderbuffer(GL gl, int internalFormat) throws GLException, IllegalArgumentException { + public final void attachRenderbuffer(final GL gl, final int internalFormat) throws GLException, IllegalArgumentException { final Attachment.Type atype = Attachment.Type.determine(internalFormat); if( Attachment.Type.DEPTH != atype && Attachment.Type.STENCIL != atype && Attachment.Type.DEPTH_STENCIL != atype ) { - throw new IllegalArgumentException("renderformat invalid: 0x"+Integer.toHexString(internalFormat)+", "+this); + throw new IllegalArgumentException("renderformat invalid: "+toHexString(internalFormat)+", "+this); } attachRenderbufferImpl(gl, atype, internalFormat); } - - protected final void attachRenderbufferImpl(GL gl, Attachment.Type atype, int internalFormat) throws GLException { + + protected final void attachRenderbufferImpl(final GL gl, final Attachment.Type atype, final int internalFormat) throws GLException { if( null != depth && ( Attachment.Type.DEPTH == atype || Attachment.Type.DEPTH_STENCIL == atype ) ) { - throw new GLException("FBO depth buffer already attached (rb "+depth+"), type is "+atype+", 0x"+Integer.toHexString(internalFormat)+", "+this); - } + throw new GLException("FBO depth buffer already attached (rb "+depth+"), type is "+atype+", "+toHexString(internalFormat)+", "+this); + } if( null != stencil && ( Attachment.Type.STENCIL== atype || Attachment.Type.DEPTH_STENCIL == atype ) ) { - throw new GLException("FBO stencil buffer already attached (rb "+stencil+"), type is "+atype+", 0x"+Integer.toHexString(internalFormat)+", "+this); + throw new GLException("FBO stencil buffer already attached (rb "+stencil+"), type is "+atype+", "+toHexString(internalFormat)+", "+this); } + bind(gl); + attachRenderbufferImpl2(gl, atype, internalFormat); } - - private final void attachRenderbufferImpl2(GL gl, Attachment.Type atype, int internalFormat) throws GLException { + + private final void attachRenderbufferImpl2(final GL gl, final Attachment.Type atype, final int internalFormat) throws GLException { + // atype and current depth and stencil instance are already validated in 'attachRenderbufferImpl(..)' if( Attachment.Type.DEPTH == atype ) { if(null == depth) { - depth = new RenderAttachment(Type.DEPTH, internalFormat, samples, width, height, 0); + depth = createRenderAttachment(Type.DEPTH, internalFormat, samples, width, height); } else { depth.setSize(width, height); depth.setSamples(samples); } + validateAttachmentSize(depth); depth.initialize(gl); } else if( Attachment.Type.STENCIL == atype ) { if(null == stencil) { - stencil = new RenderAttachment(Type.STENCIL, internalFormat, samples, width, height, 0); + stencil = createRenderAttachment(Type.STENCIL, internalFormat, samples, width, height); } else { stencil.setSize(width, height); stencil.setSamples(samples); } + validateAttachmentSize(stencil); stencil.initialize(gl); } else if( Attachment.Type.DEPTH_STENCIL == atype ) { if(null == depth) { - depth = new RenderAttachment(Type.DEPTH, internalFormat, samples, width, height, 0); + if(null != stencil) { + throw new InternalError("XXX: DEPTH_STENCIL, depth was null, stencil not: "+this.toString()); + } + depth = createRenderAttachment(Type.DEPTH_STENCIL, internalFormat, samples, width, height); } else { depth.setSize(width, height); depth.setSamples(samples); } + validateAttachmentSize(depth); depth.initialize(gl); - if(null == stencil) { - stencil = new RenderAttachment(Type.STENCIL, internalFormat, samples, width, height, depth.getName()); - } else { - stencil.setName(depth.getName()); - stencil.setSize(width, height); - stencil.setSamples(samples); - } - stencil.initialize(gl); + // DEPTH_STENCIL shares buffer w/ depth and stencil + stencil = depth; } - bind(gl); - // Attach the buffer if( Attachment.Type.DEPTH == atype ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, depth.getName()); } else if( Attachment.Type.STENCIL == atype ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, stencil.getName()); } else if( Attachment.Type.DEPTH_STENCIL == atype ) { - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, depth.getName()); + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, depth.getName()); gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, stencil.getName()); } - + + modified = true; + if(!ignoreStatus) { updateStatus(gl); if( !isStatusValid() ) { detachRenderbuffer(gl, atype, true); - throw new GLException("renderbuffer attachment failed: "+this.getStatusString()); + throw new GLException("renderbuffer [attachmentType "+atype+", iformat "+toHexString(internalFormat)+"] failed: "+this.getStatusString()+": "+this.toString()); } } if(DEBUG) { - System.err.println("FBObject.attachRenderbuffer: [attachmentType "+atype+"]: "+this); - } + System.err.println("FBObject.attachRenderbuffer.X: [attachmentType "+atype+", iformat "+toHexString(internalFormat)+"]: "+this); + } } - + /** - * Detaches a {@link Colorbuffer}, i.e. {@link ColorAttachment} or {@link TextureAttachment}. + * Detaches a {@link Colorbuffer}, i.e. {@link ColorAttachment} or {@link TextureAttachment}. * <p>Leaves the FBO bound!</p> - * + * * @param gl * @param attachmentPoint * @param dispose true if the Colorbuffer shall be disposed * @return the detached Colorbuffer * @throws IllegalArgumentException */ - public final Colorbuffer detachColorbuffer(GL gl, int attachmentPoint, boolean dispose) throws IllegalArgumentException { - final Colorbuffer res = detachColorbufferImpl(gl, attachmentPoint, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + public final Colorbuffer detachColorbuffer(final GL gl, final int attachmentPoint, final boolean dispose) throws IllegalArgumentException { + bind(gl); + + final Colorbuffer res = detachColorbufferImpl(gl, attachmentPoint, dispose ? DetachAction.DISPOSE : DetachAction.NONE, 0); if(null == res) { - throw new IllegalArgumentException("ColorAttachment at "+attachmentPoint+", not attached, "+this); + throw new IllegalArgumentException("ColorAttachment at "+attachmentPoint+", not attached, "+this); } if(DEBUG) { - System.err.println("FBObject.detachColorbuffer: [attachmentPoint "+attachmentPoint+", dispose "+dispose+"]: "+res+", "+this); + System.err.println("FBObject.detachColorbuffer.X: [attachmentPoint "+attachmentPoint+", dispose "+dispose+"]: "+res+", "+this); } return res; } - - private final Colorbuffer detachColorbufferImpl(GL gl, int attachmentPoint, DetachAction detachAction) { - Colorbuffer colbuf = colorAttachmentPoints[attachmentPoint]; // shortcut, don't validate here - - if(null == colbuf) { + + private final Colorbuffer detachColorbufferImpl(final GL gl, final int attachmentPoint, final DetachAction detachAction, final int sampleCountChange) { + final Colorbuffer colbufOld = colorbufferAttachments[attachmentPoint]; // shortcut, don't validate here + + if(null == colbufOld) { return null; } - - bind(gl); - - removeColorAttachment(attachmentPoint, colbuf); - - if(colbuf instanceof TextureAttachment) { - final TextureAttachment texA = (TextureAttachment) colbuf; + + removeColorAttachment(attachmentPoint, colbufOld); + + if( colbufOld.isTextureAttachment() ) { + final TextureAttachment texA = colbufOld.getTextureAttachment(); if( 0 != texA.getName() ) { gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, GL.GL_TEXTURE_2D, 0, 0); gl.glBindTexture(GL.GL_TEXTURE_2D, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + texA.free(gl); + break; + default: + } } - switch(detachAction) { - case DISPOSE: - texA.free(gl); - break; - case RECREATE: - texA.free(gl); - if(samples == 0) { - // stay non MSAA - texA.setSize(width, height); - } else { - // switch to MSAA - colbuf = createColorAttachment(hasAlpha(texA.format)); - } - attachColorbuffer(gl, attachmentPoint, colbuf); - break; - default: + if(DetachAction.RECREATE == detachAction) { + final Colorbuffer colbufNew; + if( 0 < sampleCountChange ) { + // switch to MSAA: TextureAttachment -> ColorAttachment + colbufNew = createColorAttachment(hasAlpha(texA.format)); + } else { + // keep MSAA settings + texA.setSize(width, height); + colbufNew = texA; + } + attachColorbufferImpl(gl, attachmentPoint, colbufNew); } - } else if(colbuf instanceof ColorAttachment) { - final ColorAttachment colA = (ColorAttachment) colbuf; + } else { + final ColorAttachment colA = colbufOld.getColorAttachment(); if( 0 != colA.getName() ) { - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, - GL.GL_COLOR_ATTACHMENT0+attachmentPoint, + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0+attachmentPoint, GL.GL_RENDERBUFFER, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + colA.free(gl); + break; + default: + } } - switch(detachAction) { - case DISPOSE: - colA.free(gl); - break; - case RECREATE: - colA.free(gl); - if(samples > 0) { - // stay MSAA - colA.setSize(width, height); - colA.setSamples(samples); + if(DetachAction.RECREATE == detachAction) { + final Colorbuffer colbufNew; + if( 0 <= sampleCountChange || null == samplingColorSink ) { + // keep ColorAttachment, + // including 'switch to non-MSAA' if no samplingColorSink is available + // to determine whether a TextureAttachment or ColorAttachment is desired! + colA.setSize(width, height); + colA.setSamples(samples); + colbufNew = colA; + } else { + // switch to non MSAA + if( samplingColorSink.isTextureAttachment() ) { + final TextureAttachment samplingTextureSink = samplingColorSink.getTextureAttachment(); + colbufNew = createColorTextureAttachment(samplingTextureSink.format, width, height, + samplingTextureSink.dataFormat, samplingTextureSink.dataType, + samplingTextureSink.magFilter, samplingTextureSink.minFilter, + samplingTextureSink.wrapS, samplingTextureSink.wrapT); } else { - // switch to non MSAA - if(null != samplesSinkTexture) { - colbuf = createColorTextureAttachment(samplesSinkTexture.format, width, height, - samplesSinkTexture.dataFormat, samplesSinkTexture.dataType, - samplesSinkTexture.magFilter, samplesSinkTexture.minFilter, - samplesSinkTexture.wrapS, samplesSinkTexture.wrapT); - } else { - colbuf = createColorTextureAttachment(gl.getGLProfile(), true, width, height); - } + colbufNew = createColorAttachment(samplingColorSink.getFormat(), 0, width, height); } - attachColorbuffer(gl, attachmentPoint, colbuf); - break; - default: + } + attachColorbuffer(gl, attachmentPoint, colbufNew); + } + } + return colbufOld; + } + + private final void freeAllColorbufferImpl(final GL gl) { + for(int i=0; i<maxColorAttachments; i++) { + final Colorbuffer colbuf = colorbufferAttachments[i]; // shortcut, don't validate here + + if(null == colbuf) { + return; + } + + if( colbuf.isTextureAttachment() ) { + final TextureAttachment texA = colbuf.getTextureAttachment(); + if( 0 != texA.getName() ) { + gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0 + i, + GL.GL_TEXTURE_2D, 0, 0); + gl.glBindTexture(GL.GL_TEXTURE_2D, 0); + } + texA.free(gl); + } else { + final ColorAttachment colA = colbuf.getColorAttachment(); + if( 0 != colA.getName() ) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0 + i, + GL.GL_RENDERBUFFER, 0); + } + colA.free(gl); } } - return colbuf; } - + /** - * + * * @param gl * @param dispose true if the Colorbuffer shall be disposed - * @param reqAType {@link Type#DEPTH}, {@link Type#DEPTH} or {@link Type#DEPTH_STENCIL} + * @param reqAType {@link Type#DEPTH}, {@link Type#DEPTH} or {@link Type#DEPTH_STENCIL} */ - public final void detachRenderbuffer(GL gl, Attachment.Type atype, boolean dispose) throws IllegalArgumentException { - detachRenderbufferImpl(gl, atype, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + public final void detachRenderbuffer(final GL gl, final Attachment.Type atype, final boolean dispose) throws IllegalArgumentException { + bind(gl); + final RenderAttachment res = detachRenderbufferImpl(gl, atype, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + if(null == res) { + throw new IllegalArgumentException("RenderAttachment type "+atype+", not attached, "+this); + } if(DEBUG) { - System.err.println("FBObject.detachRenderbuffer: [attachmentType "+atype+", dispose "+dispose+"]: "+this); - } + System.err.println("FBObject.detachRenderbuffer.X: [attachmentType "+atype+", dispose "+dispose+"]: "+this); + } } - + public final boolean isDepthStencilPackedFormat() { final boolean res = null != depth && null != stencil && depth.format == stencil.format ; - if(res && depth.getName() != stencil.getName() ) { - throw new InternalError("depth/stencil packed format not sharing: depth "+depth+", stencil "+stencil); + if(res) { + if(depth.getName() != stencil.getName() ) { + throw new InternalError("depth/stencil packed format not sharing: depth "+depth+", stencil "+stencil); + } + if(depth != stencil) { + throw new InternalError("depth/stencil packed format not a shared reference: depth "+depth+", stencil "+stencil); + } } return res; } - - private final void detachRenderbufferImpl(GL gl, Attachment.Type atype, DetachAction detachAction) throws IllegalArgumentException { + + private final RenderAttachment detachRenderbufferImpl(final GL gl, Attachment.Type atype, final DetachAction detachAction) throws IllegalArgumentException { switch ( atype ) { case DEPTH: case STENCIL: - case DEPTH_STENCIL: + case DEPTH_STENCIL: break; default: throw new IllegalArgumentException("only depth/stencil types allowed, was "+atype+", "+this); - } + } if( null == depth && null == stencil ) { - return ; // nop - } - // reduction of possible combinations, create unique atype command(s) - final ArrayList<Attachment.Type> actions = new ArrayList<Attachment.Type>(2); - if( isDepthStencilPackedFormat() ) { - // packed - actions.add(Attachment.Type.DEPTH_STENCIL); - } else { - // individual - switch ( atype ) { - case DEPTH: - if( null != depth ) { actions.add(Attachment.Type.DEPTH); } - break; - case STENCIL: - if( null != stencil ) { actions.add(Attachment.Type.STENCIL); } - break; - case DEPTH_STENCIL: - if( null != depth ) { actions.add(Attachment.Type.DEPTH); } - if( null != stencil ) { actions.add(Attachment.Type.STENCIL); } - break; - default: // handled - } + return null; // nop } - - bind(gl); - - for(int i = 0; i < actions.size(); i++) { - final int format; - - Attachment.Type action = actions.get(i); - switch ( action ) { - case DEPTH: - format = depth.format; - if( 0 != depth.getName() ) { + final boolean packed = isDepthStencilPackedFormat(); + if( packed ) { + // Note: DEPTH_STENCIL shares buffer w/ depth and stencil + atype = Attachment.Type.DEPTH_STENCIL; + } + final RenderAttachment renderOld; + switch ( atype ) { + case DEPTH: + renderOld = depth; + if( null != renderOld ) { + final int format = renderOld.format; + if( 0 != renderOld.getName() ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + renderOld.free(gl); + break; + default: + } } - switch(detachAction) { - case DISPOSE: - case RECREATE: - depth.free(gl); - break; - default: - } - if(DetachAction.RECREATE != detachAction) { + if(DetachAction.RECREATE == detachAction) { + attachRenderbufferImpl2(gl, atype, format); + } else { depth = null; } - break; - case STENCIL: - format = stencil.format; - if(0 != stencil.getName()) { + } + break; + case STENCIL: + renderOld = stencil; + if( null != renderOld ) { + final int format = renderOld.format; + if(0 != renderOld.getName()) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + renderOld.free(gl); + break; + default: + } } - switch(detachAction) { - case DISPOSE: - case RECREATE: - stencil.free(gl); - break; - default: - } - if(DetachAction.RECREATE != detachAction) { + if(DetachAction.RECREATE == detachAction) { + attachRenderbufferImpl2(gl, atype, format); + } else { stencil = null; } - break; - case DEPTH_STENCIL: - format = depth.format; - if(0 != depth.getName()) { + } + break; + case DEPTH_STENCIL: + renderOld = depth; + if( null != renderOld ) { + final int format = renderOld.format; + if(0 != renderOld.getName()) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); - } - switch(detachAction) { - case DISPOSE: - case RECREATE: - depth.free(gl); - stencil.free(gl); - break; - default: + if(packed) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + } + switch(detachAction) { + case DISPOSE: + case RECREATE: + renderOld.free(gl); + break; + default: + } } - if(DetachAction.RECREATE != detachAction) { + if(DetachAction.RECREATE == detachAction) { + attachRenderbufferImpl2(gl, packed ? Attachment.Type.DEPTH_STENCIL : Attachment.Type.DEPTH, format); + } else { depth = null; + if(packed) { + stencil = null; + } + } + } + if( !packed && null != stencil ) { + final int format = stencil.format; + if(0 != stencil.getName()) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + stencil.free(gl); + break; + default: + } + } + if(DetachAction.RECREATE == detachAction) { + attachRenderbufferImpl2(gl, Attachment.Type.STENCIL, format); + } else { stencil = null; } - break; - default: - throw new InternalError("XXX"); + } + break; + default: + throw new InternalError("XXX"); // handled by caller + } + modified = true; + return renderOld; + } + + private final void freeAllRenderbufferImpl(final GL gl) throws IllegalArgumentException { + // Note: DEPTH_STENCIL shares buffer w/ depth and stencil + final boolean packed = isDepthStencilPackedFormat(); + if( null != depth ) { + if(0 != depth.getName()) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + if(packed) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + } + depth.free(gl); } - if(DetachAction.RECREATE == detachAction) { - attachRenderbufferImpl2(gl, action, format); + } + if( !packed && null != stencil ) { + if(0 != stencil.getName()) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + stencil.free(gl); } - } + } } - - /** + + /** * Detaches all {@link ColorAttachment}s, {@link TextureAttachment}s and {@link RenderAttachment}s * and disposes them. - * <p>Leaves the FBO bound!</p> + * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. - * </p> + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. + * </p> * @param gl the current GL context */ - public final void detachAll(GL gl) { - if(null != samplesSink) { - samplesSink.detachAll(gl); - } - detachAllImpl(gl, true/* detachNonColorbuffer */, false /* recreate */); - } - - /** - * Detaches all {@link ColorAttachment}s and {@link TextureAttachment}s + public final void detachAll(final GL gl) { + if(null != samplingSink) { + samplingSink.detachAll(gl); + } + detachAllImpl(gl, true/* detachNonColorbuffer */, false /* recreate */, 0); + } + + /** + * Detaches all {@link ColorAttachment}s and {@link TextureAttachment}s * and disposes them. - * <p>Leaves the FBO bound!</p> + * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. - * </p> + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. + * </p> * @param gl the current GL context */ - public final void detachAllColorbuffer(GL gl) { - if(null != samplesSink) { - samplesSink.detachAllColorbuffer(gl); - } - detachAllImpl(gl, false/* detachNonColorbuffer */, false /* recreate */); - } - - /** - * Detaches all {@link TextureAttachment}s and disposes them. - * <p>Leaves the FBO bound!</p> + public final void detachAllColorbuffer(final GL gl) { + if(null != samplingSink) { + samplingSink.detachAllColorbuffer(gl); + } + detachAllImpl(gl, false/* detachNonColorbuffer */, false /* recreate */, 0); + } + + /** + * Detaches all {@link TextureAttachment}s and disposes them. + * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. - * </p> + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. + * </p> * @param gl the current GL context */ - public final void detachAllTexturebuffer(GL gl) { - if(null != samplesSink) { - samplesSink.detachAllTexturebuffer(gl); + public final void detachAllTexturebuffer(final GL gl) { + if( !isInitialized() ) { + return; } + if(null != samplingSink) { + samplingSink.detachAllTexturebuffer(gl); + } + bind(gl); for(int i=0; i<maxColorAttachments; i++) { - if(colorAttachmentPoints[i] instanceof TextureAttachment) { - detachColorbufferImpl(gl, i, DetachAction.DISPOSE); + if( colorbufferAttachments[i].isTextureAttachment() ) { + detachColorbufferImpl(gl, i, DetachAction.DISPOSE, 0); } } if(DEBUG) { - System.err.println("FBObject.detachAllTexturebuffer: "+this); + System.err.println("FBObject.detachAllTexturebuffer.X: "+this); } } - - public final void detachAllRenderbuffer(GL gl) { - if(null != samplesSink) { - samplesSink.detachAllRenderbuffer(gl); + + public final void detachAllRenderbuffer(final GL gl) { + if( !isInitialized() ) { + return; + } + if(null != samplingSink) { + samplingSink.detachAllRenderbuffer(gl); } + bind(gl); detachRenderbufferImpl(gl, Attachment.Type.DEPTH_STENCIL, DetachAction.DISPOSE); } - - private final void detachAllImpl(GL gl, boolean detachNonColorbuffer, boolean recreate) { + + private final void detachAllImpl(final GL gl, final boolean detachNonColorbuffer, final boolean recreate, final int sampleCountChange) { + if( !isInitialized() ) { + return; + } ignoreStatus = recreate; // ignore status on single calls only if recreate -> reset try { + bind(gl); + if(FBOResizeQuirk) { + if(detachNonColorbuffer && recreate) { + // free all colorbuffer & renderbuffer 1st + freeAllColorbufferImpl(gl); + freeAllRenderbufferImpl(gl); + } + } for(int i=0; i<maxColorAttachments; i++) { - detachColorbufferImpl(gl, i, recreate ? DetachAction.RECREATE : DetachAction.DISPOSE); + detachColorbufferImpl(gl, i, recreate ? DetachAction.RECREATE : DetachAction.DISPOSE, sampleCountChange); } - if( !recreate && colorAttachmentCount>0 ) { + if( !recreate && colorbufferCount>0 ) { throw new InternalError("Non zero ColorAttachments "+this); } - + if(detachNonColorbuffer) { detachRenderbufferImpl(gl, Attachment.Type.DEPTH_STENCIL, recreate ? DetachAction.RECREATE : DetachAction.DISPOSE); } if(ignoreStatus) { // post validate - updateStatus(gl); + /* if(true) { + throw new GLException("Simulating bug 617, reset FBO failure"); + } */ + updateStatus(gl); if(!isStatusValid()) { throw new GLException("detachAllImpl failed "+getStatusString()+", "+this); } @@ -1785,182 +2204,337 @@ public class FBObject { ignoreStatus = false; } if(DEBUG) { - System.err.println("FBObject.detachAll: [resetNonColorbuffer "+detachNonColorbuffer+", recreate "+recreate+"]: "+this); + System.err.println("FBObject.detachAll.X: [resetNonColorbuffer "+detachNonColorbuffer+", recreate "+recreate+"]: "+this); } } - + /** * @param gl the current GL context */ - public final void destroy(GL gl) { - if(null != samplesSink) { - samplesSink.destroy(gl); + public final void destroy(final GL gl) { + if(!initialized) { + return; + } + if(DEBUG) { + System.err.println("FBObject.destroy.0: "+this); + // Thread.dumpStack(); } - - detachAllImpl(gl, true /* detachNonColorbuffer */, false /* recreate */); - + if( null != samplingSink && samplingSink.isInitialized() ) { + samplingSink.destroy(gl); + } + + detachAllImpl(gl, true /* detachNonColorbuffer */, false /* recreate */, 0); + // cache FB names, preset exposed to zero, // braking ties w/ GL/GLContext link to getReadFramebuffer()/getWriteFramebuffer() final int fb_cache = fbName; fbName = 0; - int name[] = new int[1]; + final int name[] = new int[1]; if(0!=fb_cache) { name[0] = fb_cache; gl.glDeleteFramebuffers(1, name, 0); - } + } initialized = false; bound = false; if(DEBUG) { - System.err.println("FBObject.destroy: "+this); + System.err.println("FBObject.destroy.X: "+this); } } private final boolean sampleSinkSizeMismatch() { - return samplesSink.getWidth() != width || samplesSink.getHeight() != height ; - } - private final boolean sampleSinkTexMismatch() { - return null == samplesSinkTexture || 0 == samplesSinkTexture.getName() ; + return samplingSink.getWidth() != width || samplingSink.getHeight() != height ; } private final boolean sampleSinkDepthStencilMismatch() { - final boolean depthMismatch = ( null != depth && null == samplesSink.depth ) || - ( null != depth && null != samplesSink.depth && - depth.format != samplesSink.depth.format ); - - final boolean stencilMismatch = ( null != stencil && null == samplesSink.stencil ) || - ( null != stencil && null != samplesSink.stencil && - stencil.format != samplesSink.stencil.format ); - - return depthMismatch || stencilMismatch; - } - - private final void resetMSAATexture2DSink(GL gl) throws GLException { - if(null == samplesSink ) { - return; // this is the sample sink! - } - if(0 == samples) { + if ( ( null != depth && ( null == samplingSink.depth || depth.format != samplingSink.depth.format ) ) + || + ( null == depth && null != samplingSink.depth ) + ) { + return true; + } + + if ( ( null != stencil && ( null == samplingSink.stencil || stencil.format != samplingSink.stencil.format ) ) + || + ( null == stencil && null != samplingSink.stencil ) + ) { + return true; + } + + return false; + } + /** + * For GLES3, sampling-sink {@link Colorbuffer} <i>internal format</i> <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. + * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} + * and {@link #createColorAttachment(boolean)}. + */ + private final boolean sampleSinkExFormatMismatch(final GL gl) { + if( null != samplingColorSink && getColorbufferCount() > 0 && gl.isGL2ES3() ) { + final Attachment ca = (Attachment)getColorbuffer(0); // should be at attachment-point 0 + // We cannot comply w/ attachment's format other than attachment point 0! + // return ( null != ca && ca.format != samplingColorSink.getFormat() ) || + // hasAlpha(samplingColorSink.getFormat()) != hasAttachmentUsingAlpha(); + return null != ca && ca.format != samplingColorSink.getFormat(); + } + return false; + } + + /** + * Manually validates the MSAA sampling sink, if used. + * <p> + * If MSAA is being used and no sampling sink is attached via {@link #setSamplingSink(FBObject)} + * a new sampling sink is being created. + * </p> + * <p> + * If the sampling sink size or attributes differs from the source, its attachments are reset + * to match the source. + * </p> + * <p> + * Automatically called by {@link #reset(GL, int, int, int, boolean)} + * and {@link #syncSamplingSink(GL)}. + * </p> + * <p> + * It is recommended to call this method after initializing the FBO and attaching renderbuffer etc for the 1st time + * if access to sampling sink resources is required. + * </p> + * + * <p>Leaves the FBO bound state untouched</p> + * + * @param gl the current GL context + * @return {@code true} if this instance has been modified, otherwise {@code false}. + * @throws GLException in case of an error, i.e. size too big, etc .. + */ + public final boolean resetSamplingSink(final GL gl) throws GLException { + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.0"); + Thread.dumpStack(); + } + + if( 0 == samples ) { + final boolean modifiedInstance; // MSAA off - if(samplesSink.initialized) { + if( null != samplingSink ) { // cleanup - samplesSink.detachAll(gl); + if( samplingSink.initialized ) { + samplingSink.detachAll(gl); + } + samplingSink = null; + samplingColorSink = null; + modifiedInstance = true; + } else { + modifiedInstance = false; } - return; + this.modified = false; + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.X1: zero samples, mod "+modifiedInstance+"\n\tTHIS "+this); + } + return modifiedInstance; + } + + boolean modifiedInstance = false; + + if( null == samplingSink ) { + samplingSink = new FBObject(); + samplingSink.init(gl, width, height, 0); + samplingColorSink = null; + modifiedInstance = true; + } else if( !samplingSink.initialized ) { + throw new InternalError("InitState Mismatch: samplingSink set, but not initialized "+samplingSink); + } else if( null == samplingColorSink || 0 == samplingColorSink.getName() ) { + throw new InternalError("InitState Mismatch: samplingColorSink set, but not initialized "+samplingColorSink+", "+samplingSink); } - - if(!samplesSink.initialized) { - samplesSink.init(gl, width, height, 0); + + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.1: mod "+modifiedInstance+"\n\tTHIS "+this+",\n\tSINK "+samplingSink); } - + boolean sampleSinkExFormatMismatch = sampleSinkExFormatMismatch(gl); boolean sampleSinkSizeMismatch = sampleSinkSizeMismatch(); - boolean sampleSinkTexMismatch = sampleSinkTexMismatch(); boolean sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); - - /** if(DEBUG) { - System.err.println("FBObject.resetMSAATexture2DSink.0: \n\tTHIS "+this+",\n\tSINK "+samplesSink+ - "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); - } */ - - if(!sampleSinkSizeMismatch && !sampleSinkTexMismatch && !sampleSinkDepthStencilMismatch) { - // all properties match .. - return; - } - - unbind(gl); - + + if( modifiedInstance ) { + // samplingColorSink == null + // must match size, format and colorbuffer do not exist yet + if( sampleSinkExFormatMismatch || sampleSinkSizeMismatch ) { + throw new InternalError("InitState Mismatch: Matching exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", "+this); + } + } else { + // samplingColorSink != null + if(!sampleSinkExFormatMismatch && !sampleSinkSizeMismatch && !sampleSinkDepthStencilMismatch) { + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.X2: Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch+ + ", mod "+modifiedInstance); + } + // all properties match .. + samplingSink.modified = false; + this.modified = false; + return modifiedInstance; + } + } + + final boolean wasBound; + if( isBound() ) { + markUnbound(); // automatic GL unbind by sampleSink binding + wasBound = true; + } else { + wasBound = false; + } + if(DEBUG) { - System.err.println("FBObject.resetMSAATexture2DSink: BEGIN\n\tTHIS "+this+",\n\tSINK "+samplesSink+ - "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); - } - - if( sampleSinkDepthStencilMismatch ) { - samplesSink.detachAllRenderbuffer(gl); - } - - if( sampleSinkSizeMismatch ) { - samplesSink.reset(gl, width, height); - } - - if(null == samplesSinkTexture) { - samplesSinkTexture = samplesSink.attachTexture2D(gl, 0, true); - } else if( 0 == samplesSinkTexture.getName() ) { - samplesSinkTexture.setSize(width, height); - samplesSink.attachColorbuffer(gl, 0, samplesSinkTexture); - } - - if( sampleSinkDepthStencilMismatch ) { - samplesSink.attachRenderbuffer(gl, depth.format); + System.err.println("FBObject.resetSamplingSink.2: wasBound "+wasBound+", matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); + } + + modifiedInstance = true; + + if( sampleSinkDepthStencilMismatch ) { // includes 1st init + samplingSink.detachAllRenderbuffer(gl); + } + + final boolean samplingColorSinkShallBeTA = null == samplingColorSink || samplingColorSink.isTextureAttachment(); + + if( sampleSinkExFormatMismatch ) { + samplingSink.detachAllColorbuffer(gl); + samplingColorSink = null; + } else if( sampleSinkSizeMismatch ) { + samplingSink.resetSizeImpl(gl, width, height); + samplingColorSink = samplingSink.getColorbuffer(0); + } + + if( null == samplingColorSink ) { // sampleSinkFormatMismatch || 1st init + final Colorbuffer cb0 = getColorbuffer(0); // align with colorbuffer at attachment-point 0 + if( null != cb0 ) { + // match pre-existing format + if( samplingColorSinkShallBeTA ) { + samplingColorSink = createColorTextureAttachment(gl, cb0.getFormat(), width, height, + GL.GL_NEAREST, GL.GL_NEAREST, + GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + } else { + samplingColorSink = createColorAttachment(cb0.getFormat(), 0, width, height); + } + samplingSink.attachColorbuffer(gl, 0, samplingColorSink); + } else { + // match default format + final boolean hasAlpha = hasAttachmentUsingAlpha(); + if( samplingColorSinkShallBeTA ) { + samplingColorSink = samplingSink.attachTexture2D(gl, 0, hasAlpha); + } else { + samplingColorSink = samplingSink.attachColorbuffer(gl, 0, hasAlpha); + } + } + } + + if( sampleSinkDepthStencilMismatch ) { // includes 1st init + samplingSink.attachRenderbuffer(gl, depth.format); if( null != stencil && !isDepthStencilPackedFormat() ) { - samplesSink.attachRenderbuffer(gl, stencil.format); + samplingSink.attachRenderbuffer(gl, stencil.format); } - } - + } + + sampleSinkExFormatMismatch = sampleSinkExFormatMismatch(gl); sampleSinkSizeMismatch = sampleSinkSizeMismatch(); - sampleSinkTexMismatch = sampleSinkTexMismatch(); sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); - if(sampleSinkSizeMismatch || sampleSinkTexMismatch || sampleSinkDepthStencilMismatch) { - throw new InternalError("Samples sink mismatch after reset: \n\tTHIS "+this+",\n\t SINK "+samplesSink+ - "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + if(sampleSinkExFormatMismatch || sampleSinkSizeMismatch || sampleSinkDepthStencilMismatch) { + throw new InternalError("Samples sink mismatch after reset: \n\tTHIS "+this+",\n\t SINK "+samplingSink+ + "\n\t Mismatch. Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); + } + + samplingSink.modified = false; + samplingSink.unbind(gl); + this.modified = false; + + if(wasBound) { + bind(gl); } - + if(DEBUG) { - System.err.println("FBObject.resetMSAATexture2DSink: END\n\tTHIS "+this+",\n\tSINK "+samplesSink+ - "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + System.err.println("FBObject.resetSamplingSink.XX: END mod "+modifiedInstance+"\n\tTHIS "+this+",\n\tSINK "+samplingSink+ + "\n\t Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); + } + return modifiedInstance; + } + + /** + * Setting this FBO sampling sink. + * @param newSamplingSink the new and initialized FBO sampling sink to use, or null to remove current sampling sink + * @return the previous sampling sink or null if none was attached + * @throws GLException if this FBO doesn't use MSAA or the given sink uses MSAA itself + * @throws IllegalStateException if the {@code newSamplingSink} is not null and not initialized + */ + public FBObject setSamplingSink(final FBObject newSamplingSink) throws IllegalStateException, GLException { + final FBObject prev = samplingSink; + if( null == newSamplingSink) { + samplingSink = null; + samplingColorSink = null; + } else if( samples > 0 ) { + if( !newSamplingSink.isInitialized() ) { + throw new IllegalStateException("SamplingSink not initialized: "+newSamplingSink); + } + if( newSamplingSink.getNumSamples() > 0 ) { + throw new GLException("SamplingSink FBO cannot use MSAA itself: "+newSamplingSink); + } + samplingSink = newSamplingSink; + samplingColorSink = newSamplingSink.getColorbuffer(0); + } else { + throw new GLException("Setting SamplingSink for non MSAA FBO not allowed: "+this); } + modified = true; + samplingSinkDirty = true; + return prev; } - - /** + + /** * Bind this FBO, i.e. bind write framebuffer to {@link #getWriteFramebuffer()}. - * - * <p>If multisampling is used, it sets the read framebuffer to the sampling sink {@link #getWriteFramebuffer()}, - * if full FBO is supported.</p> - * - * <p> + * + * <p> + * If multisampling is used, it sets the read framebuffer to the sampling sink {@link #getWriteFramebuffer()}. + * </p> + * <p> * In case you have attached more than one color buffer, - * you may want to setup {@link GL2GL3#glDrawBuffers(int, int[], int)}. + * you may want to setup {@link GL2ES3#glDrawBuffers(int, int[], int)}. * </p> * @param gl the current GL context * @throws GLException */ - public final void bind(GL gl) throws GLException { + public final void bind(final GL gl) throws GLException { if(!bound || fbName != gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER)) { checkInitialized(); - if(samples > 0 && fullFBOSupport) { - // draw to multisampling - read from samplesSink - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, getWriteFramebuffer()); - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, getReadFramebuffer()); + if( fullFBOSupport ) { + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, getWriteFramebuffer()); // this fb, msaa or normal + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, getReadFramebuffer()); // msaa: sampling sink, normal: this fb } else { - // one for all - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, getWriteFramebuffer()); + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, getWriteFramebuffer()); // normal: read/write } - bound = true; - samplesSinkDirty = true; + samplingSinkDirty = true; } } - /** + /** * Unbind this FBO, i.e. bind read and write framebuffer to default, see {@link GLBase#getDefaultDrawFramebuffer()}. - * - * <p>If full FBO is supported, sets the read and write framebuffer individually to default, hence not disturbing + * + * <p>If full FBO is supported, sets the read and write framebuffer individually to default, hence not disturbing * an optional operating MSAA FBO, see {@link GLBase#getDefaultReadFramebuffer()} and {@link GLBase#getDefaultDrawFramebuffer()}</p> - * + * * @param gl the current GL context * @throws GLException */ - public final void unbind(GL gl) throws GLException { + public final void unbind(final GL gl) throws GLException { if(bound) { if(fullFBOSupport) { - // default read/draw buffers, may utilize GLContext/GLDrawable override of + // default read/draw buffers, may utilize GLContext/GLDrawable override of // GLContext.getDefaultDrawFramebuffer() and GLContext.getDefaultReadFramebuffer() - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, 0); - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, 0); } else { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); // default draw buffer + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); // default draw buffer } bound = false; } } - + /** * Method simply marks this FBO unbound w/o interfering w/ the bound framebuffer as perfomed by {@link #unbind(GL)}. * <p> @@ -1972,154 +2546,168 @@ public class FBObject { bound = false; } - /** + /** * Returns <code>true</code> if framebuffer object is bound via {@link #bind(GL)}, otherwise <code>false</code>. * <p> * Method verifies the bound state via {@link GL#getBoundFramebuffer(int)}. * </p> * @param gl the current GL context */ - public final boolean isBound(GL gl) { - bound = bound && fbName != gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER) ; + public final boolean isBound(final GL gl) { + bound = bound && fbName == gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER) ; return bound; } - + /** Returns <code>true</code> if framebuffer object is bound via {@link #bind(GL)}, otherwise <code>false</code>. */ public final boolean isBound() { return bound; } - - /** + + /** * If multisampling is being used and flagged dirty by a previous call of {@link #bind(GL)} or after initialization, - * the msaa-buffers are sampled to it's sink {@link #getSamplingSink()}. + * the msaa-buffers are sampled to it's sink {@link #getSamplingTextureSink()}. + * <p> + * Method also resets the sampling sink configuration via {@link #resetSamplingSink(GL)} if used and required. + * </p> * <p> - * Method also updates the sampling sink configuration (if used). Hence it is recommended to call this - * method after your have initialized the FBO and attached renderbuffer etc for the 1st time. * Method is called automatically by {@link #use(GL, TextureAttachment)}. * </p> * <p> - * Methos always resets the framebuffer binding to default in the end. - * If full FBO is supported, sets the read and write framebuffer individually to default after sampling, hence not disturbing + * Method always resets the framebuffer binding to default in the end. + * If full FBO is supported, sets the read and write framebuffer individually to default after sampling, hence not disturbing * an optional operating MSAA FBO, see {@link GLBase#getDefaultReadFramebuffer()} and {@link GLBase#getDefaultDrawFramebuffer()} * </p> * <p> * In case you use this FBO w/o the {@link GLFBODrawable} and intend to employ {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)} - * you may want to call {@link GL#glBindFramebuffer(int, int) glBindFramebuffer}({@link GL2GL3#GL_READ_FRAMEBUFFER}, {@link #getReadFramebuffer()}); + * you may want to call {@link GL#glBindFramebuffer(int, int) glBindFramebuffer}({@link GL2ES3#GL_READ_FRAMEBUFFER}, {@link #getReadFramebuffer()}); * </p> * <p>Leaves the FBO unbound.</p> - * + * * @param gl the current GL context * @param ta {@link TextureAttachment} to use, prev. attached w/ {@link #attachTexture2D(GL, int, boolean, int, int, int, int) attachTexture2D(..)} - * @throws IllegalArgumentException + * @throws IllegalArgumentException */ - public final void syncFramebuffer(GL gl) { + public final void syncSamplingSink(final GL gl) { markUnbound(); - if(samples>0 && samplesSinkDirty) { - samplesSinkDirty = false; - resetMSAATexture2DSink(gl); - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, fbName); - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, samplesSink.getWriteFramebuffer()); - ((GL2GL3)gl).glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, // since MSAA is supported, casting to GL2GL3 is OK + if(samples>0 && samplingSinkDirty) { // implies fullFBOSupport + samplingSinkDirty = false; + if( isModified() ) { + resetSamplingSink(gl); + } + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, fbName); // read from this MSAA fb + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, samplingSink.getWriteFramebuffer()); // write to sampling sink + ((GL2ES3)gl).glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, // since MSAA is supported, casting to GL2ES3 is OK GL.GL_COLOR_BUFFER_BIT, GL.GL_NEAREST); + if( checkError ) { + checkNoError(null, gl.glGetError(), "FBObject syncSampleSink"); // throws GLException if error + } + } else { + modified = false; } if(fullFBOSupport) { - // default read/draw buffers, may utilize GLContext/GLDrawable override of + // default read/draw buffers, may utilize GLContext/GLDrawable override of // GLContext.getDefaultDrawFramebuffer() and GLContext.getDefaultReadFramebuffer() - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, 0); - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, 0); } else { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); // default draw buffer + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); // default draw buffer } } - - /** - * Bind the given texture colorbuffer. - * - * <p>If using multiple texture units, ensure you call {@link GL#glActiveTexture(int)} first!</p> - * - * <p>{@link #syncFramebuffer(GL)} is being called</p> - * + + /** + * {@link #syncSamplingSink(GL) Synchronize the sampling sink} and bind the given {@link TextureAttachment}, if not <code>null</code>. + * + * <p>If using a {@link TextureAttachment} and multiple texture units, ensure you call {@link GL#glActiveTexture(int)} first!</p> + * + * <p>{@link #syncSamplingSink(GL)} is being called</p> + * * <p>Leaves the FBO unbound!</p> - * + * * @param gl the current GL context - * @param ta {@link TextureAttachment} to use, prev. attached w/ {@link #attachTexture2D(GL, int, boolean, int, int, int, int) attachTexture2D(..)} - * @throws IllegalArgumentException + * @param ta {@link TextureAttachment} to use, prev. attached w/ {@link #attachTexture2D(GL, int, boolean, int, int, int, int) attachTexture2D(..)}, + * may be <code>null</code> in case no {@link TextureAttachment} is used. + * @throws IllegalArgumentException */ - public final void use(GL gl, TextureAttachment ta) throws IllegalArgumentException { - if(null == ta) { throw new IllegalArgumentException("null TextureAttachment, this: "+toString()); } - syncFramebuffer(gl); - gl.glBindTexture(GL.GL_TEXTURE_2D, ta.getName()); // use it .. + public final void use(final GL gl, final TextureAttachment ta) throws IllegalArgumentException { + syncSamplingSink(gl); + if( null != ta ) { + gl.glBindTexture(GL.GL_TEXTURE_2D, ta.getName()); // use it .. + } } - /** + /** * Unbind texture, ie bind 'non' texture 0 - * + * * <p>Leaves the FBO unbound.</p> - */ - public final void unuse(GL gl) { + */ + public final void unuse(final GL gl) { unbind(gl); gl.glBindTexture(GL.GL_TEXTURE_2D, 0); // don't use it } - /** @see GL#hasFullFBOSupport() */ + /** @see GL#hasFullFBOSupport() */ public final boolean hasFullFBOSupport() throws GLException { checkInitialized(); return this.fullFBOSupport; } - - /** + + /** * Returns <code>true</code> if renderbuffer accepts internal format {@link GL#GL_RGB8} and {@link GL#GL_RGBA8}, otherwise <code>false</code>. * @throws GLException if {@link #init(GL)} hasn't been called. */ public final boolean supportsRGBA8() throws GLException { checkInitialized(); return rgba8Avail; } - - /** + + /** * Returns <code>true</code> if {@link GL#GL_DEPTH_COMPONENT16}, {@link GL#GL_DEPTH_COMPONENT24} or {@link GL#GL_DEPTH_COMPONENT32} is supported, otherwise <code>false</code>. * @param bits 16, 24 or 32 bits * @throws GLException if {@link #init(GL)} hasn't been called. */ - public final boolean supportsDepth(int bits) throws GLException { + public final boolean supportsDepth(final int bits) throws GLException { checkInitialized(); switch(bits) { - case 16: return true; + case 16: return true; case 24: return depth24Avail; case 32: return depth32Avail; - default: return false; + default: return false; } } - - /** + + /** * Returns <code>true</code> if {@link GL#GL_STENCIL_INDEX1}, {@link GL#GL_STENCIL_INDEX4}, {@link GL#GL_STENCIL_INDEX8} or {@link GL2GL3#GL_STENCIL_INDEX16} is supported, otherwise <code>false</code>. * @param bits 1, 4, 8 or 16 bits * @throws GLException if {@link #init(GL)} hasn't been called. */ - public final boolean supportsStencil(int bits) throws GLException { + public final boolean supportsStencil(final int bits) throws GLException { checkInitialized(); switch(bits) { - case 1: return stencil01Avail; + case 1: return stencil01Avail; case 4: return stencil04Avail; case 8: return stencil08Avail; case 16: return stencil16Avail; - default: return false; + default: return false; } } - - /** + + /** * Returns <code>true</code> if {@link GL#GL_DEPTH24_STENCIL8} is supported, otherwise <code>false</code>. * @throws GLException if {@link #init(GL)} hasn't been called. */ public final boolean supportsPackedDepthStencil() throws GLException { checkInitialized(); return packedDepthStencilAvail; } - + /** * Returns the maximum number of colorbuffer attachments. * @throws GLException if {@link #init(GL)} hasn't been called. */ public final int getMaxColorAttachments() throws GLException { checkInitialized(); return maxColorAttachments; } - + public final int getMaxTextureSize() throws GLException { checkInitialized(); return this.maxTextureSize; } public final int getMaxRenderbufferSize() throws GLException { checkInitialized(); return this.maxRenderbufferSize; } - + /** @see GL#getMaxRenderbufferSamples() */ public final int getMaxSamples() throws GLException { checkInitialized(); return this.maxSamples; } - + /** - * Returns <code>true</code> if this instance has been initialized with {@link #reset(GL, int, int)} - * or {@link #reset(GL, int, int, int)}, otherwise <code>false</code> + * Returns <code>true</code> if this instance has been initialized with {@link #reset(GL, int, int)} + * or {@link #reset(GL, int, int, int, boolean)}, otherwise <code>false</code> */ public final boolean isInitialized() { return initialized; } /** Returns the width */ @@ -2131,41 +2719,60 @@ public class FBObject { /** Returns the framebuffer name to render to. */ public final int getWriteFramebuffer() { return fbName; } /** Returns the framebuffer name to read from. Depending on multisampling, this may be a different framebuffer. */ - public final int getReadFramebuffer() { return ( samples > 0 ) ? samplesSink.getReadFramebuffer() : fbName; } - /** Return the number of color/texture attachments */ - public final int getColorAttachmentCount() { return colorAttachmentCount; } - /** Return the stencil {@link RenderAttachment} attachment, if exist. Maybe share the same {@link Attachment#getName()} as {@link #getDepthAttachment()}, if packed depth-stencil is being used. */ + public final int getReadFramebuffer() { + return 0 < samples ? ( null != samplingSink ? samplingSink.getReadFramebuffer() : 0 ) : fbName; + } + public final int getDefaultReadBuffer() { return GL.GL_COLOR_ATTACHMENT0; } + /** Return the number of attached {@link Colorbuffer}s */ + public final int getColorbufferCount() { return colorbufferCount; } + /** Return the number of attached {@link TextureAttachment}s */ + public final int getTextureAttachmentCount() { return textureAttachmentCount; } + /** Return the stencil {@link RenderAttachment} attachment, if exist. Maybe share the same {@link Attachment#getName()} as {@link #getDepthAttachment()}, if packed depth-stencil is being used. */ public final RenderAttachment getStencilAttachment() { return stencil; } - /** Return the depth {@link RenderAttachment} attachment. Maybe share the same {@link Attachment#getName()} as {@link #getStencilAttachment()}, if packed depth-stencil is being used. */ + /** Return the depth {@link RenderAttachment} attachment. Maybe share the same {@link Attachment#getName()} as {@link #getStencilAttachment()}, if packed depth-stencil is being used. */ public final RenderAttachment getDepthAttachment() { return depth; } - - /** Return the complete multisampling {@link FBObject} sink, if using multisampling. */ - public final FBObject getSamplingSinkFBO() { return samplesSink; } - - /** Return the multisampling {@link TextureAttachment} sink, if using multisampling. */ - public final TextureAttachment getSamplingSink() { return samplesSinkTexture; } - /** - * Returns <code>true</code> if the multisampling colorbuffer (msaa-buffer) + + /** Return the complete multisampling {@link FBObject} sink, if using multisampling. */ + public final FBObject getSamplingSinkFBO() { return samplingSink; } + + /** Return the multisampling {@link Colorbuffer} sink, if using multisampling. */ + public final Colorbuffer getSamplingSink() { return samplingColorSink; } + + /** + * Returns <code>true</code> if the multisampling colorbuffer (msaa-buffer) * has been flagged dirty by a previous call of {@link #bind(GL)}, * otherwise <code>false</code>. */ - public final boolean isSamplingBufferDirty() { return samplesSinkDirty; } - + public final boolean isSamplingBufferDirty() { return samplingSinkDirty; } + + /** + * Returns <code>true</code> if size, sample-count or any attachment of this instance + * or its {@link #getSamplingSink() sampling-sink} has been modified since last {@link #syncSamplingSink(GL) sync}, + * {@link #use(GL, TextureAttachment) use}, {@link #reset(GL, int, int, int) reset} + * or {@link #resetSamplingSink(GL) resetSamplingSink}. + * <p> + * Otherwise method returns <code>false</code>. + * </p> + */ + public final boolean isModified() { return modified || ( null != samplingSink && samplingSink.modified ); } + int objectHashCode() { return super.hashCode(); } - + + @Override public final String toString() { - final String caps = null != colorAttachmentPoints ? Arrays.asList(colorAttachmentPoints).toString() : null ; - return "FBO[name r/w "+fbName+"/"+getReadFramebuffer()+", init "+initialized+", bound "+bound+", size "+width+"x"+height+", samples "+samples+"/"+maxSamples+ - ", depth "+depth+", stencil "+stencil+", color attachments: "+colorAttachmentCount+"/"+maxColorAttachments+ - ": "+caps+", msaa-sink "+samplesSinkTexture+", isSamplesSink "+(null == samplesSink)+ - ", obj 0x"+Integer.toHexString(objectHashCode())+"]"; - } - - private final void updateStatus(GL gl) { + final String caps = null != colorbufferAttachments ? Arrays.asList(colorbufferAttachments).toString() : null ; + return "FBO[name r/w "+fbName+"/"+getReadFramebuffer()+", init "+initialized+", bound "+bound+", size "+width+"x"+height+ + ", samples "+samples+"/"+maxSamples+", modified "+modified+"/"+isModified()+", depth "+depth+", stencil "+stencil+ + ", colorbuffer attachments: "+colorbufferCount+"/"+maxColorAttachments+", with "+textureAttachmentCount+" textures"+ + ": "+caps+", msaa["+samplingColorSink+", hasSink "+(null != samplingSink)+ + ", dirty "+samplingSinkDirty+"], state "+getStatusString()+", obj "+toHexString(objectHashCode())+"]"; + } + + private final void updateStatus(final GL gl) { if( 0 == fbName ) { vStatus = -1; } else { vStatus = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); } - } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/FloatUtil.java b/src/jogl/classes/com/jogamp/opengl/FloatUtil.java deleted file mode 100644 index 93543eaf6..000000000 --- a/src/jogl/classes/com/jogamp/opengl/FloatUtil.java +++ /dev/null @@ -1,294 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl; - -import java.nio.FloatBuffer; - -/** - * Basic Float math utility functions. - * <p> - * Implementation assumes linear matrix layout in column-major order - * matching OpenGL's implementation. - * </p> - * <p> - * Derived from ProjectFloat.java - Created 11-jan-2004 - * </p> - * - * @author Erik Duijs - * @author Kenneth Russell - * @author Sven Gothel - */ -public class FloatUtil { - private static final float[] IDENTITY_MATRIX = - new float[] { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - - private static final float[] ZERO_MATRIX = - new float[] { - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f }; - - /** - * Make matrix an identity matrix - */ - public static final void makeIdentityf(float[] m, int offset) { - for (int i = 0; i < 16; i++) { - m[i+offset] = IDENTITY_MATRIX[i]; - } - } - - /** - * Make matrix an identity matrix - */ - public static final void makeIdentityf(FloatBuffer m) { - int oldPos = m.position(); - m.put(IDENTITY_MATRIX); - m.position(oldPos); - } - - /** - * Make matrix an zero matrix - */ - public static final void makeZero(float[] m, int offset) { - for (int i = 0; i < 16; i++) { - m[i+offset] = 0; - } - } - - /** - * Make matrix an zero matrix - */ - public static final void makeZero(FloatBuffer m) { - int oldPos = m.position(); - m.put(ZERO_MATRIX); - m.position(oldPos); - } - - /** - * @param a 4x4 matrix in column-major order - * @param b 4x4 matrix in column-major order - * @param d result a*b in column-major order - */ - public static final void multMatrixf(final float[] a, int a_off, final float[] b, int b_off, float[] d, int d_off) { - for (int i = 0; i < 4; i++) { - // one row in column-major order - final float ai0=a[a_off+i+0*4], ai1=a[a_off+i+1*4], ai2=a[a_off+i+2*4], ai3=a[a_off+i+3*4]; // row-i of a - d[d_off+i+0*4] = ai0 * b[b_off+0+0*4] + ai1 * b[b_off+1+0*4] + ai2 * b[b_off+2+0*4] + ai3 * b[b_off+3+0*4] ; - d[d_off+i+1*4] = ai0 * b[b_off+0+1*4] + ai1 * b[b_off+1+1*4] + ai2 * b[b_off+2+1*4] + ai3 * b[b_off+3+1*4] ; - d[d_off+i+2*4] = ai0 * b[b_off+0+2*4] + ai1 * b[b_off+1+2*4] + ai2 * b[b_off+2+2*4] + ai3 * b[b_off+3+2*4] ; - d[d_off+i+3*4] = ai0 * b[b_off+0+3*4] + ai1 * b[b_off+1+3*4] + ai2 * b[b_off+2+3*4] + ai3 * b[b_off+3+3*4] ; - } - } - - /** - * @param a 4x4 matrix in column-major order - * @param b 4x4 matrix in column-major order - * @param d result a*b in column-major order - */ - public static final void multMatrixf(final float[] a, int a_off, final float[] b, int b_off, FloatBuffer d) { - final int dP = d.position(); - for (int i = 0; i < 4; i++) { - // one row in column-major order - final float ai0=a[a_off+i+0*4], ai1=a[a_off+i+1*4], ai2=a[a_off+i+2*4], ai3=a[a_off+i+3*4]; // row-i of a - d.put(dP+i+0*4 , ai0 * b[b_off+0+0*4] + ai1 * b[b_off+1+0*4] + ai2 * b[b_off+2+0*4] + ai3 * b[b_off+3+0*4] ); - d.put(dP+i+1*4 , ai0 * b[b_off+0+1*4] + ai1 * b[b_off+1+1*4] + ai2 * b[b_off+2+1*4] + ai3 * b[b_off+3+1*4] ); - d.put(dP+i+2*4 , ai0 * b[b_off+0+2*4] + ai1 * b[b_off+1+2*4] + ai2 * b[b_off+2+2*4] + ai3 * b[b_off+3+2*4] ); - d.put(dP+i+3*4 , ai0 * b[b_off+0+3*4] + ai1 * b[b_off+1+3*4] + ai2 * b[b_off+2+3*4] + ai3 * b[b_off+3+3*4] ); - } - } - - /** - * @param a 4x4 matrix in column-major order - * @param b 4x4 matrix in column-major order - * @param d result a*b in column-major order - */ - public static final void multMatrixf(final FloatBuffer a, final float[] b, int b_off, FloatBuffer d) { - final int aP = a.position(); - final int dP = d.position(); - for (int i = 0; i < 4; i++) { - // one row in column-major order - final float ai0=a.get(aP+i+0*4), ai1=a.get(aP+i+1*4), ai2=a.get(aP+i+2*4), ai3=a.get(aP+i+3*4); // row-i of a - d.put(dP+i+0*4 , ai0 * b[b_off+0+0*4] + ai1 * b[b_off+1+0*4] + ai2 * b[b_off+2+0*4] + ai3 * b[b_off+3+0*4] ); - d.put(dP+i+1*4 , ai0 * b[b_off+0+1*4] + ai1 * b[b_off+1+1*4] + ai2 * b[b_off+2+1*4] + ai3 * b[b_off+3+1*4] ); - d.put(dP+i+2*4 , ai0 * b[b_off+0+2*4] + ai1 * b[b_off+1+2*4] + ai2 * b[b_off+2+2*4] + ai3 * b[b_off+3+2*4] ); - d.put(dP+i+3*4 , ai0 * b[b_off+0+3*4] + ai1 * b[b_off+1+3*4] + ai2 * b[b_off+2+3*4] + ai3 * b[b_off+3+3*4] ); - } - } - - /** - * @param a 4x4 matrix in column-major order - * @param b 4x4 matrix in column-major order - * @param d result a*b in column-major order - */ - public static final void multMatrixf(final FloatBuffer a, final FloatBuffer b, FloatBuffer d) { - final int aP = a.position(); - final int bP = b.position(); - final int dP = d.position(); - for (int i = 0; i < 4; i++) { - // one row in column-major order - final float ai0=a.get(aP+i+0*4), ai1=a.get(aP+i+1*4), ai2=a.get(aP+i+2*4), ai3=a.get(aP+i+3*4); // row-i of a - d.put(dP+i+0*4 , ai0 * b.get(bP+0+0*4) + ai1 * b.get(bP+1+0*4) + ai2 * b.get(bP+2+0*4) + ai3 * b.get(bP+3+0*4) ); - d.put(dP+i+1*4 , ai0 * b.get(bP+0+1*4) + ai1 * b.get(bP+1+1*4) + ai2 * b.get(bP+2+1*4) + ai3 * b.get(bP+3+1*4) ); - d.put(dP+i+2*4 , ai0 * b.get(bP+0+2*4) + ai1 * b.get(bP+1+2*4) + ai2 * b.get(bP+2+2*4) + ai3 * b.get(bP+3+2*4) ); - d.put(dP+i+3*4 , ai0 * b.get(bP+0+3*4) + ai1 * b.get(bP+1+3*4) + ai2 * b.get(bP+2+3*4) + ai3 * b.get(bP+3+3*4) ); - } - } - - - /** - * Normalize vector - * - * @param v makes len(v)==1 - */ - public static final void normalize(float[] v) { - float r = (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - - if ( r == 0.0 || r == 1.0) { - return; - } - - r = 1.0f / r; - - v[0] *= r; - v[1] *= r; - v[2] *= r; - } - - /** - * Normalize vector - * - * @param v makes len(v)==1 - */ - public static final void normalize(FloatBuffer v) { - final int vPos = v.position(); - - float r = (float) Math.sqrt(v.get(0+vPos) * v.get(0+vPos) + - v.get(1+vPos) * v.get(1+vPos) + - v.get(2+vPos) * v.get(2+vPos)); - - if ( r == 0.0 || r == 1.0) { - return; - } - - r = 1.0f / r; - - v.put(0+vPos, v.get(0+vPos) * r); - v.put(1+vPos, v.get(1+vPos) * r); - v.put(2+vPos, v.get(2+vPos) * r); - } - - - /** - * Calculate cross-product of 2 vector - * - * @param v1 3-component vector - * @param v2 3-component vector - * @param result v1 X v2 - */ - public static final void cross(float[] v1, float[] v2, float[] result) { - result[0] = v1[1] * v2[2] - v1[2] * v2[1]; - result[1] = v1[2] * v2[0] - v1[0] * v2[2]; - result[2] = v1[0] * v2[1] - v1[1] * v2[0]; - } - - /** - * Calculate cross-product of 2 vector - * - * @param v1 3-component vector - * @param v2 3-component vector - * @param result v1 X v2 - */ - public static final void cross(FloatBuffer v1, FloatBuffer v2, FloatBuffer result) { - final int v1Pos = v1.position(); - final int v2Pos = v2.position(); - final int rPos = result.position(); - - result.put(0+rPos, v1.get(1+v1Pos) * v2.get(2+v2Pos) - v1.get(2+v1Pos) * v2.get(1+v2Pos)); - result.put(1+rPos, v1.get(2+v1Pos) * v2.get(0+v2Pos) - v1.get(0+v1Pos) * v2.get(2+v2Pos)); - result.put(2+rPos, v1.get(0+v1Pos) * v2.get(1+v2Pos) - v1.get(1+v1Pos) * v2.get(0+v2Pos)); - } - - /** - * @param m_in 4x4 matrix in column-major order - * @param m_in_off - * @param v_in 4-component column-vector - * @param v_out m_in * v_in - */ - public static final void multMatrixVecf(float[] m_in, int m_in_off, float[] v_in, int v_in_off, float[] v_out) { - for (int i = 0; i < 4; i++) { - // (one matrix row in column-major order) X (column vector) - v_out[i] = - v_in[0+v_in_off] * m_in[0*4+i+m_in_off] + - v_in[1+v_in_off] * m_in[1*4+i+m_in_off] + - v_in[2+v_in_off] * m_in[2*4+i+m_in_off] + - v_in[3+v_in_off] * m_in[3*4+i+m_in_off]; - } - } - - /** - * @param m_in 4x4 matrix in column-major order - * @param m_in_off - * @param v_in 4-component column-vector - * @param v_out m_in * v_in - */ - public static final void multMatrixVecf(float[] m_in, float[] v_in, float[] v_out) { - for (int i = 0; i < 4; i++) { - // (one matrix row in column-major order) X (column vector) - v_out[i] = - v_in[0] * m_in[0*4+i] + - v_in[1] * m_in[1*4+i] + - v_in[2] * m_in[2*4+i] + - v_in[3] * m_in[3*4+i]; - } - } - - /** - * @param m_in 4x4 matrix in column-major order - * @param v_in 4-component column-vector - * @param v_out m_in * v_in - */ - public static final void multMatrixVecf(FloatBuffer m_in, FloatBuffer v_in, FloatBuffer v_out) { - int inPos = v_in.position(); - int outPos = v_out.position(); - int matrixPos = m_in.position(); - for (int i = 0; i < 4; i++) { - // (one matrix row in column-major order) X (column vector) - v_out.put(i + outPos, - v_in.get(0+inPos) * m_in.get(0*4+i+matrixPos) + - v_in.get(1+inPos) * m_in.get(1*4+i+matrixPos) + - v_in.get(2+inPos) * m_in.get(2*4+i+matrixPos) + - v_in.get(3+inPos) * m_in.get(3*4+i+matrixPos)); - } - } - -}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java index 38a8deef8..b9f1fb10c 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.opengl; import javax.media.nativewindow.AbstractGraphicsDevice; @@ -36,7 +36,9 @@ import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; +import javax.media.opengl.GLSharedContextSetter; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; @@ -48,24 +50,40 @@ import jogamp.opengl.GLDrawableImpl; /** * Fully functional {@link GLAutoDrawable} implementation - * utilizing already created created {@link GLDrawable} and {@link GLContext} instances. + * utilizing already created {@link GLDrawable} and {@link GLContext} instances. * <p> * Since no native windowing system events are being processed, it is recommended - * to handle at least: + * to handle at least the {@link com.jogamp.newt.event.WindowEvent window events}: * <ul> * <li>{@link com.jogamp.newt.event.WindowListener#windowRepaint(com.jogamp.newt.event.WindowUpdateEvent) repaint} using {@link #windowRepaintOp()}</li> * <li>{@link com.jogamp.newt.event.WindowListener#windowResized(com.jogamp.newt.event.WindowEvent) resize} using {@link #windowResizedOp()}</li> - * <li>{@link com.jogamp.newt.event.WindowListener#windowDestroyNotify(com.jogamp.newt.event.WindowEvent) destroy-notify} using {@link #windowDestroyNotifyOp()}</li> - * </ul> + * </ul> + * and setup a {@link com.jogamp.newt.Window#setWindowDestroyNotifyAction(Runnable) custom toolkit destruction} issuing {@link #windowDestroyNotifyOp()}. * </p> - * <p> + * <p> * See example {@link com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateNEWT TestGLAutoDrawableDelegateNEWT}. * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAutoDrawable { /** - * @param drawable a valid and already realized {@link GLDrawable} - * @param context a valid {@link GLContext}, may not be made current (created) yet. + * <p> + * The {@link GLContext} can be assigned later manually via {@link GLAutoDrawable#setContext(GLContext, boolean) setContext(ctx)} + * <i>or</i> it will be created <i>lazily</i> at the 1st {@link GLAutoDrawable#display() display()} method call.<br> + * <i>Lazy</i> {@link GLContext} creation will take a shared {@link GLContext} into account + * which has been set {@link #setSharedContext(GLContext) directly} + * or {@link #setSharedAutoDrawable(GLAutoDrawable) via another GLAutoDrawable}. + * </p> + * @param drawable a valid {@link GLDrawable}, may not be {@link GLDrawable#isRealized() realized} yet. + * @param context a valid {@link GLContext}, + * may not have been made current (created) yet, + * may not be associated w/ <code>drawable<code> yet, + * may be <code>null</code> for lazy initialization at 1st {@link #display()}. * @param upstreamWidget optional UI element holding this instance, see {@link #getUpstreamWidget()}. * @param ownDevice pass <code>true</code> if {@link AbstractGraphicsDevice#close()} shall be issued, * otherwise pass <code>false</code>. Closing the device is required in case @@ -73,21 +91,15 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto * and no further lifecycle handling is applied. * @param lock optional custom {@link RecursiveLock}. */ - public GLAutoDrawableDelegate(GLDrawable drawable, GLContext context, Object upstreamWidget, boolean ownDevice, RecursiveLock lock) { + public GLAutoDrawableDelegate(final GLDrawable drawable, final GLContext context, final Object upstreamWidget, final boolean ownDevice, final RecursiveLock lock) { super((GLDrawableImpl)drawable, (GLContextImpl)context, ownDevice); if(null == drawable) { throw new IllegalArgumentException("null drawable"); } - if(null == context) { - throw new IllegalArgumentException("null context"); - } - if(!drawable.isRealized()) { - throw new IllegalArgumentException("drawable not realized"); - } this.upstreamWidget = upstreamWidget; this.lock = ( null != lock ) ? lock : LockFactory.createRecursiveLock() ; } - + // // expose default methods // @@ -96,48 +108,63 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto public final void windowRepaintOp() { super.defaultWindowRepaintOp(); } - - /** Implementation to handle resize events from the windowing system. All required locks are being claimed. */ - public final void windowResizedOp(int newWidth, int newHeight) { + + /** + * Handling resize events from the windowing system. + * <p> + * Implementation: + * <ul> + * <li>resizes {@link #getDelegatedDrawable() the GLDrawable}, if offscreen,</li> + * <li>triggers a pending {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape events}, and</li> + * <li>issues a {@link #display()} call, if no animator is present.</li> + * </ul> + * </p> + * <p> + * All required locks are being claimed. + * </p> + * @param newWidth new width in pixel units + * @param newWidth new height in pixel units + */ + public final void windowResizedOp(final int newWidth, final int newHeight) { super.defaultWindowResizedOp(newWidth, newHeight); } - - /** + + /** * Implementation to handle destroy notifications from the windowing system. - * + * * <p> - * If the {@link NativeSurface} does not implement {@link WindowClosingProtocol} + * If the {@link NativeSurface} does not implement {@link WindowClosingProtocol} * or {@link WindowClosingMode#DISPOSE_ON_CLOSE} is enabled (default), * a thread safe destruction is being induced. - * </p> + * </p> */ public final void windowDestroyNotifyOp() { super.defaultWindowDestroyNotifyOp(); } - + // // Complete GLAutoDrawable // - + private Object upstreamWidget; private final RecursiveLock lock; - + @Override - protected final RecursiveLock getLock() { return lock; } - + public final RecursiveLock getUpstreamLock() { return lock; } + @Override public final Object getUpstreamWidget() { return upstreamWidget; } - + /** * Set the upstream UI toolkit object. * @see #getUpstreamWidget() */ - public final void setUpstreamWidget(Object newUpstreamWidget) { + public final void setUpstreamWidget(final Object newUpstreamWidget) { upstreamWidget = newUpstreamWidget; } - + /** * {@inheritDoc} * <p> @@ -145,7 +172,7 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto * </p> * <p> * User still needs to destroy the upstream window, which details are hidden from this aspect. - * This can be performed by overriding {@link #destroyImplInLock()}. + * This can be performed by overriding {@link #destroyImplInLock()}. * </p> */ @Override @@ -157,33 +184,29 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto protected void destroyImplInLock() { super.destroyImplInLock(); } - + @Override - public void display() { + public void display() { defaultDisplay(); } - + // // GLDrawable delegation // - + @Override public final GLDrawableFactory getFactory() { return drawable.getFactory(); } - - @Override - public final void setRealized(boolean realized) { - } @Override public final void swapBuffers() throws GLException { defaultSwapBuffers(); } - + @Override public String toString() { return getClass().getSimpleName()+"[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + ", \n\tContext: " + context + ", \n\tUpstreamWidget: "+upstreamWidget+ /** ", \n\tFactory: "+factory+ */ "]"; - } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java new file mode 100644 index 000000000..bfd5fe115 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java @@ -0,0 +1,552 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +import java.util.ArrayList; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.DefaultGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable; + +import jogamp.opengl.Debug; + +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.nativewindow.MutableGraphicsConfiguration; +import com.jogamp.opengl.util.GLDrawableUtil; + +/** + * GLEventListenerState is holding {@link GLAutoDrawable} components crucial + * to relocating all its {@link GLEventListener} w/ their operating {@link GLContext}, etc. + * The components are: + * <ul> + * <li>{@link GLContext}</li> + * <li>All {@link GLEventListener}, incl. their init state</li> + * <li>{@link GLAnimatorControl}</li> + * <!--li>{@link GLCapabilitiesImmutable} for compatibility check</li--> + * <li>{@link AbstractGraphicsDevice} for compatibility check and preserving the native device handle incl. ownership</li> + * </ul> + * <p> + * A GLEventListenerState instance can be created while components are {@link #moveFrom(GLAutoDrawable) moved from} a {@link GLAutoDrawable} + * to the new instance, which gains {@link #isOwner() ownership} of the moved components. + * </p> + * <p> + * A GLEventListenerState instance's components can be {@link #moveTo(GLAutoDrawable) moved to} a {@link GLAutoDrawable}, + * while loosing {@link #isOwner() ownership} of the moved components. + * </p> + */ +public class GLEventListenerState { + private static final boolean DEBUG = Debug.debug("GLDrawable") || Debug.debug("GLEventListenerState"); + + private GLEventListenerState(final AbstractGraphicsDevice upstreamDevice, final boolean proxyOwnsUpstreamDevice, final AbstractGraphicsDevice device, + final GLCapabilitiesImmutable caps, + final RecursiveLock upstreamLock, final NativeSurface lockedSurface, + final GLContext context, final int count, final GLAnimatorControl anim, final boolean animStarted) { + this.upstreamDevice = upstreamDevice; + this.proxyOwnsUpstreamDevice = proxyOwnsUpstreamDevice; + this.device = device; + this.caps = caps; + this.upstreamLock = upstreamLock; + this.lockedSurface = lockedSurface; + this.context = context; + this.listeners = new GLEventListener[count]; + this.listenersInit = new boolean[count]; + this.anim = anim; + this.animStarted = animStarted; + + this.owner = true; + } + /** + * Returns <code>true</code>, if this instance is the current owner of the components, + * otherwise <code>false</code>. + * <p> + * Ownership is lost if {@link #moveTo(GLAutoDrawable)} is being called successfully + * and all components are transferred to the new {@link GLAutoDrawable}. + * </p> + */ + public final boolean isOwner() { return owner; } + + public final int listenerCount() { return listeners.length; } + + public final AbstractGraphicsDevice upstreamDevice; + public final boolean proxyOwnsUpstreamDevice; + public final AbstractGraphicsDevice device; + public final GLCapabilitiesImmutable caps; + public final GLContext context; + public final GLEventListener[] listeners; + public final boolean[] listenersInit; + public final GLAnimatorControl anim; + public final boolean animStarted; + + private volatile RecursiveLock upstreamLock; + private volatile NativeSurface lockedSurface; + private volatile boolean owner; + + /** + * Returns a {@link Runnable} {@link NativeSurface#unlockSurface() unlocking} an eventually locked {@link NativeSurface}, + * see {@link #moveFrom(GLAutoDrawable, boolean)} and {@link #moveTo(GLAutoDrawable, Runnable)}. + */ + public Runnable getUnlockSurfaceOp() { return unlockOp; } + + private final Runnable unlockOp = new Runnable() { + public void run() { + final RecursiveLock rl = upstreamLock; + final NativeSurface ls = lockedSurface; + upstreamLock = null; + lockedSurface = null; + if( null != rl ) { + rl.unlock(); + } + if( null != ls ) { + ls.unlockSurface(); + } + } }; + + /** + * Last resort to destroy and loose ownership + */ + public void destroy() { + if( owner ) { + final int aSz = listenerCount(); + for(int i=0; i<aSz; i++) { + listeners[i] = null; + } + // context.destroy(); - NPE (null drawable) + unlockOp.run(); + device.close(); + owner = false; + } + } + + private static AbstractGraphicsDevice cloneDevice(final AbstractGraphicsDevice aDevice) { + return (AbstractGraphicsDevice) aDevice.clone(); + } + + /** + * Moves all GLEventListenerState components from the given {@link GLAutoDrawable} + * to a newly created instance. + * <p> + * Note that all components are removed from the {@link GLAutoDrawable}, + * i.e. the {@link GLContext}, all {@link GLEventListener}. + * </p> + * <p> + * If the {@link GLAutoDrawable} was added to a {@link GLAnimatorControl}, it is removed + * and the {@link GLAnimatorControl} added to the GLEventListenerState. + * </p> + * <p> + * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * + * @param src {@link GLAutoDrawable} source to move components from + * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. + * + * @see #moveTo(GLAutoDrawable) + */ + public static GLEventListenerState moveFrom(final GLAutoDrawable src) { + return GLEventListenerState.moveFrom(src, false); + } + + /** + * Moves all GLEventListenerState components from the given {@link GLAutoDrawable} + * to a newly created instance. + * <p> + * Note that all components are removed from the {@link GLAutoDrawable}, + * i.e. the {@link GLContext}, all {@link GLEventListener}. + * </p> + * <p> + * If the {@link GLAutoDrawable} was added to a {@link GLAnimatorControl}, it is removed + * and the {@link GLAnimatorControl} added to the GLEventListenerState. + * </p> + * <p> + * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}, + * which is <i>not released</i> if <code>keepLocked</code> is <code>true</code>. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> + * <code>keepLocked</code> may be utilized if swapping a context between drawables + * and to ensure atomicity of operation. + * Here, the {@link #getUnlockSurfaceOp()} shall be passed to {@link #moveTo(GLAutoDrawable, Runnable)}. + * See {@link GLDrawableUtil#swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}. + * </p> + * + * @param src {@link GLAutoDrawable} source to move components from + * @param keepLocked keep {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface} locked, see above + * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. + * + * @see #moveTo(GLAutoDrawable, Runnable) + */ + public static GLEventListenerState moveFrom(final GLAutoDrawable src, final boolean keepLocked) { + final GLAnimatorControl srcAnim = src.getAnimator(); + final boolean srcAnimStarted; + if( null != srcAnim ) { + srcAnimStarted = srcAnim.isStarted(); + srcAnim.remove(src); // also handles ECT + } else { + srcAnimStarted = false; + } + + final GLEventListenerState glls; + final RecursiveLock srcUpstreamLock = src.getUpstreamLock(); + srcUpstreamLock.lock(); + try { + final NativeSurface srcSurface = src.getNativeSurface(); + final boolean srcSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < srcSurface.lockSurface(); + if( src.isRealized() && !srcSurfaceLocked ) { + throw new GLException("Could not lock realized surface "+src); + } + + try { + final int aSz = src.getGLEventListenerCount(); + + // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable + // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device! + final AbstractGraphicsConfiguration aCfg = srcSurface.getGraphicsConfiguration(); + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); + final AbstractGraphicsDevice aDevice1 = aCfg.getScreen().getDevice(); + final AbstractGraphicsDevice aDevice2 = cloneDevice(aDevice1); + aDevice1.clearHandleOwner(); // don't close device handle + if( DEBUG ) { + System.err.println("GLEventListenerState.moveFrom.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveFrom.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + System.err.println("GLEventListenerState.moveFrom.1: "+srcSurface.getClass().getName()/*+", "+aSurface*/); + } + final AbstractGraphicsDevice aUpDevice2; + final boolean proxyOwnsUpstreamDevice; + { + AbstractGraphicsDevice _aUpDevice2 = null; + if(srcSurface instanceof ProxySurface) { + final ProxySurface aProxy = (ProxySurface)srcSurface; + proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + final NativeSurface aUpSurface = aProxy.getUpstreamSurface(); + if(DEBUG && null != aUpSurface) { + System.err.println("GLEventListenerState.moveFrom.2: "+aUpSurface.getClass().getName()+", "+aUpSurface); + } + if(null != aUpSurface) { + final AbstractGraphicsDevice aUpDevice1 = aUpSurface.getGraphicsConfiguration().getScreen().getDevice(); + _aUpDevice2 = cloneDevice(aUpDevice1); + aUpDevice1.clearHandleOwner(); // don't close device handle + if(DEBUG) { + System.err.println("GLEventListenerState.moveFrom.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveFrom.3b: up-pres 0x"+Integer.toHexString(_aUpDevice2.hashCode())+", "+_aUpDevice2); + System.err.println("GLEventListenerState.moveFrom.3c: "+srcSurface.getClass().getName()+", "+srcSurface); + System.err.println("GLEventListenerState.moveFrom.3d: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } + } + } else { + proxyOwnsUpstreamDevice = false; + } + aUpDevice2 = _aUpDevice2; + } + + glls = new GLEventListenerState(aUpDevice2, proxyOwnsUpstreamDevice, aDevice2, caps, + keepLocked ? srcUpstreamLock : null, + srcSurfaceLocked && keepLocked ? srcSurface : null, + src.getContext(), aSz, srcAnim, srcAnimStarted); + + // + // remove and cache all GLEventListener and their init-state + // + for(int i=0; i<aSz; i++) { + final GLEventListener l = src.getGLEventListener(0); + glls.listenersInit[i] = src.getGLEventListenerInitState(l); + glls.listeners[i] = src.removeGLEventListener( l ); + } + + src.setContext( null, false ); // implicit glFinish() ctx/drawable sync + + } finally { + if( srcSurfaceLocked && !keepLocked ) { + srcSurface.unlockSurface(); + } + } + } finally { + if( !keepLocked ) { + srcUpstreamLock.unlock(); + } + } + return glls; + } + + /** + * Moves all GLEventListenerState components to the given {@link GLAutoDrawable} + * from this instance, while loosing {@link #isOwner() ownership}. + * <p> + * If the previous {@link GLAutoDrawable} was removed from a {@link GLAnimatorControl} by previous {@link #moveFrom(GLAutoDrawable)}, + * the given {@link GLAutoDrawable} is added to the cached {@link GLAnimatorControl}. + * This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> + * Note: After this operation, the GLEventListenerState reference should be released. + * </p> + * + * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to + * + * @throws GLException if a realized surface could not be locked. + * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one. + * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> + * + * @see #moveFrom(GLAutoDrawable) + * @see #isOwner() + */ + public final void moveTo(final GLAutoDrawable dest) throws GLException { + this.moveTo(dest, null); + } + + /** + * Moves all GLEventListenerState components to the given {@link GLAutoDrawable} + * from this instance, while loosing {@link #isOwner() ownership}. + * <p> + * If the previous {@link GLAutoDrawable} was removed from a {@link GLAnimatorControl} by previous {@link #moveFrom(GLAutoDrawable, boolean)}, + * the given {@link GLAutoDrawable} is added to the cached {@link GLAnimatorControl}. + * This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> + * If the {@link GLAutoDrawable} <code>dest</code> has been kept locked by {@link #moveFrom(GLAutoDrawable, boolean)}, + * it's {@link #getUnlockSurfaceOp()} shall be passed here to <code>destUnlockOperation</code> to be unlocked. + * </p> + * <p> + * Note: After this operation, the GLEventListenerState reference should be released. + * </p> + * + * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to + * @param destUnlockOperation optional unlock operation for <code>dest</code>, see {@link #moveFrom(GLAutoDrawable, boolean)}. + * + * @throws GLException if a realized surface could not be locked. + * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one. + * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> + * + * @see #moveFrom(GLAutoDrawable, boolean) + * @see #isOwner() + */ + public final void moveTo(final GLAutoDrawable dest, final Runnable destUnlockOperation) throws GLException { + final GLAnimatorControl destAnim = dest.getAnimator(); + final boolean destAnimPaused; + if( null != destAnim ) { + destAnimPaused = destAnim.pause(); + destAnim.remove(dest); // also handles ECT + } else { + destAnimPaused = false; + } + + final List<GLRunnable> aGLCmds = new ArrayList<GLRunnable>(); + final int aSz = listenerCount(); + + final RecursiveLock destUpstreamLock = dest.getUpstreamLock(); + destUpstreamLock.lock(); + final boolean destIsRealized; + try { + final NativeSurface destSurface = dest.getNativeSurface(); + final boolean destSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < destSurface.lockSurface(); + if( dest.isRealized() && !destSurfaceLocked ) { + throw new GLException("Could not lock realized surface "+dest); + } + try { + + final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) destSurface.getGraphicsConfiguration(); + /** + final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); + if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) || + caps.getVisualID(VisualIDHolder.VIDType.NATIVE) != aCaps.getVisualID(VisualIDHolder.VIDType.NATIVE) ) { + throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps); + } */ + final DefaultGraphicsDevice aDevice1 = (DefaultGraphicsDevice) aCfg.getScreen().getDevice(); + final DefaultGraphicsDevice aDevice2 = (DefaultGraphicsDevice) device; + if( !aDevice1.getUniqueID().equals( aDevice2.getUniqueID() ) ) { + throw new GLException("Incompatible devices: Preserved <"+aDevice2.getUniqueID()+">, target <"+aDevice1.getUniqueID()+">"); + } + + // collect optional upstream surface info + final ProxySurface aProxy; + final NativeSurface aUpSurface; + if(destSurface instanceof ProxySurface) { + aProxy = (ProxySurface)destSurface; + aUpSurface = aProxy.getUpstreamSurface(); + } else { + aProxy = null; + aUpSurface = null; + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.0 : has aProxy "+(null!=aProxy)); + System.err.println("GLEventListenerState.moveTo.0 : has aUpSurface "+(null!=aUpSurface)); + } + if( null==aUpSurface && null != upstreamDevice ) { + throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = true, New-Holder = false"); + } + + // Destroy and remove currently associated GLContext, if any (will be replaced) + dest.setContext( null, true ); + destIsRealized = dest.isRealized(); + if( destIsRealized && null != aUpSurface ) { + // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook + dest.getDelegatedDrawable().setRealized(false); + } + + // Set new Screen and close previous one + { + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveTo.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + } + DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aDevice1, aDevice2); + aDevice2.close(); + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.1a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveTo.1b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + } + } + + // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it + if( null != aUpSurface ) { + final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration(); + if( null != upstreamDevice ) { + final DefaultGraphicsDevice aUpDevice1 = (DefaultGraphicsDevice) aUpCfg.getScreen().getDevice(); + final DefaultGraphicsDevice aUpDevice2 = (DefaultGraphicsDevice)upstreamDevice; + if( !aUpDevice1.getUniqueID().equals( aUpDevice2.getUniqueID() ) ) { + throw new GLException("Incompatible updtream devices: Preserved <"+aUpDevice2.getUniqueID()+">, target <"+aUpDevice1.getUniqueID()+">"); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.2a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveTo.2b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); + System.err.println("GLEventListenerState.moveTo.2c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } + DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aUpDevice1, aUpDevice2); + aUpDevice2.close(); + if( proxyOwnsUpstreamDevice ) { + aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveTo.3b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); + System.err.println("GLEventListenerState.moveTo.3c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } + } else { + throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true"); + } + } + + if( destIsRealized && null != aUpSurface ) { + dest.getDelegatedDrawable().setRealized(true); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.X : has aProxy "+(null!=aProxy)); + System.err.println("GLEventListenerState.moveTo.X : has aUpSurface "+(null!=aUpSurface)); + } + dest.setContext( context, false ); + } finally { + if( destSurfaceLocked ) { + destSurface.unlockSurface(); + } + } + } finally { + destUpstreamLock.unlock(); + } + if( null != destUnlockOperation ) { + destUnlockOperation.run(); + } + + owner = false; + + // + // Trigger GL-Viewport reset and reshape of all initialized GLEventListeners + // + aGLCmds.add(setViewport); + for(int i=0; i<aSz; i++) { + if( listenersInit[i] ) { + aGLCmds.add(new GLDrawableUtil.ReshapeGLEventListener( listeners[i], false ) ); + } + } + aGLCmds.add(glFinish); + dest.invoke(destIsRealized, aGLCmds); // only wait if already realized + + // add all cached GLEventListener to their destination and fix their init-state + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + dest.addGLEventListener( l ); + dest.setGLEventListenerInitState(l, listenersInit[i]); + listeners[i] = null; + } + + if( null != destAnim ) { + // prefer already bound animator + destAnim.add(dest); + if( destAnimPaused ) { + destAnim.resume(); + } + } else if ( null != anim ) { + // use previously bound animator + anim.add(dest); // also handles ECT + if(animStarted) { + anim.start(); + } + } + } + + private static final GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + return true; + } + }; + + private static final GLRunnable glFinish = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + drawable.getGL().glFinish(); + return true; + } + }; +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLExtensions.java b/src/jogl/classes/com/jogamp/opengl/GLExtensions.java index cf81b85ee..1593da25c 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLExtensions.java +++ b/src/jogl/classes/com/jogamp/opengl/GLExtensions.java @@ -28,17 +28,17 @@ package com.jogamp.opengl; /** - * Class holding OpenGL extension strings, commonly used by JOGL's implementation. + * Class holding OpenGL extension strings, commonly used by JOGL's implementation. */ public class GLExtensions { public static final String VERSION_1_2 = "GL_VERSION_1_2"; public static final String VERSION_1_4 = "GL_VERSION_1_4"; public static final String VERSION_1_5 = "GL_VERSION_1_5"; public static final String VERSION_2_0 = "GL_VERSION_2_0"; - + public static final String ARB_debug_output = "GL_ARB_debug_output"; public static final String AMD_debug_output = "GL_AMD_debug_output"; - + public static final String ARB_framebuffer_object = "GL_ARB_framebuffer_object"; public static final String OES_framebuffer_object = "GL_OES_framebuffer_object"; public static final String EXT_framebuffer_object = "GL_EXT_framebuffer_object"; @@ -49,16 +49,17 @@ public class GLExtensions { public static final String OES_depth32 = "GL_OES_depth32"; public static final String OES_packed_depth_stencil = "GL_OES_packed_depth_stencil"; public static final String NV_fbo_color_attachments = "GL_NV_fbo_color_attachments"; - + public static final String ARB_ES2_compatibility = "GL_ARB_ES2_compatibility"; - + public static final String ARB_ES3_compatibility = "GL_ARB_ES3_compatibility"; + public static final String EXT_abgr = "GL_EXT_abgr"; public static final String OES_rgb8_rgba8 = "GL_OES_rgb8_rgba8"; public static final String OES_stencil1 = "GL_OES_stencil1"; public static final String OES_stencil4 = "GL_OES_stencil4"; public static final String OES_stencil8 = "GL_OES_stencil8"; public static final String APPLE_float_pixels = "GL_APPLE_float_pixels"; - + public static final String ARB_texture_non_power_of_two = "GL_ARB_texture_non_power_of_two"; public static final String ARB_texture_rectangle = "GL_ARB_texture_rectangle"; public static final String EXT_texture_rectangle = "GL_EXT_texture_rectangle"; @@ -69,16 +70,19 @@ public class GLExtensions { public static final String NV_texture_compression_vtc = "GL_NV_texture_compression_vtc"; public static final String SGIS_generate_mipmap = "GL_SGIS_generate_mipmap"; public static final String OES_read_format = "GL_OES_read_format"; - + public static final String OES_single_precision = "GL_OES_single_precision"; public static final String OES_EGL_image_external = "GL_OES_EGL_image_external"; - + /** Required to be requested for OpenGL ES 2.0, <i>not</i> ES 3.0! */ + public static final String OES_standard_derivatives = "GL_OES_standard_derivatives"; + public static final String ARB_gpu_shader_fp64 = "GL_ARB_gpu_shader_fp64"; public static final String ARB_shader_objects = "GL_ARB_shader_objects"; - + public static final String ARB_geometry_shader4 = "GL_ARB_geometry_shader4"; + // // Aliased GLX/WGL/.. extensions // - - public static final String ARB_pixel_format = "GL_ARB_pixel_format"; + + public static final String ARB_pixel_format = "GL_ARB_pixel_format"; public static final String ARB_pbuffer = "GL_ARB_pbuffer"; } diff --git a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java new file mode 100644 index 000000000..e4cd5c5d9 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java @@ -0,0 +1,627 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +import java.util.IdentityHashMap; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.opengl.GLCapabilitiesImmutable; + +import com.jogamp.common.os.Platform; + +import jogamp.opengl.egl.EGL; +import jogamp.opengl.egl.EGLExt; + +/** + * GLRendererQuirks contains information of known bugs of various GL renderer. + * This information allows us to workaround them. + * <p> + * Using centralized quirk identifier enables us to + * locate code dealing w/ it and hence eases it's maintenance. + * </p> + * <p> + * <i>Some</i> <code>GL_VENDOR</code> and <code>GL_RENDERER</code> strings are + * listed here <http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR>. + * </p> + */ +public class GLRendererQuirks { + /** + * Crashes XServer when using double buffered PBuffer with GL_RENDERER: + * <ul> + * <li>Mesa DRI Intel(R) Sandybridge Desktop</li> + * <li>Mesa DRI Intel(R) Ivybridge Mobile - 3.0 Mesa 8.0.4</li> + * <li>Gallium 0.4 on AMD CYPRESS</li> + * </ul> + * For now, it is safe to disable it w/ hw-acceleration. + */ + public static final int NoDoubleBufferedPBuffer = 0; + + /** On Windows no double buffered bitmaps are guaranteed to be available. */ + public static final int NoDoubleBufferedBitmap = 1; + + /** Crashes application when trying to set EGL swap interval on Android 4.0.3 / Pandaboard ES / PowerVR SGX 540 */ + public static final int NoSetSwapInterval = 2; + + /** No offscreen bitmap available, currently true for JOGL's OSX implementation. */ + public static final int NoOffscreenBitmap = 3; + + /** SIGSEGV on setSwapInterval() after changing the context's drawable w/ 'Mesa 8.0.4' dri2SetSwapInterval/DRI2 (soft & intel) */ + public static final int NoSetSwapIntervalPostRetarget = 4; + + /** + * GLSL <code>discard</code> command leads to undefined behavior or won't get compiled if being used. + * <p> + * Appears to <i>have</i> happened on Nvidia Tegra2, but seems to be fine now.<br/> + * FIXME: Constrain version. + * </p> + */ + public static final int GLSLBuggyDiscard = 5; + + /** + * Non compliant GL context due to a buggy implementation not suitable for use. + * <p> + * Currently, Mesa >= 9.1.3 (may extend back as far as 9.0) OpenGL 3.1 compatibility + * context is not compliant. Most programs will give completely broken output (or no + * output at all. For now, this context is not trusted. + * </p> + * The above has been confirmed for the following Mesa 9.* GL_RENDERER strings: + * <ul> + * <li>Mesa .* Intel(R) Sandybridge Desktop</li> + * <li>Gallium 0.4 on AMD RS880</li> + * </ul> + * </p> + * <p> + * It still has to be verified whether the AMD OpenGL 3.1 core driver is compliant enought. + */ + public static final int GLNonCompliant = 6; + + /** + * The OpenGL context needs a <code>glFlush()</code> before releasing it, otherwise driver may freeze: + * <ul> + * <li>OSX < 10.7.3 - NVidia Driver. Bug 533 and Bug 548 @ https://jogamp.org/bugzilla/.</li> + * </ul> + */ + public static final int GLFlushBeforeRelease = 7; + + /** + * Closing X11 displays may cause JVM crashes or X11 errors with some buggy drivers + * while being used in concert w/ OpenGL. + * <p> + * Some drivers may require X11 displays to be closed in the same order as they were created, + * some may not allow them to be closed at all while resources are being used somehow. + * </p> + * <p> + * Drivers known exposing such bug: + * <ul> + * <li>Mesa < 8.0 _with_ X11 software renderer <code>Mesa X11</code>, not with GLX/DRI renderer.</li> + * <li>ATI proprietary Catalyst X11 driver versions: + * <ul> + * <li>8.78.6</li> + * <li>8.881</li> + * <li>8.911</li> + * <li>9.01.8</li> + * </ul></li> + * </ul> + * </p> + * <p> + * See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515 + * and {@link jogamp.nativewindow.x11.X11Util#ATI_HAS_XCLOSEDISPLAY_BUG}. + * </p> + * <p> + * See Bug 705 - https://jogamp.org/bugzilla/show_bug.cgi?id=705 + * </p> + */ + public static final int DontCloseX11Display = 8; + + /** + * Need current GL context when calling new ARB <i>pixel format query</i> functions, + * otherwise driver crashes the VM. + * <p> + * Drivers known exposing such bug: + * <ul> + * <li>ATI proprietary Catalyst driver on Windows version ≤ XP. + * TODO: Validate if bug actually relates to 'old' ATI Windows drivers for old GPU's like X300 + * regardless of the Windows version.</li> + * </ul> + * <p> + * See Bug 480 - https://jogamp.org/bugzilla/show_bug.cgi?id=480 + * </p> + */ + public static final int NeedCurrCtx4ARBPixFmtQueries = 9; + + /** + * Need current GL context when calling new ARB <i>CreateContext</i> function, + * otherwise driver crashes the VM. + * <p> + * Drivers known exposing such bug: + * <ul> + * <li>ATI proprietary Catalyst Windows driver on laptops with a driver version as reported in <i>GL_VERSION</i>: + * <ul> + * <li> <i>null</i> </li> + * <li> < <code>12.102.3.0</code> ( <i>amd_catalyst_13.5_mobility_beta2</i> ) </li> + * </ul></li> + * </ul> + * </p> + * <p> + * See Bug 706 - https://jogamp.org/bugzilla/show_bug.cgi?id=706<br/> + * See Bug 520 - https://jogamp.org/bugzilla/show_bug.cgi?id=520 + * </p> + */ + public static final int NeedCurrCtx4ARBCreateContext = 10; + + /** + * No full FBO support, i.e. not compliant w/ + * <ul> + * <li>GL_ARB_framebuffer_object</li> + * <li>EXT_framebuffer_object</li> + * <li>EXT_framebuffer_multisample</li> + * <li>EXT_framebuffer_blit</li> + * <li>EXT_packed_depth_stencil</li> + * </ul>. + * Drivers known exposing such bug: + * <ul> + * <li>Mesa <i>7.12-devel</i> on Windows with VMware <i>SVGA3D</i> renderer: + * <ul> + * <li>GL_VERSION: <i>2.1 Mesa 7.12-devel (git-d6c318e)</i> </li> + * <li>GL_RENDERER: <i>Gallium 0.4 on SVGA3D; build: RELEASE;</i> </li> + * </ul></li> + * </ul> + * <p> + * Also enabled via {@link #BuggyColorRenderbuffer}. + * </p> + * <p> + * Quirk can also be enabled via property: <code>jogl.fbo.force.min</code>. + * </p> + */ + public static final int NoFullFBOSupport = 11; + + /** + * GLSL is not compliant or even not stable (crash) + * <ul> + * <li>OSX < 10.7.0 (?) - NVidia Driver. Bug 818 @ https://jogamp.org/bugzilla/.</li> + * </ul> + */ + public static final int GLSLNonCompliant = 12; + + /** + * GL4 context needs to be requested via GL3 profile attribute + * <ul> + * <li>OSX >= 10.9.0 - kCGLOGLPVersion_GL4_Core may not produce hw-accel context. Bug 867 @ https://jogamp.org/bugzilla/.</li> + * </ul> + */ + public static final int GL4NeedsGL3Request = 13; + + /** + * Buggy shared OpenGL context support within a multithreaded use-case, not suitable for stable usage. + * <p> + * <i>X11 Mesa DRI Intel(R) driver >= 9.2.1</i> cannot handle multithreaded shared GLContext usage + * with non-blocking exclusive X11 display connections. + * References: + * <ul> + * <li>Bug 873: https://jogamp.org/bugzilla/show_bug.cgi?id=873</li> + * <li>https://bugs.freedesktop.org/show_bug.cgi?id=41736#c8</li> + * </ul> + * <p> + * However, not all multithreaded use-cases are broken, e.g. our GLMediaPlayer does work. + * </p> + * The above has been confirmed for the following Mesa 9.* strings: + * <ul> + * <li>GL_VENDOR Intel Open Source Technology Center</li> + * <li>GL_RENDERER Mesa DRI Intel(R) Sandybridge Desktop</li> + * <li>GL_RENDERER Mesa DRI Intel(R) Ivybridge Mobile</li> + * <li>GL_VERSION 3.1 (Core Profile) Mesa 9.2.1</li> + * </ul> + * </p> + * <p> + * On Android 4.*, <i>Huawei's Ascend G615 w/ Immersion.16</i> could not make a shared context + * current, which uses a pbuffer drawable: + * <ul> + * <li>Android 4.*</li> + * <li>GL_VENDOR Hisilicon Technologies</li> + * <li>GL_RENDERER Immersion.16</li> + * <li>GL_VERSION OpenGL ES 2.0</li> + * </ul> + * </p> + * <p> + * </p> + */ + public static final int GLSharedContextBuggy = 14; + + /** + * Bug 925 - Accept an ES3 Context, if reported via GL-Version-String w/o {@link EGLExt#EGL_OPENGL_ES3_BIT_KHR}. + * <p> + * The ES3 Context can be used via {@link EGL#EGL_OPENGL_ES2_BIT}. + * </p> + * <p> + * The ES3 Context {@link EGL#eglCreateContext(long, long, long, java.nio.IntBuffer) must be created} with version attributes: + * <pre> + * EGL.EGL_CONTEXT_CLIENT_VERSION, 2, .. + * </pre> + * </p> + * <ul> + * <li>Mesa/AMD >= 9.2.1</li> + * <li>Some Android ES3 drivers ..</li> + * </ul> + */ + public static final int GLES3ViaEGLES2Config = 15; + + /** + * Bug 948 - NVIDIA 331.38 (Linux X11) EGL impl. only supports _one_ EGL Device via {@link EGL#eglGetDisplay(long)}. + * <p> + * Subsequent calls to {@link EGL#eglGetDisplay(long)} fail. + * </p> + * <p> + * Reusing global EGL display works. + * </p> + * <p> + * The quirk is autodetected within EGLDrawableFactory's initial default device setup! + * </p> + * <p> + * Appears on: + * <ul> + * <li>EGL_VENDOR NVIDIA</li> + * <li>EGL_VERSION 1.4</li> + * <li>GL_VENDOR NVIDIA Corporation</li> + * <li>GL_VERSION OpenGL ES 3.0 331.38 (probably w/ 1st NV EGL lib on x86)</li> + * <li>Platform X11</li> + * <li>CPU Family {@link Platform.CPUFamily#X86}</li> + * </ul> + * </p> + */ + public static final int SingletonEGLDisplayOnly = 16; + + /** + * No reliable MSAA / FSAA {@link GLCapabilitiesImmutable#getSampleBuffers() multi} + * {@link GLCapabilitiesImmutable#getNumSamples() sampling} available, + * i.e. driver <i>may crash</i>. + * <p> + * Appears on: + * <ul> + * <li>GL_VENDOR nouveau</li> + * <li>GL_RENDERER Gallium 0.4 on NV34</li> + * </ul> + * TODO: We have to determine the exact version range, i.e. not adding the quirk with fixed driver version! + * </p> + * TODO: Since we currently don't handle this quirk internally, a user may need to do the following: + * <pre> + * final AbstractGraphicsDevice adevice = GLDrawableFactory.getDesktopFactory(); // or similar + * if( GLRendererQuirks.existStickyDeviceQuirk(adevice, GLRendererQuirks.NoMultiSamplingBuffers) ) { + * // don't use MSAA + * } + * </pre> + */ + public static final int NoMultiSamplingBuffers = 17; + + /** + * Buggy FBO color renderbuffer target, + * i.e. driver <i>may crash</i>. + * <p> + * Appears on: + * <ul> + * <li>GL_VENDOR Brian Paul</li> + * <li>GL_RENDERER Mesa X11</li> + * <li>GL_VERSION 2.1 Mesa 7.2</li> + * </ul> + * TODO: We have to determine the exact version range, i.e. not adding the quirk with fixed driver version! + * </p> + * <p> + * Note: Also enables {@link #NoFullFBOSupport}. + * </p> + * <p> + * Note: GLFBODrawable always uses texture attachments if set. + * </p> + * <p> + * Quirk can also be enabled via property: <code>jogl.fbo.force.nocolorrenderbuffer</code>. + * </p> + */ + public static final int BuggyColorRenderbuffer = 18; + + /** + * No pbuffer supporting accumulation buffers available, + * even if driver claims otherwise. + * <p> + * Some drivers wrongly claim to support pbuffers + * with accumulation buffers. However, the creation of such pbuffer fails: + * <pre> + * javax.media.opengl.GLException: pbuffer creation error: Couldn't find a suitable pixel format + * </pre> + * </p> + * <p> + * Appears on: + * <ul> + * <li>GL_VENDOR Intel</li> + * <li>GL_RENDERER Intel Bear Lake B</li> + * <li>GL_VERSION 1.4.0 - Build 8.14.10.1930</li> + * <li>Platform Windows</li> + * </ul> + * </p> + */ + public static final int NoPBufferWithAccum = 19; + + /** + * Need GL objects (VBO, ..) to be synchronized when utilized + * concurrently from multiple threads via a shared GL context, + * otherwise driver crashes the VM. + * <p> + * Usually synchronization should not be required, if the shared GL objects + * are created and immutable before concurrent usage.<br> + * However, using drivers exposing this issue always require the user to + * synchronize access of shared GL objects. + * </p> + * <p> + * Synchronization can be avoided if accessing the shared GL objects + * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example. + * </p> + * <p> + * Appears on: + * <ul> + * <li>Platform OSX + * <ul> + * <li>detected on OSX 10.9.5 first</li> + * <li>any driver</li> + * <li>enabled for all OSX versions</li> + * </ul> + * </li> + * </ul> + * </p> + * <p> + * See Bug 1088 - https://jogamp.org/bugzilla/show_bug.cgi?id=1088 + * </p> + */ + public static final int NeedSharedObjectSync = 20; + + /** + * No reliable ARB_create_context implementation, + * even if driver claims otherwise. + * <p> + * Some drivers wrongly claim to support ARB_create_context. + * However, the creation of such context fails: + * <pre> + * javax.media.opengl.GLException: AWT-EventQueue-0: WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2 + * requested (OpenGL >= 3.0.1). Requested: GLProfile[GL3bc/GL3bc.hw], current: 2.1 (Compat profile, FBO, hardware) + * - 2.1.8787 + * </pre> + * </p> + * <p> + * Appears on: + * <ul> + * <li>GL_VENDOR ATI Technologies Inc.</li> + * <li>GL_RENDERER ATI Radeon 3100 Graphics</li> + * <li>GL_VERSION 2.1.8787</li> + * <li>Platform Windows</li> + * </ul> + * </p> + */ + public static final int NoARBCreateContext = 21; + + /** Return the number of known quirks. */ + public static final int getCount() { return 22; } + + private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", + "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", + "GLNonCompliant", "GLFlushBeforeRelease", "DontCloseX11Display", + "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext", + "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request", + "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly", + "NoMultiSamplingBuffers", "BuggyColorRenderbuffer", "NoPBufferWithAccum", + "NeedSharedObjectSync", "NoARBCreateContext" + }; + + private static final IdentityHashMap<String, GLRendererQuirks> stickyDeviceQuirks = new IdentityHashMap<String, GLRendererQuirks>(); + + /** + * Retrieval of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * The {@link AbstractGraphicsDevice}s are mapped via their {@link AbstractGraphicsDevice#getUniqueID()}. + * </p> + * <p> + * Not thread safe. + * </p> + * @see #areSameStickyDevice(AbstractGraphicsDevice, AbstractGraphicsDevice) + */ + public static GLRendererQuirks getStickyDeviceQuirks(final AbstractGraphicsDevice device) { + final String key = device.getUniqueID(); + final GLRendererQuirks has = stickyDeviceQuirks.get(key); + final GLRendererQuirks res; + if( null == has ) { + res = new GLRendererQuirks(); + stickyDeviceQuirks.put(key, res); + } else { + res = has; + } + return res; + } + + /** + * Returns true if both devices have the same {@link AbstractGraphicsDevice#getUniqueID()}, + * otherwise false. + */ + public static boolean areSameStickyDevice(final AbstractGraphicsDevice device1, final AbstractGraphicsDevice device2) { + return device1.getUniqueID() == device2.getUniqueID(); // uses .intern()! + } + + /** + * {@link #addQuirk(int) Adding given quirk} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * Not thread safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static void addStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) throws IllegalArgumentException { + final GLRendererQuirks sq = getStickyDeviceQuirks(device); + sq.addQuirk(quirk); + } + /** + * {@link #addQuirks(int[], int, int) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * Not thread safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static void addStickyDeviceQuirks(final AbstractGraphicsDevice device, final int[] quirks, final int offset, final int len) throws IllegalArgumentException { + final GLRendererQuirks sq = getStickyDeviceQuirks(device); + sq.addQuirks(quirks, offset, len); + } + /** + * {@link #addQuirks(GLRendererQuirks) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * Not thread safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static void addStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks quirks) throws IllegalArgumentException { + final GLRendererQuirks sq = getStickyDeviceQuirks(device); + sq.addQuirks(quirks); + } + /** + * {@link #exist(int) Query} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * Not thread safe. However, use after changing the sticky quirks is safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static boolean existStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) { + return getStickyDeviceQuirks(device).exist(quirk); + } + /** + * {@link #addQuirks(GLRendererQuirks) Pushing} the sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks} + * to the given {@link GLRendererQuirks destination}. + * <p> + * Not thread safe. However, use after changing the sticky quirks is safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static void pushStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks dest) { + dest.addQuirks(getStickyDeviceQuirks(device)); + } + + private int _bitmask; + + public GLRendererQuirks() { + _bitmask = 0; + } + + /** + * @param quirks an array of valid quirks + * @param offset offset in quirks array to start reading + * @param len number of quirks to read from offset within quirks array + * @throws IllegalArgumentException if one of the quirks is out of range + */ + public GLRendererQuirks(final int[] quirks, final int offset, final int len) throws IllegalArgumentException { + this(); + addQuirks(quirks, offset, len); + } + + /** + * @param quirk valid quirk to be added + * @throws IllegalArgumentException if the quirk is out of range + */ + public final void addQuirk(final int quirk) throws IllegalArgumentException { + validateQuirk(quirk); + _bitmask |= 1 << quirk; + } + + /** + * @param quirks an array of valid quirks to be added + * @param offset offset in quirks array to start reading + * @param len number of quirks to read from offset within quirks array + * @throws IllegalArgumentException if one of the quirks is out of range + */ + public final void addQuirks(final int[] quirks, final int offset, final int len) throws IllegalArgumentException { + int bitmask = 0; + if( !( 0 <= offset + len && offset + len <= quirks.length ) ) { + throw new IllegalArgumentException("offset and len out of bounds: offset "+offset+", len "+len+", array-len "+quirks.length); + } + for(int i=offset; i<offset+len; i++) { + final int quirk = quirks[i]; + validateQuirk(quirk); + bitmask |= 1 << quirk; + } + _bitmask |= bitmask; + } + + /** + * @param quirks valid GLRendererQuirks to be added + */ + public final void addQuirks(final GLRendererQuirks quirks) { + _bitmask |= quirks._bitmask; + } + + /** + * @param quirk the quirk to be tested + * @return true if quirk exist, otherwise false + * @throws IllegalArgumentException if quirk is out of range + */ + public final boolean exist(final int quirk) throws IllegalArgumentException { + validateQuirk(quirk); + return 0 != ( ( 1 << quirk ) & _bitmask ); + } + + public final StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("["); + boolean first=true; + for(int i=0; i<getCount(); i++) { + final int testmask = 1 << i; + if( 0 != ( _bitmask & testmask ) ) { + if(!first) { sb.append(", "); } + sb.append(toString(i)); + first=false; + } + } + sb.append("]"); + return sb; + } + + @Override + public final String toString() { + return toString(null).toString(); + } + + /** + * @param quirk the quirk to be validated, i.e. whether it is out of range + * @throws IllegalArgumentException if quirk is out of range + */ + public static void validateQuirk(final int quirk) throws IllegalArgumentException { + if( !( 0 <= quirk && quirk < getCount() ) ) { + throw new IllegalArgumentException("Quirks must be in range [0.."+getCount()+"[, but quirk: "+quirk); + } + } + + /** + * @param quirk the quirk to be converted to String + * @return the String equivalent of this quirk + * @throws IllegalArgumentException if quirk is out of range + */ + public static final String toString(final int quirk) throws IllegalArgumentException { + validateQuirk(quirk); + return _names[quirk]; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLStateKeeper.java b/src/jogl/classes/com/jogamp/opengl/GLStateKeeper.java new file mode 100644 index 000000000..2b452e138 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLStateKeeper.java @@ -0,0 +1,100 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +/** + * Interface adding a {@link GLEventListenerState} protocol to {@link GLAutoDrawable}s + * or other self-contained compound types combining {@link GLDrawable}, {@link GLContext} and {@link GLEventListener}. + * <p> + * Implementing classes {@link #isGLStatePreservationSupported() may support} preservation + * of the {@link GLContext} state and it's associated {@link GLEventListener}. + * </p> + */ +public interface GLStateKeeper { + + /** Listener for preserve and restore notifications. */ + public static interface Listener { + /** Invoked before preservation. */ + void glStatePreserveNotify(GLStateKeeper glsk); + /** Invoked after restoration. */ + void glStateRestored(GLStateKeeper glsk); + } + + /** + * Sets a {@link Listener}, overriding the old one. + * @param l the new {@link Listener}. + * @return the previous {@link Listener}. + */ + public Listener setGLStateKeeperListener(Listener l); + + /** + * @return <code>true</code> if GL state preservation is supported in implementation and on current platform, <code>false</code> otherwise. + * @see #preserveGLStateAtDestroy(boolean) + * @see #getPreservedGLState() + * @see #clearPreservedGLState() + */ + public boolean isGLStatePreservationSupported(); + + /** + * If set to <code>true</code>, the next {@link GLAutoDrawable#destroy()} operation will + * {@link #preserveGLEventListenerState() preserve} the {@link GLEventListenerState}. + * <p> + * This is a one-shot flag, i.e. after preserving the {@link GLEventListenerState}, + * the flag is cleared. + * </p> + * <p> + * A preserved {@link GLEventListenerState} will be + * {@link #restoreGLEventListenerState() restored} again. + * </p> + * @return <code>true</code> if supported and successful, <code>false</code> otherwise. + * @see #isGLStatePreservationSupported() + * @see #getPreservedGLState() + * @see #clearPreservedGLState() + */ + public boolean preserveGLStateAtDestroy(boolean value); + + /** + * Returns the preserved {@link GLEventListenerState} if preservation was performed, + * otherwise <code>null</code>. + * @see #isGLStatePreservationSupported() + * @see #preserveGLStateAtDestroy(boolean) + * @see #clearPreservedGLState() + */ + public GLEventListenerState getPreservedGLState(); + + /** + * Clears the preserved {@link GLEventListenerState} from this {@link GLStateKeeper}, without destroying it. + * + * @return the preserved and cleared {@link GLEventListenerState} if preservation was performed, + * otherwise <code>null</code>. + * @see #isGLStatePreservationSupported() + * @see #preserveGLStateAtDestroy(boolean) + * @see #getPreservedGLState() + */ + public GLEventListenerState clearPreservedGLState(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GenericGLCapabilitiesChooser.java b/src/jogl/classes/com/jogamp/opengl/GenericGLCapabilitiesChooser.java new file mode 100644 index 000000000..3693f647a --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GenericGLCapabilitiesChooser.java @@ -0,0 +1,48 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import java.util.List; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.opengl.DefaultGLCapabilitiesChooser; + +/** + * Ignores windowSystemRecommendedChoice parameter, + * otherwise uses {@link DefaultGLCapabilitiesChooser} implementation. + */ +public class GenericGLCapabilitiesChooser extends DefaultGLCapabilitiesChooser { + + @Override + public int chooseCapabilities(final CapabilitiesImmutable desired, + final List<? extends CapabilitiesImmutable> available, + final int windowSystemRecommendedChoice) { + return super.chooseCapabilities(desired, available, -1); + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/JoglVersion.java b/src/jogl/classes/com/jogamp/opengl/JoglVersion.java index cdb4b82bb..8d6765036 100644 --- a/src/jogl/classes/com/jogamp/opengl/JoglVersion.java +++ b/src/jogl/classes/com/jogamp/opengl/JoglVersion.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.opengl; import com.jogamp.common.GlueGenVersion; @@ -43,7 +43,7 @@ public class JoglVersion extends JogampVersion { protected static volatile JoglVersion jogampCommonVersionInfo; - protected JoglVersion(String packageName, Manifest mf) { + protected JoglVersion(final String packageName, final Manifest mf) { super(packageName, mf); } @@ -60,17 +60,17 @@ public class JoglVersion extends JogampVersion { return jogampCommonVersionInfo; } - public StringBuilder toString(GL gl, StringBuilder sb) { + public StringBuilder toString(final GL gl, StringBuilder sb) { sb = super.toString(sb).append(Platform.getNewline()); getGLInfo(gl, sb); return sb; } - public String toString(GL gl) { + public String toString(final GL gl) { return toString(gl, null).toString(); } - public static StringBuilder getAvailableCapabilitiesInfo(GLDrawableFactory factory, AbstractGraphicsDevice device, StringBuilder sb) { + public static StringBuilder getAvailableCapabilitiesInfo(final GLDrawableFactory factory, final AbstractGraphicsDevice device, StringBuilder sb) { if(null==sb) { sb = new StringBuilder(); } @@ -84,55 +84,78 @@ public class JoglVersion extends JogampVersion { } done = true; } - } catch (GLException gle) { /* n/a */ } + } catch (final GLException gle) { /* n/a */ } } if(!done) { sb.append("\tnone").append(Platform.getNewline()); } sb.append(Platform.getNewline()); - return sb; + return sb; + } + + public static StringBuilder getAllAvailableCapabilitiesInfo(AbstractGraphicsDevice device, StringBuilder sb) { + if(null==sb) { + sb = new StringBuilder(); + } + if(null == device) { + device = GLProfile.getDefaultDevice(); + } + sb.append(Platform.getNewline()).append(Platform.getNewline()); + sb.append("Desktop Capabilities: ").append(Platform.getNewline()); + getAvailableCapabilitiesInfo(GLDrawableFactory.getDesktopFactory(), device, sb); + sb.append("EGL Capabilities: ").append(Platform.getNewline()); + getAvailableCapabilitiesInfo(GLDrawableFactory.getEGLFactory(), device, sb); + return sb; } - - public static StringBuilder getDefaultOpenGLInfo(AbstractGraphicsDevice device, StringBuilder sb, boolean withCapabilitiesInfo) { + + public static StringBuilder getDefaultOpenGLInfo(AbstractGraphicsDevice device, StringBuilder sb, final boolean withCapabilitiesInfo) { if(null==sb) { sb = new StringBuilder(); } if(null == device) { device = GLProfile.getDefaultDevice(); } - sb.append("Default Profiles on device ").append(device).append(Platform.getNewline()); + sb.append("GLProfiles on device ").append(device).append(Platform.getNewline()); if(null!=device) { GLProfile.glAvailabilityToString(device, sb, "\t", 1); } else { sb.append("none"); } if(withCapabilitiesInfo) { - sb.append(Platform.getNewline()).append(Platform.getNewline()); - sb.append("Desktop Capabilities: ").append(Platform.getNewline()); - getAvailableCapabilitiesInfo(GLDrawableFactory.getDesktopFactory(), device, sb); - sb.append("EGL Capabilities: ").append(Platform.getNewline()); - getAvailableCapabilitiesInfo(GLDrawableFactory.getEGLFactory(), device, sb); + sb = getAllAvailableCapabilitiesInfo(device, sb); } return sb; } - - public static StringBuilder getGLInfo(GL gl, StringBuilder sb) { - AbstractGraphicsDevice device = gl.getContext().getGLDrawable().getNativeSurface() + + public static StringBuilder getGLInfo(final GL gl, final StringBuilder sb) { + return getGLInfo(gl, sb, false); + } + public static StringBuilder getGLInfo(final GL gl, StringBuilder sb, final boolean withCapabilitiesAndExtensionInfo) { + final AbstractGraphicsDevice device = gl.getContext().getGLDrawable().getNativeSurface() .getGraphicsConfiguration().getScreen().getDevice(); if(null==sb) { sb = new StringBuilder(); } - + sb.append(VersionUtil.SEPERATOR).append(Platform.getNewline()); sb.append(device.getClass().getSimpleName()).append("[type ") .append(device.getType()).append(", connection ").append(device.getConnection()).append("]: ").append(Platform.getNewline()); - GLProfile.glAvailabilityToString(device, sb, "\t", 1); + GLProfile.glAvailabilityToString(device, sb, "\t", 1); sb.append(Platform.getNewline()); - return getGLStrings(gl, sb); + sb = getGLStrings(gl, sb, withCapabilitiesAndExtensionInfo); + + if( withCapabilitiesAndExtensionInfo ) { + sb = getAllAvailableCapabilitiesInfo(device, sb); + } + return sb; } - - public static StringBuilder getGLStrings(GL gl, StringBuilder sb) { + + public static StringBuilder getGLStrings(final GL gl, final StringBuilder sb) { + return getGLStrings(gl, sb, true); + } + + public static StringBuilder getGLStrings(final GL gl, StringBuilder sb, final boolean withExtensions) { if(null==sb) { sb = new StringBuilder(); } @@ -141,35 +164,60 @@ public class JoglVersion extends JogampVersion { sb.append(Platform.getNewline()); sb.append("GL Profile ").append(gl.getGLProfile()); sb.append(Platform.getNewline()); - sb.append("CTX VERSION ").append(gl.getContext().getGLVersion()); + sb.append("GL Version ").append(ctx.getGLVersion()).append(" [GL ").append(ctx.getGLVersionNumber()).append(", vendor ").append(ctx.getGLVendorVersionNumber()).append("]"); sb.append(Platform.getNewline()); - sb.append("GL ").append(gl); + sb.append("Quirks ").append(ctx.getRendererQuirks()); + sb.append(Platform.getNewline()); + sb.append("Impl. class ").append(gl.getClass().getCanonicalName()); sb.append(Platform.getNewline()); sb.append("GL_VENDOR ").append(gl.glGetString(GL.GL_VENDOR)); sb.append(Platform.getNewline()); sb.append("GL_RENDERER ").append(gl.glGetString(GL.GL_RENDERER)); sb.append(Platform.getNewline()); sb.append("GL_VERSION ").append(gl.glGetString(GL.GL_VERSION)); - sb.append(Platform.getNewline()); - sb.append("GLSL ").append(gl.hasGLSL()).append(", has-compiler: ").append(gl.isFunctionAvailable("glCompileShader")); + sb.append(Platform.getNewline()); + sb.append("GLSL ").append(gl.hasGLSL()).append(", has-compiler-func: ").append(gl.isFunctionAvailable("glCompileShader")); if(gl.hasGLSL()) { - sb.append(", version: ").append(gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION)); + sb.append(", version: ").append(gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION)).append(" / ").append(ctx.getGLSLVersionNumber()); } sb.append(Platform.getNewline()); - sb.append("GL_EXTENSIONS ").append(ctx.getGLExtensionCount()); + sb.append("GL FBO: basic ").append(gl.hasBasicFBOSupport()).append(", full ").append(gl.hasFullFBOSupport()); sb.append(Platform.getNewline()); - sb.append(" ").append(ctx.getGLExtensionsString()); + sb.append("GL_EXTENSIONS ").append(ctx.getGLExtensionCount()); sb.append(Platform.getNewline()); + if( withExtensions ) { + sb.append(" ").append(ctx.getGLExtensionsString()); + sb.append(Platform.getNewline()); + } sb.append("GLX_EXTENSIONS ").append(ctx.getPlatformExtensionCount()); sb.append(Platform.getNewline()); - sb.append(" ").append(ctx.getPlatformExtensionsString()); - sb.append(Platform.getNewline()); + if( withExtensions ) { + sb.append(" ").append(ctx.getPlatformExtensionsString()); + sb.append(Platform.getNewline()); + } sb.append(VersionUtil.SEPERATOR); return sb; } - public static void main(String args[]) { + public StringBuilder getBriefOSGLBuildInfo(final GL gl, StringBuilder sb) { + if(null==sb) { + sb = new StringBuilder(); + } + sb.append("OS: ").append(Platform.getOSName()).append(", version ").append(Platform.getOSVersion()).append(", arch ").append(Platform.getArchName()); + sb.append(Platform.getNewline()); + sb.append("GL_VENDOR ").append(gl.glGetString(GL.GL_VENDOR)); + sb.append(Platform.getNewline()); + sb.append("GL_RENDERER ").append(gl.glGetString(GL.GL_RENDERER)); + sb.append(Platform.getNewline()); + sb.append("GL_VERSION ").append(gl.glGetString(GL.GL_VERSION)); + sb.append(Platform.getNewline()); + sb.append("JOGL GIT sha1 ").append(getImplementationCommit()); + sb.append(Platform.getNewline()); + return sb; + } + + public static void main(final String args[]) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); // System.err.println(NativeWindowVersion.getInstance()); diff --git a/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java b/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java index d160eccff..8f033a8b8 100644 --- a/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java +++ b/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package com.jogamp.opengl.cg; import com.jogamp.common.jvm.JNILibLoaderBase; @@ -39,21 +39,22 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; -public class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { - private static List<String> glueLibNames; +public final class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { + private static final List<String> glueLibNames; static { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { Platform.initSingleton(); - + if(TempJarCache.isInitialized()) { // only: jogl-cg.jar -> jogl-cg-natives-<os.and.arch>.jar [atomic JAR files only] - JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { CgDynamicLibraryBundleInfo.class }, null, null ); + JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { jogamp.opengl.cg.CgPackagePlaceholder.class }, null); } return null; } }); - + glueLibNames = new ArrayList<String>(); // glueLibNames.addAll(getGlueLibNamesPreload()); glueLibNames.add("jogl_cg"); @@ -69,11 +70,16 @@ public class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { /** Make Cg symbols available to CgGL */ @Override - public boolean shallLinkGlobal() { return true; } + public final boolean shallLinkGlobal() { return true; } - /** default **/ + /** + * {@inheritDoc} + * <p> + * Returns <code>false</code>. + * </p> + */ @Override - public boolean shallLookupGlobal() { return false; } + public final boolean shallLookupGlobal() { return false; } /** Tool has none **/ @Override @@ -83,22 +89,22 @@ public class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { /** Tool has none **/ @Override - public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) { + public final long toolGetProcAddress(final long toolGetProcAddressHandle, final String funcName) { return 0; } - + @Override - public boolean useToolGetProcAdressFirst(String funcName) { + public final boolean useToolGetProcAdressFirst(final String funcName) { return false; } @Override - public List<List<String>> getToolLibNames() { + public final List<List<String>> getToolLibNames() { final List<List<String>> libsList = new ArrayList<List<String>>(); final List<String> libsCg = new ArrayList<String>(); libsCg.add("Cg"); libsList.add(libsCg); - + final List<String> libsCgGL = new ArrayList<String>(); libsCgGL.add("CgGL"); libsList.add(libsCgGL); @@ -112,9 +118,9 @@ public class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { } @Override - public RunnableExecutor getLibLoaderExecutor() { + public final RunnableExecutor getLibLoaderExecutor() { return DynamicLibraryBundle.getDefaultRunnableExecutor(); - } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/cg/CgException.java b/src/jogl/classes/com/jogamp/opengl/cg/CgException.java index 8bfd9e23e..0a3535ba6 100644 --- a/src/jogl/classes/com/jogamp/opengl/cg/CgException.java +++ b/src/jogl/classes/com/jogamp/opengl/cg/CgException.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -50,18 +50,18 @@ public class CgException extends RuntimeException { } /** Constructs a CgException object with the specified detail message. */ - public CgException(String message) { + public CgException(final String message) { super(message); } /** Constructs a CgException object with the specified detail message and root cause. */ - public CgException(String message, Throwable cause) { + public CgException(final String message, final Throwable cause) { super(message, cause); } /** Constructs a CgException object with the specified root cause. */ - public CgException(Throwable cause) { + public CgException(final Throwable cause) { super(cause); } } diff --git a/src/jogl/classes/com/jogamp/opengl/math/Binary16.java b/src/jogl/classes/com/jogamp/opengl/math/Binary16.java new file mode 100644 index 000000000..8e4aa9176 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Binary16.java @@ -0,0 +1,569 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * <p> + * Functions to convert values to/from the <code>binary16</code> format + * specified in <code>IEEE 754 2008</code>. + * </p> + */ + +public final class Binary16 +{ + /** + * The encoded form of negative infinity <code>-∞</code>. + */ + + public static final char NEGATIVE_INFINITY; + + /** + * The encoded form of positive infinity <code>∞</code>. + */ + + public static final char POSITIVE_INFINITY; + + /** + * The encoded form of positive zero <code>0</code>. + */ + + public static final char POSITIVE_ZERO; + + /** + * The encoded form of negative zero <code>-0</code>. + */ + + public static final char NEGATIVE_ZERO; + + /** + * The <i>bias</i> value used to offset the encoded exponent. A given + * exponent <code>e</code> is encoded as <code>{@link #BIAS} + e</code>. + */ + + public static final int BIAS; + + static { + NEGATIVE_INFINITY = 0xFC00; + POSITIVE_INFINITY = 0x7C00; + POSITIVE_ZERO = 0x0000; + NEGATIVE_ZERO = 0x8000; + BIAS = 15; + } + + private static final int MASK_SIGN; + private static final int MASK_EXPONENT; + private static final int MASK_SIGNIFICAND; + + static { + MASK_SIGN = 0x8000; + MASK_EXPONENT = 0x7C00; + MASK_SIGNIFICAND = 0x03FF; + } + + /** + * One possible not-a-number value. + */ + + public static char exampleNaN() + { + final int n = + Binary16.packSetExponentUnbiasedUnchecked(16) + | Binary16.packSetSignificandUnchecked(1); + final char c = (char) n; + return c; + } + + /** + * Return <code>true</code> if the given packed <code>binary16</code> value + * is infinite. + */ + + public static boolean isInfinite( + final char k) + { + if (Binary16.unpackGetExponentUnbiased(k) == 16) { + if (Binary16.unpackGetSignificand(k) == 0) { + return true; + } + } + return false; + } + + /** + * Return <code>true</code> if the given packed <code>binary16</code> value + * is not a number (<code>NaN</code>). + */ + + public static boolean isNaN( + final char k) + { + final int e = Binary16.unpackGetExponentUnbiased(k); + final int s = Binary16.unpackGetSignificand(k); + return (e == 16) && (s > 0); + } + + /** + * <p> + * Convert a double precision floating point value to a packed + * <code>binary16</code> value. + * </p> + * <p> + * For the following specific cases, the function returns: + * </p> + * <ul> + * <li><code>NaN</code> iff <code>isNaN(k)</code></li> + * <li>{@link #POSITIVE_INFINITY} iff + * <code>k == {@link Double#POSITIVE_INFINITY}</code></li> + * <li>{@link #NEGATIVE_INFINITY} iff + * <code>k == {@link Double#NEGATIVE_INFINITY}</code></li> + * <li>{@link #NEGATIVE_ZERO} iff <code>k == -0.0</code></li> + * <li>{@link #POSITIVE_ZERO} iff <code>k == 0.0</code></li> + * </ul> + * <p> + * Otherwise, the <code>binary16</code> value that most closely represents + * <code>k</code> is returned. This may obviously be an infinite value as + * the interval of double precision values is far larger than that of the + * <code>binary16</code> type. + * </p> + * + * @see #unpackDouble(char) + */ + + public static char packDouble( + final double k) + { + if (Double.isNaN(k)) { + return Binary16.exampleNaN(); + } + if (k == Double.POSITIVE_INFINITY) { + return Binary16.POSITIVE_INFINITY; + } + if (k == Double.NEGATIVE_INFINITY) { + return Binary16.NEGATIVE_INFINITY; + } + if (Double.doubleToLongBits(k) == Binary64.NEGATIVE_ZERO_BITS) { + return Binary16.NEGATIVE_ZERO; + } + if (k == 0.0) { + return Binary16.POSITIVE_ZERO; + } + + final long de = Binary64.unpackGetExponentUnbiased(k); + final long ds = Binary64.unpackGetSign(k); + final long dn = Binary64.unpackGetSignificand(k); + final char rsr = Binary16.packSetSignUnchecked((int) ds); + + /** + * Extract the 5 least-significant bits of the exponent. + */ + + final int rem = (int) (de & 0x001F); + final char rer = Binary16.packSetExponentUnbiasedUnchecked(rem); + + /** + * Extract the 10 most-significant bits of the significand. + */ + + final long rnm = dn & 0xFFC0000000000L; + final long rns = rnm >> 42; + final char rnr = Binary16.packSetSignificandUnchecked((int) rns); + + /** + * Combine the results. + */ + + return (char) (rsr | rer | rnr); + } + + /** + * <p> + * Convert a single precision floating point value to a packed + * <code>binary16</code> value. + * </p> + * <p> + * For the following specific cases, the function returns: + * </p> + * <ul> + * <li><code>NaN</code> iff <code>isNaN(k)</code></li> + * <li>{@link #POSITIVE_INFINITY} iff + * <code>k == {@link Float#POSITIVE_INFINITY}</code></li> + * <li>{@link #NEGATIVE_INFINITY} iff + * <code>k == {@link Float#NEGATIVE_INFINITY}</code></li> + * <li>{@link #NEGATIVE_ZERO} iff <code>k == -0.0</code></li> + * <li>{@link #POSITIVE_ZERO} iff <code>k == 0.0</code></li> + * </ul> + * <p> + * Otherwise, the <code>binary16</code> value that most closely represents + * <code>k</code> is returned. This may obviously be an infinite value as + * the interval of single precision values is far larger than that of the + * <code>binary16</code> type. + * </p> + * + * @see #unpackFloat(char) + */ + + public static char packFloat( + final float k) + { + if (Float.isNaN(k)) { + return Binary16.exampleNaN(); + } + if (k == Float.POSITIVE_INFINITY) { + return Binary16.POSITIVE_INFINITY; + } + if (k == Float.NEGATIVE_INFINITY) { + return Binary16.NEGATIVE_INFINITY; + } + if (Float.floatToIntBits(k) == Binary32.NEGATIVE_ZERO_BITS) { + return Binary16.NEGATIVE_ZERO; + } + if (k == 0.0) { + return Binary16.POSITIVE_ZERO; + } + + final long de = Binary32.unpackGetExponentUnbiased(k); + final long ds = Binary32.unpackGetSign(k); + final long dn = Binary32.unpackGetSignificand(k); + final char rsr = Binary16.packSetSignUnchecked((int) ds); + + /** + * Extract the 5 least-significant bits of the exponent. + */ + + final int rem = (int) (de & 0x001F); + final char rer = Binary16.packSetExponentUnbiasedUnchecked(rem); + + /** + * Extract the 10 most-significant bits of the significand. + */ + + final long rnm = dn & 0x7FE000L; + final long rns = rnm >> 13; + final char rnr = Binary16.packSetSignificandUnchecked((int) rns); + + /** + * Combine the results. + */ + + return (char) (rsr | rer | rnr); + } + + /** + * <p> + * Encode the unbiased exponent <code>e</code>. Values should be in the + * range <code>[-15, 16]</code> - values outside of this range will be + * truncated. + * </p> + * + * @see #unpackGetExponentUnbiased(char) + */ + + public static char packSetExponentUnbiasedUnchecked( + final int e) + { + final int eb = e + Binary16.BIAS; + final int es = eb << 10; + final int em = es & Binary16.MASK_EXPONENT; + return (char) em; + } + + /** + * <p> + * Encode the significand <code>s</code>. Values should be in the range + * <code>[0, 1023]</code>. Values outside of this range will be truncated. + * </p> + * + * @see #unpackGetSignificand(char) + */ + + public static char packSetSignificandUnchecked( + final int s) + { + final int sm = s & Binary16.MASK_SIGNIFICAND; + return (char) sm; + } + + /** + * <p> + * Encode the sign bit <code>s</code>. Values should be in the range + * <code>[0, 1]</code>, with <code>0</code> ironically denoting a positive + * value. Values outside of this range will be truncated. + * </p> + * + * @see #unpackGetSign(char) + */ + + public static char packSetSignUnchecked( + final int s) + { + final int ss = s << 15; + final int sm = ss & Binary16.MASK_SIGN; + return (char) sm; + } + + /** + * Show the given raw packed <code>binary16</code> value as a string of + * binary digits. + */ + + public static String toRawBinaryString( + final char k) + { + final StringBuilder b = new StringBuilder(); + int z = k; + for (int i = 0; i < 16; ++i) { + if ((z & 1) == 1) { + b.insert(0, "1"); + } else { + b.insert(0, "0"); + } + z >>= 1; + } + return b.toString(); + } + + /** + * <p> + * Convert a packed <code>binary16</code> value <code>k</code> to a + * double-precision floating point value. + * </p> + * <p> + * The function returns: + * </p> + * <ul> + * <li><code>NaN</code> iff <code>isNaN(k)</code></li> + * <li>{@link Double#POSITIVE_INFINITY} iff + * <code>k == {@link #POSITIVE_INFINITY}</code></li> + * <li>{@link Double#NEGATIVE_INFINITY} iff + * <code>k == {@link #NEGATIVE_INFINITY}</code></li> + * <li><code>-0.0</code> iff <code>k == {@link #NEGATIVE_ZERO}</code></li> + * <li><code>0.0</code> iff <code>k == {@link #POSITIVE_ZERO}</code></li> + * <li><code>(-1.0 * n) * (2 ^ e) * 1.s</code>, for the decoded sign + * <code>n</code> of <code>k</code>, the decoded exponent <code>e</code> of + * <code>k</code>, and the decoded significand <code>s</code> of + * <code>k</code>.</li> + * </ul> + * + * @see #packDouble(double) + */ + + public static double unpackDouble( + final char k) + { + if (Binary16.isNaN(k)) { + return Double.NaN; + } + if (k == Binary16.POSITIVE_INFINITY) { + return Double.POSITIVE_INFINITY; + } + if (k == Binary16.NEGATIVE_INFINITY) { + return Double.NEGATIVE_INFINITY; + } + if (k == Binary16.NEGATIVE_ZERO) { + return -0.0; + } + if (k == Binary16.POSITIVE_ZERO) { + return 0.0; + } + + final long e = Binary16.unpackGetExponentUnbiased(k); + final long s = Binary16.unpackGetSign(k); + final long n = Binary16.unpackGetSignificand(k); + + /** + * Shift the sign bit to the position at which it will appear in the + * resulting value. + */ + + final long rsr = s << 63; + + /** + * 1. Bias the exponent. + * + * 2. Shift the result left to the position at which it will appear in the + * resulting value. + */ + + final long reb = (e + Binary64.BIAS); + final long rer = reb << 52; + + /** + * Shift the significand left to the position at which it will appear in + * the resulting value. + */ + + final long rnr = n << 42; + return Double.longBitsToDouble(rsr | rer | rnr); + } + + /** + * <p> + * Convert a packed <code>binary16</code> value <code>k</code> to a + * single-precision floating point value. + * </p> + * <p> + * The function returns: + * </p> + * <ul> + * <li><code>NaN</code> iff <code>isNaN(k)</code></li> + * <li>{@link Float#POSITIVE_INFINITY} iff + * <code>k == {@link #POSITIVE_INFINITY}</code></li> + * <li>{@link Float#NEGATIVE_INFINITY} iff + * <code>k == {@link #NEGATIVE_INFINITY}</code></li> + * <li><code>-0.0</code> iff <code>k == {@link #NEGATIVE_ZERO}</code></li> + * <li><code>0.0</code> iff <code>k == {@link #POSITIVE_ZERO}</code></li> + * <li><code>(-1.0 * n) * (2 ^ e) * 1.s</code>, for the decoded sign + * <code>n</code> of <code>k</code>, the decoded exponent <code>e</code> of + * <code>k</code>, and the decoded significand <code>s</code> of + * <code>k</code>.</li> + * </ul> + * + * @see #packFloat(float) + */ + + public static float unpackFloat( + final char k) + { + if (Binary16.isNaN(k)) { + return Float.NaN; + } + if (k == Binary16.POSITIVE_INFINITY) { + return Float.POSITIVE_INFINITY; + } + if (k == Binary16.NEGATIVE_INFINITY) { + return Float.NEGATIVE_INFINITY; + } + if (k == Binary16.NEGATIVE_ZERO) { + return -0.0f; + } + if (k == Binary16.POSITIVE_ZERO) { + return 0.0f; + } + + final int e = Binary16.unpackGetExponentUnbiased(k); + final int s = Binary16.unpackGetSign(k); + final int n = Binary16.unpackGetSignificand(k); + + /** + * Shift the sign bit to the position at which it will appear in the + * resulting value. + */ + + final int rsr = s << 31; + + /** + * 1. Bias the exponent. + * + * 2. Shift the result left to the position at which it will appear in the + * resulting value. + */ + + final int reb = (e + Binary32.BIAS); + final int rer = reb << 23; + + /** + * Shift the significand left to the position at which it will appear in + * the resulting value. + */ + + final int rnr = n << 13; + return Float.intBitsToFloat(rsr | rer | rnr); + } + + /** + * <p> + * Extract and unbias the exponent of the given packed <code>binary16</code> + * value. + * </p> + * <p> + * The exponent is encoded <i>biased</i> as a number in the range + * <code>[0, 31]</code>, with <code>0</code> indicating that the number is + * <i>subnormal</i> and <code>[1, 30]</code> denoting the actual exponent + * plus {@link #BIAS}. Infinite and <code>NaN</code> values always have an + * exponent of <code>31</code>. + * </p> + * <p> + * This function will therefore return: + * </p> + * <ul> + * <li> + * <code>0 - {@link #BIAS} = -15</code> iff the input is a <i>subnormal</i> + * number.</li> + * <li>An integer in the range + * <code>[1 - {@link #BIAS}, 30 - {@link #BIAS}] = [-14, 15]</code> iff the + * input is a <i>normal</i> number.</li> + * <li> + * <code>16</code> iff the input is {@link #POSITIVE_INFINITY}, + * {@link #NEGATIVE_INFINITY}, or <code>NaN</code>.</li> + * </ul> + * + * @see #packSetExponentUnbiasedUnchecked(int) + */ + + public static int unpackGetExponentUnbiased( + final char k) + { + final int em = k & Binary16.MASK_EXPONENT; + final int es = em >> 10; + return es - Binary16.BIAS; + } + + /** + * Retrieve the sign bit of the given packed <code>binary16</code> value, as + * an integer in the range <code>[0, 1]</code>. + * + * @see Binary16#packSetSignUnchecked(int) + */ + + public static int unpackGetSign( + final char k) + { + return (k & Binary16.MASK_SIGN) >> 15; + } + + /** + * <p> + * Return the significand of the given packed <code>binary16</code> value as + * an integer in the range <code>[0, 1023]</code>. + * </p> + * + * @see Binary16#packSetSignificandUnchecked(int) + */ + + public static int unpackGetSignificand( + final char k) + { + return k & Binary16.MASK_SIGNIFICAND; + } + + private Binary16() + { + throw new AssertionError("Unreachable code, report this bug!"); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Binary32.java b/src/jogl/classes/com/jogamp/opengl/math/Binary32.java new file mode 100644 index 000000000..599b1ff68 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Binary32.java @@ -0,0 +1,116 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * Functions for interrogating <code>binary32</code> (float) values. + */ + +public final class Binary32 +{ + static final int NEGATIVE_ZERO_BITS; + static final int MASK_SIGN; + static final int MASK_EXPONENT; + static final int MASK_SIGNIFICAND; + static final int BIAS; + + static { + NEGATIVE_ZERO_BITS = 0x80000000; + MASK_SIGN = 0x80000000; + MASK_EXPONENT = 0x7ff00000; + MASK_SIGNIFICAND = 0x7fffff; + BIAS = 127; + } + + /** + * <p> + * Extract and unbias the exponent of the given packed <code>float</code> + * value. + * </p> + * <p> + * The exponent is encoded <i>biased</i> as a number in the range + * <code>[0, 255]</code>, with <code>0</code> indicating that the number is + * <i>subnormal</i> and <code>[1, 254]</code> denoting the actual exponent + * plus {@link #BIAS}. Infinite and <code>NaN</code> values always have a + * biased exponent of <code>255</code>. + * </p> + * <p> + * This function will therefore return: + * </p> + * <ul> + * <li> + * <code>0 - {@link #BIAS} = -127</code> iff the input is a <i>subnormal</i> + * number.</li> + * <li>An integer in the range + * <code>[1 - {@link #BIAS}, 254 - {@link #BIAS}] = [-126, 127]</code> iff + * the input is a <i>normal</i> number.</li> + * <li> + * <code>255 - {@link #BIAS} = 128</code> iff the input is + * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY}, or + * <code>NaN</code>.</li> + * </ul> + * + * @see #packSetExponentUnbiasedUnchecked(int) + */ + + public static int unpackGetExponentUnbiased( + final float d) + { + final int b = Float.floatToRawIntBits(d); + final int em = b & Binary32.MASK_EXPONENT; + final int es = em >> 23; + return es - Binary32.BIAS; + } + + /** + * <p> + * Return the sign of the given float value. + * </p> + */ + + public static int unpackGetSign( + final float d) + { + final int b = Float.floatToRawIntBits(d); + return ((b & Binary32.MASK_SIGN) >> 31) & 1; + } + + /** + * <p> + * Return the significand of the given float value. + * </p> + */ + + public static int unpackGetSignificand( + final float d) + { + final int b = Float.floatToRawIntBits(d); + return b & Binary32.MASK_SIGNIFICAND; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Binary64.java b/src/jogl/classes/com/jogamp/opengl/math/Binary64.java new file mode 100644 index 000000000..d4ec636a5 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Binary64.java @@ -0,0 +1,116 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * Functions for interrogating <code>binary64</code> (double) values. + */ + +public final class Binary64 +{ + static final long NEGATIVE_ZERO_BITS; + static final long MASK_SIGN; + static final long MASK_EXPONENT; + static final long MASK_SIGNIFICAND; + static final long BIAS; + + static { + NEGATIVE_ZERO_BITS = 0x8000000000000000L; + MASK_SIGN = 0x8000000000000000L; + MASK_EXPONENT = 0x7ff0000000000000L; + MASK_SIGNIFICAND = 0x000fffffffffffffL; + BIAS = 1023; + } + + /** + * <p> + * Extract and unbias the exponent of the given packed <code>double</code> + * value. + * </p> + * <p> + * The exponent is encoded <i>biased</i> as a number in the range + * <code>[0, 2047]</code>, with <code>0</code> indicating that the number is + * <i>subnormal</i> and <code>[1, 2046]</code> denoting the actual exponent + * plus {@link #BIAS}. Infinite and <code>NaN</code> values always have a + * biased exponent of <code>2047</code>. + * </p> + * <p> + * This function will therefore return: + * </p> + * <ul> + * <li> + * <code>0 - {@link #BIAS} = -1023</code> iff the input is a + * <i>subnormal</i> number.</li> + * <li>An integer in the range + * <code>[1 - {@link #BIAS}, 2046 - {@link #BIAS}] = [-1022, 1023]</code> + * iff the input is a <i>normal</i> number.</li> + * <li> + * <code>2047 - {@link #BIAS} = 1024</code> iff the input is + * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY}, or + * <code>NaN</code>.</li> + * </ul> + * + * @see #packSetExponentUnbiasedUnchecked(int) + */ + + public static long unpackGetExponentUnbiased( + final double d) + { + final long b = Double.doubleToRawLongBits(d); + final long em = b & Binary64.MASK_EXPONENT; + final long es = em >> 52; + return es - Binary64.BIAS; + } + + /** + * <p> + * Return the significand of the given double value. + * </p> + */ + + public static long unpackGetSignificand( + final double d) + { + final long b = Double.doubleToRawLongBits(d); + return b & Binary64.MASK_SIGNIFICAND; + } + + /** + * <p> + * Return the sign of the given double value. + * </p> + */ + + public static long unpackGetSign( + final double d) + { + final long b = Double.doubleToRawLongBits(d); + return ((b & Binary64.MASK_SIGN) >> 63) & 1; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/FixedPoint.java b/src/jogl/classes/com/jogamp/opengl/math/FixedPoint.java index 6412db5ef..31408b079 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FixedPoint.java +++ b/src/jogl/classes/com/jogamp/opengl/math/FixedPoint.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,10 +28,10 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ -package com.jogamp.opengl.util; +package com.jogamp.opengl.math; public class FixedPoint { public static final int toFixed(int value) { @@ -46,15 +46,15 @@ public class FixedPoint { return (int)(value * 65536.0f); } - public static final float toFloat(int value) { - return (float)value/65536.0f; + public static final float toFloat(final int value) { + return value/65536.0f; } - public static final int mult(int x1, int x2) { + public static final int mult(final int x1, final int x2) { return (int) ( ((long)x1*(long)x2)/65536 ); } - public static final int div(int x1, int x2) { + public static final int div(final int x1, final int x2) { return (int) ( (((long)x1)<<16)/x2 ); } } diff --git a/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java new file mode 100644 index 000000000..1a8924c8f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java @@ -0,0 +1,2320 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +import java.nio.FloatBuffer; + +import javax.media.opengl.GLException; + +import jogamp.opengl.Debug; + +import com.jogamp.common.os.Platform; +import com.jogamp.opengl.math.geom.AABBox; + +/** + * Basic Float math utility functions. + * <p> + * Implementation assumes linear matrix layout in column-major order + * matching OpenGL's implementation, illustration: + * <pre> + Row-Major Column-Major (OpenGL): + + | 0 1 2 3 | | 0 4 8 12 | + | | | | + | 4 5 6 7 | | 1 5 9 13 | + M = | | M = | | + | 8 9 10 11 | | 2 6 10 14 | + | | | | + | 12 13 14 15 | | 3 7 11 15 | + + C R C R + m[0*4+3] = tx; m[0+4*3] = tx; + m[1*4+3] = ty; m[1+4*3] = ty; + m[2*4+3] = tz; m[2+4*3] = tz; + * </pre> + * </p> + * <p> + * <ul> + * <li><a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix-FAQ</a></li> + * <li><a href="https://en.wikipedia.org/wiki/Matrix_%28mathematics%29">Wikipedia-Matrix</a></li> + * <li><a href="http://www.euclideanspace.com/maths/algebra/matrix/index.htm">euclideanspace.com-Matrix</a></li> + * </ul> + * </p> + * <p> + * Implementation utilizes unrolling of small vertices and matrices wherever possible + * while trying to access memory in a linear fashion for performance reasons, see: + * <ul> + * <li><a href="https://code.google.com/p/java-matrix-benchmark/">java-matrix-benchmark</a></li> + * <li><a href="https://github.com/lessthanoptimal/ejml">EJML Efficient Java Matrix Library</a></li> + * </ul> + * </p> + */ +public final class FloatUtil { + public static final boolean DEBUG = Debug.debug("Math"); + + // + // Matrix Ops + // + + /** + * Make matrix an identity matrix + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @return given matrix for chaining + */ + public static float[] makeIdentity(final float[] m, final int m_offset) { + m[m_offset+0+4*0] = 1f; + m[m_offset+1+4*0] = 0f; + m[m_offset+2+4*0] = 0f; + m[m_offset+3+4*0] = 0f; + + m[m_offset+0+4*1] = 0f; + m[m_offset+1+4*1] = 1f; + m[m_offset+2+4*1] = 0f; + m[m_offset+3+4*1] = 0f; + + m[m_offset+0+4*2] = 0f; + m[m_offset+1+4*2] = 0f; + m[m_offset+2+4*2] = 1f; + m[m_offset+3+4*2] = 0f; + + m[m_offset+0+4*3] = 0f; + m[m_offset+1+4*3] = 0f; + m[m_offset+2+4*3] = 0f; + m[m_offset+3+4*3] = 1f; + return m; + } + + /** + * Make matrix an identity matrix + * @param m 4x4 matrix in column-major order (also result) + * @return given matrix for chaining + */ + public static float[] makeIdentity(final float[] m) { + m[0+4*0] = 1f; + m[1+4*0] = 0f; + m[2+4*0] = 0f; + m[3+4*0] = 0f; + + m[0+4*1] = 0f; + m[1+4*1] = 1f; + m[2+4*1] = 0f; + m[3+4*1] = 0f; + + m[0+4*2] = 0f; + m[1+4*2] = 0f; + m[2+4*2] = 1f; + m[3+4*2] = 0f; + + m[0+4*3] = 0f; + m[1+4*3] = 0f; + m[2+4*3] = 0f; + m[3+4*3] = 1f; + return m; + } + + /** + * Make a translation matrix in column-major order from the given axis deltas + * <pre> + Translation matrix (Column Order): + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + x y z 1 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the diagonal and last-row is set. + * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix + * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} + * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, + * while leaving the other fields untouched for performance reasons. + * @return given matrix for chaining + */ + public static float[] makeTranslation(final float[] m, final int m_offset, final boolean initM, final float tx, final float ty, final float tz) { + if( initM ) { + makeIdentity(m, m_offset); + } else { + m[m_offset+0+4*0] = 1; + m[m_offset+1+4*1] = 1; + m[m_offset+2+4*2] = 1; + m[m_offset+3+4*3] = 1; + } + m[m_offset+0+4*3] = tx; + m[m_offset+1+4*3] = ty; + m[m_offset+2+4*3] = tz; + return m; + } + + /** + * Make a translation matrix in column-major order from the given axis deltas + * <pre> + Translation matrix (Column Order): + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + x y z 1 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the diagonal and last-row is set. + * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix + * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} + * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, + * while leaving the other fields untouched for performance reasons. + * @return given matrix for chaining + */ + public static float[] makeTranslation(final float[] m, final boolean initM, final float tx, final float ty, final float tz) { + if( initM ) { + makeIdentity(m); + } else { + m[0+4*0] = 1; + m[1+4*1] = 1; + m[2+4*2] = 1; + m[3+4*3] = 1; + } + m[0+4*3] = tx; + m[1+4*3] = ty; + m[2+4*3] = tz; + return m; + } + + /** + * Make a scale matrix in column-major order from the given axis factors + * <pre> + Scale matrix (Any Order): + x 0 0 0 + 0 y 0 0 + 0 0 z 0 + 0 0 0 1 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the diagonal and last-row is set. + * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix + * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} + * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, + * while leaving the other fields untouched for performance reasons. + * @return given matrix for chaining + */ + public static float[] makeScale(final float[] m, final int m_offset, final boolean initM, final float sx, final float sy, final float sz) { + if( initM ) { + makeIdentity(m, m_offset); + } else { + m[m_offset+0+4*3] = 0; + m[m_offset+1+4*3] = 0; + m[m_offset+2+4*3] = 0; + m[m_offset+3+4*3] = 1; + } + m[m_offset+0+4*0] = sx; + m[m_offset+1+4*1] = sy; + m[m_offset+2+4*2] = sz; + return m; + } + + /** + * Make a scale matrix in column-major order from the given axis factors + * <pre> + Scale matrix (Any Order): + x 0 0 0 + 0 y 0 0 + 0 0 z 0 + 0 0 0 1 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the diagonal and last-row is set. + * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix + * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} + * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, + * while leaving the other fields untouched for performance reasons. + * @return given matrix for chaining + */ + public static float[] makeScale(final float[] m, final boolean initM, final float sx, final float sy, final float sz) { + if( initM ) { + makeIdentity(m); + } else { + m[0+4*3] = 0; + m[1+4*3] = 0; + m[2+4*3] = 0; + m[3+4*3] = 1; + } + m[0+4*0] = sx; + m[1+4*1] = sy; + m[2+4*2] = sz; + return m; + } + + /** + * Make a rotation matrix from the given axis and angle in radians. + * <pre> + Rotation matrix (Column Order): + xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0 + xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0 + xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0 + 0 0 0 1 + * </pre> + * <p> + * All matrix fields are set. + * </p> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @return given matrix for chaining + */ + public static float[] makeRotationAxis(final float[] m, final int m_offset, final float angrad, float x, float y, float z, final float[] tmpVec3f) { + final float c = cos(angrad); + final float ic= 1.0f - c; + final float s = sin(angrad); + + tmpVec3f[0]=x; tmpVec3f[1]=y; tmpVec3f[2]=z; + VectorUtil.normalizeVec3(tmpVec3f); + x = tmpVec3f[0]; y = tmpVec3f[1]; z = tmpVec3f[2]; + + final float xy = x*y; + final float xz = x*z; + final float xs = x*s; + final float ys = y*s; + final float yz = y*z; + final float zs = z*s; + m[0+0*4+m_offset] = x*x*ic+c; + m[1+0*4+m_offset] = xy*ic+zs; + m[2+0*4+m_offset] = xz*ic-ys; + m[3+0*4+m_offset] = 0; + + m[0+1*4+m_offset] = xy*ic-zs; + m[1+1*4+m_offset] = y*y*ic+c; + m[2+1*4+m_offset] = yz*ic+xs; + m[3+1*4+m_offset] = 0; + + m[0+2*4+m_offset] = xz*ic+ys; + m[1+2*4+m_offset] = yz*ic-xs; + m[2+2*4+m_offset] = z*z*ic+c; + m[3+2*4+m_offset] = 0; + + m[0+3*4+m_offset] = 0f; + m[1+3*4+m_offset] = 0f; + m[2+3*4+m_offset] = 0f; + m[3+3*4+m_offset] = 1f; + + return m; + } + + /** + * Make a concatenated rotation matrix in column-major order from the given Euler rotation angles in radians. + * <p> + * The rotations are applied in the given order: + * <ul> + * <li>y - heading</li> + * <li>z - attitude</li> + * <li>x - bank</li> + * </ul> + * </p> + * <p> + * All matrix fields are set. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param bankX the Euler pitch angle in radians. (rotation about the X axis) + * @param headingY the Euler yaw angle in radians. (rotation about the Y axis) + * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis) + * @return given matrix for chaining + * <p> + * Implementation does not use Quaternion and hence is exposed to + * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a> + * </p> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a> + * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a> + */ + public static float[] makeRotationEuler(final float[] m, final int m_offset, final float bankX, final float headingY, final float attitudeZ) { + // Assuming the angles are in radians. + final float ch = cos(headingY); + final float sh = sin(headingY); + final float ca = cos(attitudeZ); + final float sa = sin(attitudeZ); + final float cb = cos(bankX); + final float sb = sin(bankX); + + m[0+0*4+m_offset] = ch*ca; + m[1+0*4+m_offset] = sa; + m[2+0*4+m_offset] = -sh*ca; + m[3+0*4+m_offset] = 0; + + m[0+1*4+m_offset] = sh*sb - ch*sa*cb; + m[1+1*4+m_offset] = ca*cb; + m[2+1*4+m_offset] = sh*sa*cb + ch*sb; + m[3+1*4+m_offset] = 0; + + m[0+2*4+m_offset] = ch*sa*sb + sh*cb; + m[1+2*4+m_offset] = -ca*sb; + m[2+2*4+m_offset] = -sh*sa*sb + ch*cb; + m[3+2*4+m_offset] = 0; + + m[0+3*4+m_offset] = 0; + m[1+3*4+m_offset] = 0; + m[2+3*4+m_offset] = 0; + m[3+3*4+m_offset] = 1; + + return m; + } + + /** + * Make given matrix the orthogonal matrix based on given parameters. + * <pre> + Ortho matrix (Column Order): + 2/dx 0 0 0 + 0 2/dy 0 0 + 0 0 2/dz 0 + tx ty tz 1 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the orthogonal fields are set. + * @param left + * @param right + * @param bottom + * @param top + * @param zNear + * @param zFar + * @return given matrix for chaining + */ + public static float[] makeOrtho(final float[] m, final int m_offset, final boolean initM, + final float left, final float right, + final float bottom, final float top, + final float zNear, final float zFar) { + if( initM ) { + // m[m_offset+0+4*0] = 1f; + m[m_offset+1+4*0] = 0f; + m[m_offset+2+4*0] = 0f; + m[m_offset+3+4*0] = 0f; + + m[m_offset+0+4*1] = 0f; + // m[m_offset+1+4*1] = 1f; + m[m_offset+2+4*1] = 0f; + m[m_offset+3+4*1] = 0f; + + m[m_offset+0+4*2] = 0f; + m[m_offset+1+4*2] = 0f; + // m[m_offset+2+4*2] = 1f; + m[m_offset+3+4*2] = 0f; + + // m[m_offset+0+4*3] = 0f; + // m[m_offset+1+4*3] = 0f; + // m[m_offset+2+4*3] = 0f; + // m[m_offset+3+4*3] = 1f; + } + final float dx=right-left; + final float dy=top-bottom; + final float dz=zFar-zNear; + final float tx=-1.0f*(right+left)/dx; + final float ty=-1.0f*(top+bottom)/dy; + final float tz=-1.0f*(zFar+zNear)/dz; + + m[m_offset+0+4*0] = 2.0f/dx; + + m[m_offset+1+4*1] = 2.0f/dy; + + m[m_offset+2+4*2] = -2.0f/dz; + + m[m_offset+0+4*3] = tx; + m[m_offset+1+4*3] = ty; + m[m_offset+2+4*3] = tz; + m[m_offset+3+4*3] = 1f; + + return m; + } + + /** + * Make given matrix the frustum matrix based on given parameters. + * <pre> + Frustum matrix (Column Order): + 2*zNear/dx 0 0 0 + 0 2*zNear/dy 0 0 + A B C -1 + 0 0 D 0 + * </pre> + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the frustum fields are set. + * @param left + * @param right + * @param bottom + * @param top + * @param zNear + * @param zFar + * @return given matrix for chaining + */ + public static float[] makeFrustum(final float[] m, final int m_offset, final boolean initM, + final float left, final float right, + final float bottom, final float top, + final float zNear, final float zFar) { + if(zNear<=0.0f||zFar<0.0f) { + throw new GLException("GL_INVALID_VALUE: zNear and zFar must be positive, and zNear>0"); + } + if(left==right || top==bottom) { + throw new GLException("GL_INVALID_VALUE: top,bottom and left,right must not be equal"); + } + if( initM ) { + // m[m_offset+0+4*0] = 1f; + m[m_offset+1+4*0] = 0f; + m[m_offset+2+4*0] = 0f; + m[m_offset+3+4*0] = 0f; + + m[m_offset+0+4*1] = 0f; + // m[m_offset+1+4*1] = 1f; + m[m_offset+2+4*1] = 0f; + m[m_offset+3+4*1] = 0f; + + // m[m_offset+0+4*2] = 0f; + // m[m_offset+1+4*2] = 0f; + // m[m_offset+2+4*2] = 1f; + // m[m_offset+3+4*2] = 0f; + + m[m_offset+0+4*3] = 0f; + m[m_offset+1+4*3] = 0f; + // m[m_offset+2+4*3] = 0f; + // m[m_offset+3+4*3] = 1f; + } + final float zNear2 = 2.0f*zNear; + final float dx=right-left; + final float dy=top-bottom; + final float dz=zFar-zNear; + final float A=(right+left)/dx; + final float B=(top+bottom)/dy; + final float C=-1.0f*(zFar+zNear)/dz; + final float D=-2.0f*(zFar*zNear)/dz; + + m[m_offset+0+4*0] = zNear2/dx; + + m[m_offset+1+4*1] = zNear2/dy; + + m[m_offset+0+4*2] = A; + m[m_offset+1+4*2] = B; + m[m_offset+2+4*2] = C; + m[m_offset+3+4*2] = -1.0f; + + m[m_offset+2+4*3] = D; + m[m_offset+3+4*3] = 0f; + + return m; + } + + /** + * Make given matrix the perspective {@link #makeFrustum(float[], int, boolean, float, float, float, float, float, float) frustum} + * matrix based on given parameters. + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the frustum fields are set. + * @param fovy_rad angle in radians + * @param aspect aspect ratio width / height + * @param zNear + * @param zFar + * @return given matrix for chaining + */ + public static float[] makePerspective(final float[] m, final int m_off, final boolean initM, + final float fovy_rad, final float aspect, final float zNear, final float zFar) { + final float top = tan(fovy_rad/2f) * zNear; // use tangent of half-fov ! + final float bottom = -1.0f * top; + final float left = aspect * bottom; + final float right = aspect * top; + return makeFrustum(m, m_off, initM, left, right, bottom, top, zNear, zFar); + } + + /** + * Make given matrix the perspective {@link #makeFrustum(float[], int, boolean, float, float, float, float, float, float) frustum} + * matrix based on given parameters. + * <p> + * All matrix fields are only set if <code>initM</code> is <code>true</code>. + * </p> + * + * @param m 4x4 matrix in column-major order (also result) + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param initM if true, given matrix will be initialized w/ identity matrix, + * otherwise only the frustum fields are set. + * @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent + * @param zNear + * @param zFar + * @return given matrix for chaining + */ + public static float[] makePerspective(final float[] m, final int m_offset, final boolean initM, + final FovHVHalves fovhv, final float zNear, final float zFar) { + final FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov ! + final float top = fovhvTan.top * zNear; + final float bottom = -1.0f * fovhvTan.bottom * zNear; + final float left = -1.0f * fovhvTan.left * zNear; + final float right = fovhvTan.right * zNear; + return makeFrustum(m, m_offset, initM, left, right, bottom, top, zNear, zFar); + } + + /** + * Make given matrix the <i>look-at</i> matrix based on given parameters. + * <p> + * Consist out of two matrix multiplications: + * <pre> + * <b>R</b> = <b>L</b> x <b>T</b>, + * with <b>L</b> for <i>look-at</i> matrix and + * <b>T</b> for eye translation. + * + * Result <b>R</b> can be utilized for <i>modelview</i> multiplication, i.e. + * <b>M</b> = <b>M</b> x <b>R</b>, + * with <b>M</b> being the <i>modelview</i> matrix. + * </pre> + * </p> + * <p> + * All matrix fields are set. + * </p> + * @param m 4x4 matrix in column-major order, result only + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param eye 3 component eye vector + * @param eye_offset + * @param center 3 component center vector + * @param center_offset + * @param up 3 component up vector + * @param up_offset + * @param mat4Tmp temp float[16] storage + * @return given matrix <code>m</code> for chaining + */ + public static float[] makeLookAt(final float[] m, final int m_offset, + final float[] eye, final int eye_offset, + final float[] center, final int center_offset, + final float[] up, final int up_offset, + final float[] mat4Tmp) { + final int forward_off = 0; + final int side_off = 3; + final int up2_off = 6; + + // forward! + mat4Tmp[0] = center[0+center_offset] - eye[0+eye_offset]; + mat4Tmp[1] = center[1+center_offset] - eye[1+eye_offset]; + mat4Tmp[2] = center[2+center_offset] - eye[2+eye_offset]; + + VectorUtil.normalizeVec3(mat4Tmp); // normalize forward + + /* Side = forward x up */ + VectorUtil.crossVec3(mat4Tmp, side_off, mat4Tmp, forward_off, up, up_offset); + VectorUtil.normalizeVec3(mat4Tmp, side_off); // normalize side + + /* Recompute up as: up = side x forward */ + VectorUtil.crossVec3(mat4Tmp, up2_off, mat4Tmp, side_off, mat4Tmp, forward_off); + + m[m_offset + 0 * 4 + 0] = mat4Tmp[0+side_off]; // side + m[m_offset + 0 * 4 + 1] = mat4Tmp[0+up2_off]; // up2 + m[m_offset + 0 * 4 + 2] = -mat4Tmp[0]; // forward + m[m_offset + 0 * 4 + 3] = 0; + + m[m_offset + 1 * 4 + 0] = mat4Tmp[1+side_off]; // side + m[m_offset + 1 * 4 + 1] = mat4Tmp[1+up2_off]; // up2 + m[m_offset + 1 * 4 + 2] = -mat4Tmp[1]; // forward + m[m_offset + 1 * 4 + 3] = 0; + + m[m_offset + 2 * 4 + 0] = mat4Tmp[2+side_off]; // side + m[m_offset + 2 * 4 + 1] = mat4Tmp[2+up2_off]; // up2 + m[m_offset + 2 * 4 + 2] = -mat4Tmp[2]; // forward + m[m_offset + 2 * 4 + 3] = 0; + + m[m_offset + 3 * 4 + 0] = 0; + m[m_offset + 3 * 4 + 1] = 0; + m[m_offset + 3 * 4 + 2] = 0; + m[m_offset + 3 * 4 + 3] = 1; + + makeTranslation(mat4Tmp, true, -eye[0+eye_offset], -eye[1+eye_offset], -eye[2+eye_offset]); + multMatrix(m, m_offset, mat4Tmp, 0); + + return m; + } + + /** + * Make given matrix the <i>pick</i> matrix based on given parameters. + * <p> + * Traditional <code>gluPickMatrix</code> implementation. + * </p> + * <p> + * Consist out of two matrix multiplications: + * <pre> + * <b>R</b> = <b>T</b> x <b>S</b>, + * with <b>T</b> for viewport translation matrix and + * <b>S</b> for viewport scale matrix. + * + * Result <b>R</b> can be utilized for <i>projection</i> multiplication, i.e. + * <b>P</b> = <b>P</b> x <b>R</b>, + * with <b>P</b> being the <i>projection</i> matrix. + * </pre> + * </p> + * <p> + * To effectively use the generated pick matrix for picking, + * call {@link #makePick(float[], int, float, float, float, float, int[], int, float[]) makePick} + * and multiply a {@link #makePerspective(float[], int, boolean, float, float, float, float) custom perspective matrix} + * by this pick matrix. Then you may load the result onto the perspective matrix stack. + * </p> + * <p> + * All matrix fields are set. + * </p> + * @param m 4x4 matrix in column-major order, result only + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @param x the center x-component of a picking region in window coordinates + * @param y the center y-component of a picking region in window coordinates + * @param deltaX the width of the picking region in window coordinates. + * @param deltaY the height of the picking region in window coordinates. + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param mat4Tmp temp float[16] storage + * @return given matrix <code>m</code> for chaining or <code>null</code> if either delta value is <= zero. + */ + public static float[] makePick(final float[] m, final int m_offset, + final float x, final float y, + final float deltaX, final float deltaY, + final int[] viewport, final int viewport_offset, + final float[] mat4Tmp) { + if (deltaX <= 0 || deltaY <= 0) { + return null; + } + + /* Translate and scale the picked region to the entire window */ + makeTranslation(m, m_offset, true, + (viewport[2+viewport_offset] - 2 * (x - viewport[0+viewport_offset])) / deltaX, + (viewport[3+viewport_offset] - 2 * (y - viewport[1+viewport_offset])) / deltaY, + 0); + makeScale(mat4Tmp, true, + viewport[2+viewport_offset] / deltaX, viewport[3+viewport_offset] / deltaY, 1.0f); + multMatrix(m, m_offset, mat4Tmp, 0); + return m; + } + + /** + * Transpose the given matrix. + * + * @param msrc 4x4 matrix in column-major order, the source + * @param msrc_offset offset in given array <i>msrc</i>, i.e. start of the 4x4 matrix + * @param mres 4x4 matrix in column-major order, the result + * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix + * @return given result matrix <i>mres</i> for chaining + */ + public static float[] transposeMatrix(final float[] msrc, final int msrc_offset, final float[] mres, final int mres_offset) { + mres[mres_offset+0] = msrc[msrc_offset+0*4]; + mres[mres_offset+1] = msrc[msrc_offset+1*4]; + mres[mres_offset+2] = msrc[msrc_offset+2*4]; + mres[mres_offset+3] = msrc[msrc_offset+3*4]; + + final int i4_1 = 1*4; + mres[mres_offset+0+i4_1] = msrc[msrc_offset+1+0*4]; + mres[mres_offset+1+i4_1] = msrc[msrc_offset+1+1*4]; + mres[mres_offset+2+i4_1] = msrc[msrc_offset+1+2*4]; + mres[mres_offset+3+i4_1] = msrc[msrc_offset+1+3*4]; + + final int i4_2 = 2*4; + mres[mres_offset+0+i4_2] = msrc[msrc_offset+2+0*4]; + mres[mres_offset+1+i4_2] = msrc[msrc_offset+2+1*4]; + mres[mres_offset+2+i4_2] = msrc[msrc_offset+2+2*4]; + mres[mres_offset+3+i4_2] = msrc[msrc_offset+2+3*4]; + + final int i4_3 = 3*4; + mres[mres_offset+0+i4_3] = msrc[msrc_offset+3+0*4]; + mres[mres_offset+1+i4_3] = msrc[msrc_offset+3+1*4]; + mres[mres_offset+2+i4_3] = msrc[msrc_offset+3+2*4]; + mres[mres_offset+3+i4_3] = msrc[msrc_offset+3+3*4]; + + return mres; + } + + /** + * Transpose the given matrix. + * + * @param msrc 4x4 matrix in column-major order, the source + * @param mres 4x4 matrix in column-major order, the result + * @return given result matrix <i>mres</i> for chaining + */ + public static float[] transposeMatrix(final float[] msrc, final float[] mres) { + mres[0] = msrc[0*4]; + mres[1] = msrc[1*4]; + mres[2] = msrc[2*4]; + mres[3] = msrc[3*4]; + + final int i4_1 = 1*4; + mres[0+i4_1] = msrc[1+0*4]; + mres[1+i4_1] = msrc[1+1*4]; + mres[2+i4_1] = msrc[1+2*4]; + mres[3+i4_1] = msrc[1+3*4]; + + final int i4_2 = 2*4; + mres[0+i4_2] = msrc[2+0*4]; + mres[1+i4_2] = msrc[2+1*4]; + mres[2+i4_2] = msrc[2+2*4]; + mres[3+i4_2] = msrc[2+3*4]; + + final int i4_3 = 3*4; + mres[0+i4_3] = msrc[3+0*4]; + mres[1+i4_3] = msrc[3+1*4]; + mres[2+i4_3] = msrc[3+2*4]; + mres[3+i4_3] = msrc[3+3*4]; + + return mres; + } + + /** + * Returns the determinant of the given matrix + * @param m 4x4 matrix in column-major order, the source + * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix + * @return the matrix determinant + */ + public static float matrixDeterminant(final float[] m, final int m_offset) { + float a11 = m[ 1+4*1 + m_offset ]; + float a21 = m[ 2+4*1 + m_offset ]; + float a31 = m[ 3+4*1 + m_offset ]; + float a12 = m[ 1+4*2 + m_offset ]; + float a22 = m[ 2+4*2 + m_offset ]; + float a32 = m[ 3+4*2 + m_offset ]; + float a13 = m[ 1+4*3 + m_offset ]; + float a23 = m[ 2+4*3 + m_offset ]; + float a33 = m[ 3+4*3 + m_offset ]; + + float ret = 0; + ret += m[ 0 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a11 = m[ 1+4*0 + m_offset ]; + a21 = m[ 2+4*0 + m_offset ]; + a31 = m[ 3+4*0 + m_offset ]; + ret -= m[ 0+4*1 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a12 = m[ 1+4*1 + m_offset ]; + a22 = m[ 2+4*1 + m_offset ]; + a32 = m[ 3+4*1 + m_offset ]; + ret += m[ 0+4*2 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a13 = m[ 1+4*2 + m_offset ]; + a23 = m[ 2+4*2 + m_offset ]; + a33 = m[ 3+4*2 + m_offset ]; + ret -= m[ 0+4*3 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + return ret; + } + + /** + * Returns the determinant of the given matrix + * @param m 4x4 matrix in column-major order, the source + * @return the matrix determinant + */ + public static float matrixDeterminant(final float[] m) { + float a11 = m[ 1+4*1 ]; + float a21 = m[ 2+4*1 ]; + float a31 = m[ 3+4*1 ]; + float a12 = m[ 1+4*2 ]; + float a22 = m[ 2+4*2 ]; + float a32 = m[ 3+4*2 ]; + float a13 = m[ 1+4*3 ]; + float a23 = m[ 2+4*3 ]; + float a33 = m[ 3+4*3 ]; + + float ret = 0; + ret += m[ 0 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a11 = m[ 1+4*0 ]; + a21 = m[ 2+4*0 ]; + a31 = m[ 3+4*0 ]; + ret -= m[ 0+4*1 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a12 = m[ 1+4*1 ]; + a22 = m[ 2+4*1 ]; + a32 = m[ 3+4*1 ]; + ret += m[ 0+4*2 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + a13 = m[ 1+4*2 ]; + a23 = m[ 2+4*2 ]; + a33 = m[ 3+4*2 ]; + ret -= m[ 0+4*3 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); + return ret; + } + + /** + * Invert the given matrix. + * <p> + * Returns <code>null</code> if inversion is not possible, + * e.g. matrix is singular due to a bad matrix. + * </p> + * + * @param msrc 4x4 matrix in column-major order, the source + * @param msrc_offset offset in given array <i>msrc</i>, i.e. start of the 4x4 matrix + * @param mres 4x4 matrix in column-major order, the result - may be <code>msrc</code> (in-place) + * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix - may be <code>msrc_offset</code> (in-place) + * @return given result matrix <i>mres</i> for chaining if successful, otherwise <code>null</code>. See above. + */ + public static float[] invertMatrix(final float[] msrc, final int msrc_offset, final float[] mres, final int mres_offset) { + final float scale; + { + float max = Math.abs(msrc[0]); + + for( int i = 1; i < 16; i++ ) { + final float a = Math.abs(msrc[i]); + if( a > max ) max = a; + } + if( 0 == max ) { + return null; + } + scale = 1.0f/max; + } + + final float a11 = msrc[0+4*0+msrc_offset]*scale; + final float a21 = msrc[1+4*0+msrc_offset]*scale; + final float a31 = msrc[2+4*0+msrc_offset]*scale; + final float a41 = msrc[3+4*0+msrc_offset]*scale; + final float a12 = msrc[0+4*1+msrc_offset]*scale; + final float a22 = msrc[1+4*1+msrc_offset]*scale; + final float a32 = msrc[2+4*1+msrc_offset]*scale; + final float a42 = msrc[3+4*1+msrc_offset]*scale; + final float a13 = msrc[0+4*2+msrc_offset]*scale; + final float a23 = msrc[1+4*2+msrc_offset]*scale; + final float a33 = msrc[2+4*2+msrc_offset]*scale; + final float a43 = msrc[3+4*2+msrc_offset]*scale; + final float a14 = msrc[0+4*3+msrc_offset]*scale; + final float a24 = msrc[1+4*3+msrc_offset]*scale; + final float a34 = msrc[2+4*3+msrc_offset]*scale; + final float a44 = msrc[3+4*3+msrc_offset]*scale; + + final float m11 = + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42); + final float m12 = -( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)); + final float m13 = + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41); + final float m14 = -( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)); + final float m21 = -( + a12*(a33*a44 - a34*a43) - a13*(a32*a44 - a34*a42) + a14*(a32*a43 - a33*a42)); + final float m22 = + a11*(a33*a44 - a34*a43) - a13*(a31*a44 - a34*a41) + a14*(a31*a43 - a33*a41); + final float m23 = -( + a11*(a32*a44 - a34*a42) - a12*(a31*a44 - a34*a41) + a14*(a31*a42 - a32*a41)); + final float m24 = + a11*(a32*a43 - a33*a42) - a12*(a31*a43 - a33*a41) + a13*(a31*a42 - a32*a41); + final float m31 = + a12*(a23*a44 - a24*a43) - a13*(a22*a44 - a24*a42) + a14*(a22*a43 - a23*a42); + final float m32 = -( + a11*(a23*a44 - a24*a43) - a13*(a21*a44 - a24*a41) + a14*(a21*a43 - a23*a41)); + final float m33 = + a11*(a22*a44 - a24*a42) - a12*(a21*a44 - a24*a41) + a14*(a21*a42 - a22*a41); + final float m34 = -( + a11*(a22*a43 - a23*a42) - a12*(a21*a43 - a23*a41) + a13*(a21*a42 - a22*a41)); + final float m41 = -( + a12*(a23*a34 - a24*a33) - a13*(a22*a34 - a24*a32) + a14*(a22*a33 - a23*a32)); + final float m42 = + a11*(a23*a34 - a24*a33) - a13*(a21*a34 - a24*a31) + a14*(a21*a33 - a23*a31); + final float m43 = -( + a11*(a22*a34 - a24*a32) - a12*(a21*a34 - a24*a31) + a14*(a21*a32 - a22*a31)); + final float m44 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); + + final float det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale; + + if( 0 == det ) { + return null; + } + + mres[0+4*0+mres_offset] = m11 / det; + mres[1+4*0+mres_offset] = m12 / det; + mres[2+4*0+mres_offset] = m13 / det; + mres[3+4*0+mres_offset] = m14 / det; + mres[0+4*1+mres_offset] = m21 / det; + mres[1+4*1+mres_offset] = m22 / det; + mres[2+4*1+mres_offset] = m23 / det; + mres[3+4*1+mres_offset] = m24 / det; + mres[0+4*2+mres_offset] = m31 / det; + mres[1+4*2+mres_offset] = m32 / det; + mres[2+4*2+mres_offset] = m33 / det; + mres[3+4*2+mres_offset] = m34 / det; + mres[0+4*3+mres_offset] = m41 / det; + mres[1+4*3+mres_offset] = m42 / det; + mres[2+4*3+mres_offset] = m43 / det; + mres[3+4*3+mres_offset] = m44 / det; + return mres; + } + + /** + * Invert the given matrix. + * <p> + * Returns <code>null</code> if inversion is not possible, + * e.g. matrix is singular due to a bad matrix. + * </p> + * + * @param msrc 4x4 matrix in column-major order, the source + * @param mres 4x4 matrix in column-major order, the result - may be <code>msrc</code> (in-place) + * @return given result matrix <i>mres</i> for chaining if successful, otherwise <code>null</code>. See above. + */ + public static float[] invertMatrix(final float[] msrc, final float[] mres) { + final float scale; + { + float max = Math.abs(msrc[0]); + + for( int i = 1; i < 16; i++ ) { + final float a = Math.abs(msrc[i]); + if( a > max ) max = a; + } + if( 0 == max ) { + return null; + } + scale = 1.0f/max; + } + + final float a11 = msrc[0+4*0]*scale; + final float a21 = msrc[1+4*0]*scale; + final float a31 = msrc[2+4*0]*scale; + final float a41 = msrc[3+4*0]*scale; + final float a12 = msrc[0+4*1]*scale; + final float a22 = msrc[1+4*1]*scale; + final float a32 = msrc[2+4*1]*scale; + final float a42 = msrc[3+4*1]*scale; + final float a13 = msrc[0+4*2]*scale; + final float a23 = msrc[1+4*2]*scale; + final float a33 = msrc[2+4*2]*scale; + final float a43 = msrc[3+4*2]*scale; + final float a14 = msrc[0+4*3]*scale; + final float a24 = msrc[1+4*3]*scale; + final float a34 = msrc[2+4*3]*scale; + final float a44 = msrc[3+4*3]*scale; + + final float m11 = + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42); + final float m12 = -( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)); + final float m13 = + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41); + final float m14 = -( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)); + final float m21 = -( + a12*(a33*a44 - a34*a43) - a13*(a32*a44 - a34*a42) + a14*(a32*a43 - a33*a42)); + final float m22 = + a11*(a33*a44 - a34*a43) - a13*(a31*a44 - a34*a41) + a14*(a31*a43 - a33*a41); + final float m23 = -( + a11*(a32*a44 - a34*a42) - a12*(a31*a44 - a34*a41) + a14*(a31*a42 - a32*a41)); + final float m24 = + a11*(a32*a43 - a33*a42) - a12*(a31*a43 - a33*a41) + a13*(a31*a42 - a32*a41); + final float m31 = + a12*(a23*a44 - a24*a43) - a13*(a22*a44 - a24*a42) + a14*(a22*a43 - a23*a42); + final float m32 = -( + a11*(a23*a44 - a24*a43) - a13*(a21*a44 - a24*a41) + a14*(a21*a43 - a23*a41)); + final float m33 = + a11*(a22*a44 - a24*a42) - a12*(a21*a44 - a24*a41) + a14*(a21*a42 - a22*a41); + final float m34 = -( + a11*(a22*a43 - a23*a42) - a12*(a21*a43 - a23*a41) + a13*(a21*a42 - a22*a41)); + final float m41 = -( + a12*(a23*a34 - a24*a33) - a13*(a22*a34 - a24*a32) + a14*(a22*a33 - a23*a32)); + final float m42 = + a11*(a23*a34 - a24*a33) - a13*(a21*a34 - a24*a31) + a14*(a21*a33 - a23*a31); + final float m43 = -( + a11*(a22*a34 - a24*a32) - a12*(a21*a34 - a24*a31) + a14*(a21*a32 - a22*a31)); + final float m44 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); + + final float det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale; + + if( 0 == det ) { + return null; + } + + mres[0+4*0] = m11 / det; + mres[1+4*0] = m12 / det; + mres[2+4*0] = m13 / det; + mres[3+4*0] = m14 / det; + mres[0+4*1] = m21 / det; + mres[1+4*1] = m22 / det; + mres[2+4*1] = m23 / det; + mres[3+4*1] = m24 / det; + mres[0+4*2] = m31 / det; + mres[1+4*2] = m32 / det; + mres[2+4*2] = m33 / det; + mres[3+4*2] = m34 / det; + mres[0+4*3] = m41 / det; + mres[1+4*3] = m42 / det; + mres[2+4*3] = m43 / det; + mres[3+4*3] = m44 / det; + return mres; + } + + /** + * Map object coordinates to window coordinates. + * <p> + * Traditional <code>gluProject</code> implementation. + * </p> + * + * @param objx + * @param objy + * @param objz + * @param modelMatrix 4x4 modelview matrix + * @param modelMatrix_offset + * @param projMatrix 4x4 projection matrix + * @param projMatrix_offset + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param win_pos 3 component window coordinate, the result + * @param win_pos_offset + * @param vec4Tmp1 4 component vector for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return true if successful, otherwise false (z is 1) + */ + public static boolean mapObjToWinCoords(final float objx, final float objy, final float objz, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float[] win_pos, final int win_pos_offset, + final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { + vec4Tmp1[0] = objx; + vec4Tmp1[1] = objy; + vec4Tmp1[2] = objz; + vec4Tmp1[3] = 1.0f; + + // vec4Tmp2 = Mv * o + // vec4Tmp1 = P * vec4Tmp2 + // vec4Tmp1 = P * ( Mv * o ) + // vec4Tmp1 = P * Mv * o + multMatrixVec(modelMatrix, modelMatrix_offset, vec4Tmp1, 0, vec4Tmp2, 0); + multMatrixVec(projMatrix, projMatrix_offset, vec4Tmp2, 0, vec4Tmp1, 0); + + if (vec4Tmp1[3] == 0.0f) { + return false; + } + + vec4Tmp1[3] = (1.0f / vec4Tmp1[3]) * 0.5f; + + // Map x, y and z to range 0-1 + vec4Tmp1[0] = vec4Tmp1[0] * vec4Tmp1[3] + 0.5f; + vec4Tmp1[1] = vec4Tmp1[1] * vec4Tmp1[3] + 0.5f; + vec4Tmp1[2] = vec4Tmp1[2] * vec4Tmp1[3] + 0.5f; + + // Map x,y to viewport + win_pos[0+win_pos_offset] = vec4Tmp1[0] * viewport[2+viewport_offset] + viewport[0+viewport_offset]; + win_pos[1+win_pos_offset] = vec4Tmp1[1] * viewport[3+viewport_offset] + viewport[1+viewport_offset]; + win_pos[2+win_pos_offset] = vec4Tmp1[2]; + + return true; + } + + /** + * Map object coordinates to window coordinates. + * <p> + * Traditional <code>gluProject</code> implementation. + * </p> + * + * @param objx + * @param objy + * @param objz + * @param mat4PMv [projection] x [modelview] matrix, i.e. P x Mv + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param win_pos 3 component window coordinate, the result + * @param win_pos_offset + * @param vec4Tmp1 4 component vector for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return true if successful, otherwise false (z is 1) + */ + public static boolean mapObjToWinCoords(final float objx, final float objy, final float objz, + final float[/*16*/] mat4PMv, + final int[] viewport, final int viewport_offset, + final float[] win_pos, final int win_pos_offset, + final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { + vec4Tmp2[0] = objx; + vec4Tmp2[1] = objy; + vec4Tmp2[2] = objz; + vec4Tmp2[3] = 1.0f; + + // vec4Tmp1 = P * Mv * o + multMatrixVec(mat4PMv, vec4Tmp2, vec4Tmp1); + + if (vec4Tmp1[3] == 0.0f) { + return false; + } + + vec4Tmp1[3] = (1.0f / vec4Tmp1[3]) * 0.5f; + + // Map x, y and z to range 0-1 + vec4Tmp1[0] = vec4Tmp1[0] * vec4Tmp1[3] + 0.5f; + vec4Tmp1[1] = vec4Tmp1[1] * vec4Tmp1[3] + 0.5f; + vec4Tmp1[2] = vec4Tmp1[2] * vec4Tmp1[3] + 0.5f; + + // Map x,y to viewport + win_pos[0+win_pos_offset] = vec4Tmp1[0] * viewport[2+viewport_offset] + viewport[0+viewport_offset]; + win_pos[1+win_pos_offset] = vec4Tmp1[1] * viewport[3+viewport_offset] + viewport[1+viewport_offset]; + win_pos[2+win_pos_offset] = vec4Tmp1[2]; + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param modelMatrix 4x4 modelview matrix + * @param modelMatrix_offset + * @param projMatrix 4x4 projection matrix + * @param projMatrix_offset + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param obj_pos 3 component object coordinate, the result + * @param obj_pos_offset + * @param mat4Tmp1 16 component matrix for temp storage + * @param mat4Tmp2 16 component matrix for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float[] obj_pos, final int obj_pos_offset, + final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { + // mat4Tmp1 = P x Mv + multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); + + // mat4Tmp1 = Inv(P x Mv) + if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) { + return false; + } + mat4Tmp2[0] = winx; + mat4Tmp2[1] = winy; + mat4Tmp2[2] = winz; + mat4Tmp2[3] = 1.0f; + + // Map x and y from window coordinates + mat4Tmp2[0] = (mat4Tmp2[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; + mat4Tmp2[1] = (mat4Tmp2[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; + + // Map to range -1 to 1 + mat4Tmp2[0] = mat4Tmp2[0] * 2 - 1; + mat4Tmp2[1] = mat4Tmp2[1] * 2 - 1; + mat4Tmp2[2] = mat4Tmp2[2] * 2 - 1; + + final int raw_off = 4; + // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 + multMatrixVec(mat4Tmp1, 0, mat4Tmp2, 0, mat4Tmp2, raw_off); + + if (mat4Tmp2[3+raw_off] == 0.0) { + return false; + } + + mat4Tmp2[3+raw_off] = 1.0f / mat4Tmp2[3+raw_off]; + + obj_pos[0+obj_pos_offset] = mat4Tmp2[0+raw_off] * mat4Tmp2[3+raw_off]; + obj_pos[1+obj_pos_offset] = mat4Tmp2[1+raw_off] * mat4Tmp2[3+raw_off]; + obj_pos[2+obj_pos_offset] = mat4Tmp2[2+raw_off] * mat4Tmp2[3+raw_off]; + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param mat4PMvI inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv) + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param obj_pos 3 component object coordinate, the result + * @param obj_pos_offset + * @param vec4Tmp1 4 component vector for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, + final float[/*16*/] mat4PMvI, + final int[] viewport, final int viewport_offset, + final float[] obj_pos, final int obj_pos_offset, + final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { + vec4Tmp1[0] = winx; + vec4Tmp1[1] = winy; + vec4Tmp1[2] = winz; + vec4Tmp1[3] = 1.0f; + + // Map x and y from window coordinates + vec4Tmp1[0] = (vec4Tmp1[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; + vec4Tmp1[1] = (vec4Tmp1[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; + + // Map to range -1 to 1 + vec4Tmp1[0] = vec4Tmp1[0] * 2 - 1; + vec4Tmp1[1] = vec4Tmp1[1] * 2 - 1; + vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; + + // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 + multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); + + if (vec4Tmp2[3] == 0.0) { + return false; + } + + vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; + + obj_pos[0+obj_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; + obj_pos[1+obj_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; + obj_pos[2+obj_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; + + return true; + } + + /** + * Map two window coordinates to two object coordinates, + * distinguished by their z component. + * + * @param winx + * @param winy + * @param winz1 + * @param winz2 + * @param mat4PMvI inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv) + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param obj1_pos 3 component object coordinate, the result for winz1 + * @param obj1_pos_offset + * @param obj2_pos 3 component object coordinate, the result for winz2 + * @param obj2_pos_offset + * @param vec4Tmp1 4 component vector for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz1, final float winz2, + final float[/*16*/] mat4PMvI, + final int[] viewport, final int viewport_offset, + final float[] obj1_pos, final int obj1_pos_offset, + final float[] obj2_pos, final int obj2_pos_offset, + final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { + vec4Tmp1[0] = winx; + vec4Tmp1[1] = winy; + vec4Tmp1[3] = 1.0f; + + // Map x and y from window coordinates + vec4Tmp1[0] = (vec4Tmp1[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; + vec4Tmp1[1] = (vec4Tmp1[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; + + // Map to range -1 to 1 + vec4Tmp1[0] = vec4Tmp1[0] * 2 - 1; + vec4Tmp1[1] = vec4Tmp1[1] * 2 - 1; + + // + // winz1 + // + vec4Tmp1[2] = winz1; + vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; + + // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 + multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); + + if (vec4Tmp2[3] == 0.0) { + return false; + } + + vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; + + obj1_pos[0+obj1_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; + obj1_pos[1+obj1_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; + obj1_pos[2+obj1_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; + + // + // winz2 + // + vec4Tmp1[2] = winz2; + vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; + + // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 + multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); + + if (vec4Tmp2[3] == 0.0) { + return false; + } + + vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; + + obj2_pos[0+obj2_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; + obj2_pos[1+obj2_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; + obj2_pos[2+obj2_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject4</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param clipw + * @param modelMatrix 4x4 modelview matrix + * @param modelMatrix_offset + * @param projMatrix 4x4 projection matrix + * @param projMatrix_offset + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param near + * @param far + * @param obj_pos 4 component object coordinate, the result + * @param obj_pos_offset + * @param mat4Tmp1 16 component matrix for temp storage + * @param mat4Tmp2 16 component matrix for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, final float clipw, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float near, final float far, + final float[] obj_pos, final int obj_pos_offset, + final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { + // mat4Tmp1 = P x Mv + multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); + + // mat4Tmp1 = Inv(P x Mv) + if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) { + return false; + } + + mat4Tmp2[0] = winx; + mat4Tmp2[1] = winy; + mat4Tmp2[2] = winz; + mat4Tmp2[3] = 1.0f; + + // Map x and y from window coordinates + mat4Tmp2[0] = (mat4Tmp2[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; + mat4Tmp2[1] = (mat4Tmp2[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; + mat4Tmp2[2] = (mat4Tmp2[2] - near) / (far - near); + + // Map to range -1 to 1 + mat4Tmp2[0] = mat4Tmp2[0] * 2 - 1; + mat4Tmp2[1] = mat4Tmp2[1] * 2 - 1; + mat4Tmp2[2] = mat4Tmp2[2] * 2 - 1; + + final int raw_off = 4; + // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 + multMatrixVec(mat4Tmp1, 0, mat4Tmp2, 0, mat4Tmp2, raw_off); + + if (mat4Tmp2[3+raw_off] == 0.0) { + return false; + } + + mat4Tmp2[3+raw_off] = 1.0f / mat4Tmp2[3+raw_off]; + + obj_pos[0+obj_pos_offset] = mat4Tmp2[0+raw_off]; + obj_pos[1+obj_pos_offset] = mat4Tmp2[1+raw_off]; + obj_pos[2+obj_pos_offset] = mat4Tmp2[2+raw_off]; + obj_pos[3+obj_pos_offset] = mat4Tmp2[3+raw_off]; + + return true; + } + + + /** + * Map two window coordinates w/ shared X/Y and distinctive Z + * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> + * using a {@link AABBox#getRayIntersection(Ray, float[]) bounding box}. + * <p> + * Notes for picking <i>winz0</i> and <i>winz1</i>: + * <ul> + * <li>see {@link #getZBufferEpsilon(int, float, float)}</li> + * <li>see {@link #getZBufferValue(int, float, float, float)}</li> + * <li>see {@link #getOrthoWinZ(float, float, float)}</li> + * </ul> + * </p> + * @param winx + * @param winy + * @param winz0 + * @param winz1 + * @param modelMatrix 4x4 modelview matrix + * @param modelMatrix_offset + * @param projMatrix 4x4 projection matrix + * @param projMatrix_offset + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param ray storage for the resulting {@link Ray} + * @param mat4Tmp1 16 component matrix for temp storage + * @param mat4Tmp2 16 component matrix for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity) + */ + public static boolean mapWinToRay(final float winx, final float winy, final float winz0, final float winz1, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final Ray ray, + final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2, final float[/*4*/] vec4Tmp2) { + // mat4Tmp1 = P x Mv + multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); + + // mat4Tmp1 = Inv(P x Mv) + if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) { + return false; + } + if( mapWinToObjCoords(winx, winy, winz0, winz1, mat4Tmp1, + viewport, viewport_offset, + ray.orig, 0, ray.dir, 0, + mat4Tmp2, vec4Tmp2) ) { + VectorUtil.normalizeVec3( VectorUtil.subVec3(ray.dir, ray.dir, ray.orig) ); + return true; + } else { + return false; + } + } + + /** + * Multiply matrix: [d] = [a] x [b] + * @param a 4x4 matrix in column-major order + * @param b 4x4 matrix in column-major order + * @param d result a*b in column-major order + * @return given result matrix <i>d</i> for chaining + */ + public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off, final float[] d, final int d_off) { + final float b00 = b[b_off+0+0*4]; + final float b10 = b[b_off+1+0*4]; + final float b20 = b[b_off+2+0*4]; + final float b30 = b[b_off+3+0*4]; + final float b01 = b[b_off+0+1*4]; + final float b11 = b[b_off+1+1*4]; + final float b21 = b[b_off+2+1*4]; + final float b31 = b[b_off+3+1*4]; + final float b02 = b[b_off+0+2*4]; + final float b12 = b[b_off+1+2*4]; + final float b22 = b[b_off+2+2*4]; + final float b32 = b[b_off+3+2*4]; + final float b03 = b[b_off+0+3*4]; + final float b13 = b[b_off+1+3*4]; + final float b23 = b[b_off+2+3*4]; + final float b33 = b[b_off+3+3*4]; + + float ai0=a[a_off+ 0*4]; // row-0 of a + float ai1=a[a_off+ 1*4]; + float ai2=a[a_off+ 2*4]; + float ai3=a[a_off+ 3*4]; + d[d_off+ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[d_off+ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[d_off+ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[d_off+ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+1+0*4]; // row-1 of a + ai1=a[a_off+1+1*4]; + ai2=a[a_off+1+2*4]; + ai3=a[a_off+1+3*4]; + d[d_off+1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[d_off+1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[d_off+1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[d_off+1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+2+0*4]; // row-2 of a + ai1=a[a_off+2+1*4]; + ai2=a[a_off+2+2*4]; + ai3=a[a_off+2+3*4]; + d[d_off+2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[d_off+2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[d_off+2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[d_off+2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+3+0*4]; // row-3 of a + ai1=a[a_off+3+1*4]; + ai2=a[a_off+3+2*4]; + ai3=a[a_off+3+3*4]; + d[d_off+3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[d_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[d_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[d_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + return d; + } + + /** + * Multiply matrix: [d] = [a] x [b] + * @param a 4x4 matrix in column-major order + * @param b 4x4 matrix in column-major order + * @param d result a*b in column-major order + * @return given result matrix <i>d</i> for chaining + */ + public static float[] multMatrix(final float[] a, final float[] b, final float[] d) { + final float b00 = b[0+0*4]; + final float b10 = b[1+0*4]; + final float b20 = b[2+0*4]; + final float b30 = b[3+0*4]; + final float b01 = b[0+1*4]; + final float b11 = b[1+1*4]; + final float b21 = b[2+1*4]; + final float b31 = b[3+1*4]; + final float b02 = b[0+2*4]; + final float b12 = b[1+2*4]; + final float b22 = b[2+2*4]; + final float b32 = b[3+2*4]; + final float b03 = b[0+3*4]; + final float b13 = b[1+3*4]; + final float b23 = b[2+3*4]; + final float b33 = b[3+3*4]; + + float ai0=a[ 0*4]; // row-0 of a + float ai1=a[ 1*4]; + float ai2=a[ 2*4]; + float ai3=a[ 3*4]; + d[ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[1+0*4]; // row-1 of a + ai1=a[1+1*4]; + ai2=a[1+2*4]; + ai3=a[1+3*4]; + d[1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[2+0*4]; // row-2 of a + ai1=a[2+1*4]; + ai2=a[2+2*4]; + ai3=a[2+3*4]; + d[2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[3+0*4]; // row-3 of a + ai1=a[3+1*4]; + ai2=a[3+2*4]; + ai3=a[3+3*4]; + d[3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + d[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + d[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + d[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + return d; + } + + /** + * Multiply matrix: [a] = [a] x [b] + * @param a 4x4 matrix in column-major order (also result) + * @param b 4x4 matrix in column-major order + * @return given result matrix <i>a</i> for chaining + */ + public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off) { + final float b00 = b[b_off+0+0*4]; + final float b10 = b[b_off+1+0*4]; + final float b20 = b[b_off+2+0*4]; + final float b30 = b[b_off+3+0*4]; + final float b01 = b[b_off+0+1*4]; + final float b11 = b[b_off+1+1*4]; + final float b21 = b[b_off+2+1*4]; + final float b31 = b[b_off+3+1*4]; + final float b02 = b[b_off+0+2*4]; + final float b12 = b[b_off+1+2*4]; + final float b22 = b[b_off+2+2*4]; + final float b32 = b[b_off+3+2*4]; + final float b03 = b[b_off+0+3*4]; + final float b13 = b[b_off+1+3*4]; + final float b23 = b[b_off+2+3*4]; + final float b33 = b[b_off+3+3*4]; + + float ai0=a[a_off+ 0*4]; // row-0 of a + float ai1=a[a_off+ 1*4]; + float ai2=a[a_off+ 2*4]; + float ai3=a[a_off+ 3*4]; + a[a_off+ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[a_off+ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[a_off+ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[a_off+ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+1+0*4]; // row-1 of a + ai1=a[a_off+1+1*4]; + ai2=a[a_off+1+2*4]; + ai3=a[a_off+1+3*4]; + a[a_off+1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[a_off+1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[a_off+1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[a_off+1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+2+0*4]; // row-2 of a + ai1=a[a_off+2+1*4]; + ai2=a[a_off+2+2*4]; + ai3=a[a_off+2+3*4]; + a[a_off+2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[a_off+2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[a_off+2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[a_off+2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[a_off+3+0*4]; // row-3 of a + ai1=a[a_off+3+1*4]; + ai2=a[a_off+3+2*4]; + ai3=a[a_off+3+3*4]; + a[a_off+3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[a_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[a_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[a_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + return a; + } + + /** + * Multiply matrix: [a] = [a] x [b] + * @param a 4x4 matrix in column-major order (also result) + * @param b 4x4 matrix in column-major order + * @return given result matrix <i>a</i> for chaining + */ + public static float[] multMatrix(final float[] a, final float[] b) { + final float b00 = b[0+0*4]; + final float b10 = b[1+0*4]; + final float b20 = b[2+0*4]; + final float b30 = b[3+0*4]; + final float b01 = b[0+1*4]; + final float b11 = b[1+1*4]; + final float b21 = b[2+1*4]; + final float b31 = b[3+1*4]; + final float b02 = b[0+2*4]; + final float b12 = b[1+2*4]; + final float b22 = b[2+2*4]; + final float b32 = b[3+2*4]; + final float b03 = b[0+3*4]; + final float b13 = b[1+3*4]; + final float b23 = b[2+3*4]; + final float b33 = b[3+3*4]; + + float ai0=a[ 0*4]; // row-0 of a + float ai1=a[ 1*4]; + float ai2=a[ 2*4]; + float ai3=a[ 3*4]; + a[ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[1+0*4]; // row-1 of a + ai1=a[1+1*4]; + ai2=a[1+2*4]; + ai3=a[1+3*4]; + a[1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[2+0*4]; // row-2 of a + ai1=a[2+1*4]; + ai2=a[2+2*4]; + ai3=a[2+3*4]; + a[2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + ai0=a[3+0*4]; // row-3 of a + ai1=a[3+1*4]; + ai2=a[3+2*4]; + ai3=a[3+3*4]; + a[3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ; + a[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; + a[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; + a[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; + + return a; + } + + /** + * Multiply matrix: [d] = [a] x [b] + * @param a 4x4 matrix in column-major order + * @param b 4x4 matrix in column-major order + * @param d result a*b in column-major order + * @deprecated use on of the float[] variants + */ + public static void multMatrix(final FloatBuffer a, final FloatBuffer b, final float[] d) { + final int a_off = a.position(); + final int b_off = b.position(); + for (int i = 0; i < 4; i++) { + // one row in column-major order + final int a_off_i = a_off+i; + final float ai0=a.get(a_off_i+0*4), ai1=a.get(a_off_i+1*4), ai2=a.get(a_off_i+2*4), ai3=a.get(a_off_i+3*4); // row-i of a + d[i+0*4] = ai0 * b.get(b_off+0+0*4) + ai1 * b.get(b_off+1+0*4) + ai2 * b.get(b_off+2+0*4) + ai3 * b.get(b_off+3+0*4) ; + d[i+1*4] = ai0 * b.get(b_off+0+1*4) + ai1 * b.get(b_off+1+1*4) + ai2 * b.get(b_off+2+1*4) + ai3 * b.get(b_off+3+1*4) ; + d[i+2*4] = ai0 * b.get(b_off+0+2*4) + ai1 * b.get(b_off+1+2*4) + ai2 * b.get(b_off+2+2*4) + ai3 * b.get(b_off+3+2*4) ; + d[i+3*4] = ai0 * b.get(b_off+0+3*4) + ai1 * b.get(b_off+1+3*4) + ai2 * b.get(b_off+2+3*4) + ai3 * b.get(b_off+3+3*4) ; + } + } + + /** + * Multiply matrix: [a] = [a] x [b] + * @param a 4x4 matrix in column-major order (also result) + * @param b 4x4 matrix in column-major order + * @deprecated use on of the float[] variants + */ + public static void multMatrix(final FloatBuffer a, final FloatBuffer b) { + final int a_off = a.position(); + final int b_off = b.position(); + for (int i = 0; i < 4; i++) { + // one row in column-major order + final int a_off_i = a_off+i; + final float ai0=a.get(a_off_i+0*4), ai1=a.get(a_off_i+1*4), ai2=a.get(a_off_i+2*4), ai3=a.get(a_off_i+3*4); // row-i of a + a.put(a_off_i+0*4 , ai0 * b.get(b_off+0+0*4) + ai1 * b.get(b_off+1+0*4) + ai2 * b.get(b_off+2+0*4) + ai3 * b.get(b_off+3+0*4) ); + a.put(a_off_i+1*4 , ai0 * b.get(b_off+0+1*4) + ai1 * b.get(b_off+1+1*4) + ai2 * b.get(b_off+2+1*4) + ai3 * b.get(b_off+3+1*4) ); + a.put(a_off_i+2*4 , ai0 * b.get(b_off+0+2*4) + ai1 * b.get(b_off+1+2*4) + ai2 * b.get(b_off+2+2*4) + ai3 * b.get(b_off+3+2*4) ); + a.put(a_off_i+3*4 , ai0 * b.get(b_off+0+3*4) + ai1 * b.get(b_off+1+3*4) + ai2 * b.get(b_off+2+3*4) + ai3 * b.get(b_off+3+3*4) ); + } + } + + /** + * @param m_in 4x4 matrix in column-major order + * @param m_in_off + * @param v_in 4-component column-vector + * @param v_out m_in * v_in + * @return given result vector <i>v_out</i> for chaining + */ + public static float[] multMatrixVec(final float[] m_in, final int m_in_off, + final float[] v_in, final int v_in_off, + final float[] v_out, final int v_out_off) { + // (one matrix row in column-major order) X (column vector) + v_out[0 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off ] + v_in[1+v_in_off] * m_in[1*4+m_in_off ] + + v_in[2+v_in_off] * m_in[2*4+m_in_off ] + v_in[3+v_in_off] * m_in[3*4+m_in_off ]; + + final int m_in_off_1 = 1+m_in_off; + v_out[1 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_1] + v_in[1+v_in_off] * m_in[1*4+m_in_off_1] + + v_in[2+v_in_off] * m_in[2*4+m_in_off_1] + v_in[3+v_in_off] * m_in[3*4+m_in_off_1]; + + final int m_in_off_2 = 2+m_in_off; + v_out[2 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_2] + v_in[1+v_in_off] * m_in[1*4+m_in_off_2] + + v_in[2+v_in_off] * m_in[2*4+m_in_off_2] + v_in[3+v_in_off] * m_in[3*4+m_in_off_2]; + + final int m_in_off_3 = 3+m_in_off; + v_out[3 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_3] + v_in[1+v_in_off] * m_in[1*4+m_in_off_3] + + v_in[2+v_in_off] * m_in[2*4+m_in_off_3] + v_in[3+v_in_off] * m_in[3*4+m_in_off_3]; + + return v_out; + } + + /** + * @param m_in 4x4 matrix in column-major order + * @param m_in_off + * @param v_in 4-component column-vector + * @param v_out m_in * v_in + * @return given result vector <i>v_out</i> for chaining + */ + public static float[] multMatrixVec(final float[] m_in, final float[] v_in, final float[] v_out) { + // (one matrix row in column-major order) X (column vector) + v_out[0] = v_in[0] * m_in[0*4 ] + v_in[1] * m_in[1*4 ] + + v_in[2] * m_in[2*4 ] + v_in[3] * m_in[3*4 ]; + + v_out[1] = v_in[0] * m_in[0*4+1] + v_in[1] * m_in[1*4+1] + + v_in[2] * m_in[2*4+1] + v_in[3] * m_in[3*4+1]; + + v_out[2] = v_in[0] * m_in[0*4+2] + v_in[1] * m_in[1*4+2] + + v_in[2] * m_in[2*4+2] + v_in[3] * m_in[3*4+2]; + + v_out[3] = v_in[0] * m_in[0*4+3] + v_in[1] * m_in[1*4+3] + + v_in[2] * m_in[2*4+3] + v_in[3] * m_in[3*4+3]; + + return v_out; + } + + /** + * @param m_in 4x4 matrix in column-major order + * @param v_in 4-component column-vector + * @param v_out m_in * v_in + * @deprecated use on of the float[] variants + */ + public static void multMatrixVec(final FloatBuffer m_in, final float[] v_in, final float[] v_out) { + final int m_in_off = m_in.position(); + for (int i = 0; i < 4; i++) { + // (one matrix row in column-major order) X (column vector) + final int i_m_in_off = i+m_in_off; + v_out[i] = + v_in[0] * m_in.get(0*4+i_m_in_off) + + v_in[1] * m_in.get(1*4+i_m_in_off) + + v_in[2] * m_in.get(2*4+i_m_in_off) + + v_in[3] * m_in.get(3*4+i_m_in_off); + } + } + + /** + * Copy the named column of the given column-major matrix to v_out. + * <p> + * v_out may be 3 or 4 components long, hence the 4th row may not be stored. + * </p> + * @param m_in input column-major matrix + * @param m_in_off offset to input matrix + * @param column named column to copy + * @param v_out the column-vector storage, at least 3 components long + * @param v_out_off offset to storage + * @return given result vector <i>v_out</i> for chaining + */ + public static float[] copyMatrixColumn(final float[] m_in, final int m_in_off, final int column, final float[] v_out, final int v_out_off) { + v_out[0+v_out_off]=m_in[0+column*4+m_in_off]; + v_out[1+v_out_off]=m_in[1+column*4+m_in_off]; + v_out[2+v_out_off]=m_in[2+column*4+m_in_off]; + if( v_out.length > 3+v_out_off ) { + v_out[3+v_out_off]=m_in[3+column*4+m_in_off]; + } + return v_out; + } + + /** + * Copy the named row of the given column-major matrix to v_out. + * <p> + * v_out may be 3 or 4 components long, hence the 4th column may not be stored. + * </p> + * @param m_in input column-major matrix + * @param m_in_off offset to input matrix + * @param row named row to copy + * @param v_out the row-vector storage, at least 3 components long + * @param v_out_off offset to storage + * @return given result vector <i>v_out</i> for chaining + */ + public static float[] copyMatrixRow(final float[] m_in, final int m_in_off, final int row, final float[] v_out, final int v_out_off) { + v_out[0+v_out_off]=m_in[row+0*4+m_in_off]; + v_out[1+v_out_off]=m_in[row+1*4+m_in_off]; + v_out[2+v_out_off]=m_in[row+2*4+m_in_off]; + if( v_out.length > 3+v_out_off ) { + v_out[3+v_out_off]=m_in[row+3*4+m_in_off]; + } + return v_out; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a mxn matrix (rows x columns) + * @param aOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @param row row number to print + * @return matrix row string representation + * @deprecated use on of the float[] variants + */ + public static StringBuilder matrixRowToString(StringBuilder sb, final String f, + final FloatBuffer a, final int aOffset, + final int rows, final int columns, final boolean rowMajorOrder, final int row) { + if(null == sb) { + sb = new StringBuilder(); + } + final int a0 = aOffset + a.position(); + if(rowMajorOrder) { + for(int c=0; c<columns; c++) { + sb.append( String.format( f+" ", a.get( a0 + row*columns + c ) ) ); + } + } else { + for(int r=0; r<columns; r++) { + sb.append( String.format( f+" ", a.get( a0 + row + r*rows ) ) ); + } + } + return sb; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a mxn matrix (rows x columns) + * @param aOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @param row row number to print + * @return matrix row string representation + */ + public static StringBuilder matrixRowToString(StringBuilder sb, final String f, + final float[] a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder, final int row) { + if(null == sb) { + sb = new StringBuilder(); + } + if(rowMajorOrder) { + for(int c=0; c<columns; c++) { + sb.append( String.format( f+" ", a[ aOffset + row*columns + c ] ) ); + } + } else { + for(int r=0; r<columns; r++) { + sb.append( String.format( f+" ", a[ aOffset + row + r*rows ] ) ); + } + } + return sb; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param rowPrefix optional prefix for each row + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a mxn matrix (rows x columns) + * @param aOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @return matrix string representation + * @deprecated use on of the float[] variants + */ + public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, + final FloatBuffer a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder) { + if(null == sb) { + sb = new StringBuilder(); + } + final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + for(int i=0; i<rows; i++) { + sb.append(prefix).append("[ "); + matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); + sb.append("]").append(Platform.getNewline()); + } + return sb; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param rowPrefix optional prefix for each row + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a mxn matrix (rows x columns) + * @param aOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @return matrix string representation + */ + public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, + final float[] a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder) { + if(null == sb) { + sb = new StringBuilder(); + } + final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + for(int i=0; i<rows; i++) { + sb.append(prefix).append("[ "); + matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); + sb.append("]").append(Platform.getNewline()); + } + return sb; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param rowPrefix optional prefix for each row + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a 4x4 matrix in column major order (OpenGL) + * @param aOffset offset to <code>a</code>'s current position + * @param b 4x4 matrix in column major order (OpenGL) + * @param bOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @return side by side representation + * @deprecated use on of the float[] variants + */ + public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, + final FloatBuffer a, final int aOffset, final FloatBuffer b, final int bOffset, + final int rows, final int columns, final boolean rowMajorOrder) { + if(null == sb) { + sb = new StringBuilder(); + } + final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + for(int i=0; i<rows; i++) { + sb.append(prefix).append("[ "); + matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); + sb.append("=?= "); + matrixRowToString(sb, f, b, bOffset, rows, columns, rowMajorOrder, i); + sb.append("]").append(Platform.getNewline()); + } + return sb; + } + + /** + * @param sb optional passed StringBuilder instance to be used + * @param rowPrefix optional prefix for each row + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a 4x4 matrix in column major order (OpenGL) + * @param aOffset offset to <code>a</code>'s current position + * @param b 4x4 matrix in column major order (OpenGL) + * @param bOffset offset to <code>a</code>'s current position + * @param rows + * @param columns + * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) + * @return side by side representation + */ + public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, + final float[] a, final int aOffset, final float[] b, final int bOffset, + final int rows, final int columns, final boolean rowMajorOrder) { + if(null == sb) { + sb = new StringBuilder(); + } + final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + for(int i=0; i<rows; i++) { + sb.append(prefix).append("[ "); + matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); + sb.append("=?= "); + matrixRowToString(sb, f, b, bOffset, rows, columns, rowMajorOrder, i); + sb.append("]").append(Platform.getNewline()); + } + return sb; + } + + // + // Scalar Ops + // + + @SuppressWarnings("unused") + private static void calculateMachineEpsilonFloat() { + final long t0; + if( DEBUG_EPSILON ) { + t0 = Platform.currentTimeMillis(); + } + float machEps = 1.0f; + int i=0; + do { + machEps /= 2.0f; + i++; + } while (1.0f + (machEps / 2.0f) != 1.0f); + machEpsilon = machEps; + if( DEBUG_EPSILON ) { + final long t1 = Platform.currentTimeMillis(); + System.err.println("MachineEpsilon: "+machEpsilon+", in "+i+" iterations within "+(t1-t0)+" ms"); + } + } + private static volatile boolean machEpsilonAvail = false; + private static float machEpsilon = 0f; + private static final boolean DEBUG_EPSILON = false; + + /** + * Return computed machine Epsilon value. + * <p> + * The machine Epsilon value is computed once. + * </p> + * <p> + * On a reference machine the result was {@link #EPSILON} in 23 iterations. + * </p> + * @see #EPSILON + */ + public static float getMachineEpsilon() { + if( !machEpsilonAvail ) { + synchronized(FloatUtil.class) { + if( !machEpsilonAvail ) { + machEpsilonAvail = true; + calculateMachineEpsilonFloat(); + } + } + } + return machEpsilon; + } + + public static final float E = 2.7182818284590452354f; + + /** The value PI, i.e. 180 degrees in radians. */ + public static final float PI = 3.14159265358979323846f; + + /** The value 2PI, i.e. 360 degrees in radians. */ + public static final float TWO_PI = 2f * PI; + + /** The value PI/2, i.e. 90 degrees in radians. */ + public static final float HALF_PI = PI / 2f; + + /** The value PI/4, i.e. 45 degrees in radians. */ + public static final float QUARTER_PI = PI / 4f; + + /** The value PI^2. */ + public final static float SQUARED_PI = PI * PI; + + /** + * Epsilon for floating point {@value}, as once computed via {@link #getMachineEpsilon()} on an AMD-64 CPU. + * <p> + * Definition of machine epsilon guarantees that: + * <pre> + * 1.0f + EPSILON != 1.0f + * </pre> + * In other words: <i>machEps</i> is the maximum relative error of the chosen rounding procedure. + * </p> + * <p> + * A number can be considered zero if it is in the range (or in the set): + * <pre> + * <b>MaybeZeroSet</b> e ]-<i>machEps</i> .. <i>machEps</i>[ <i>(exclusive)</i> + * </pre> + * While comparing floating point values, <i>machEps</i> allows to clip the relative error: + * <pre> + * boolean isZero = afloat < EPSILON; + * boolean isNotZero = afloat >= EPSILON; + * + * boolean isEqual = abs(bfloat - afloat) < EPSILON; + * boolean isNotEqual = abs(bfloat - afloat) >= EPSILON; + * </pre> + * </p> + * @see #isEqual(float, float, float) + * @see #isZero(float, float) + */ + public static final float EPSILON = 1.1920929E-7f; // Float.MIN_VALUE == 1.4e-45f ; double EPSILON 2.220446049250313E-16d + + /** + * Inversion Epsilon, used with equals method to determine if two inverted matrices are close enough to be considered equal. + * <p> + * Using {@value}, which is ~100 times {@link FloatUtil#EPSILON}. + * </p> + */ + public static final float INV_DEVIANCE = 1.0E-5f; // FloatUtil.EPSILON == 1.1920929E-7f; double ALLOWED_DEVIANCE: 1.0E-8f + + /** + * Return true if both values are equal w/o regarding an epsilon. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * </p> + * @see #isEqual(float, float, float) + */ + public static boolean isEqual(final float a, final float b) { + // Values are equal (Inf, Nan .. ) + return Float.floatToIntBits(a) == Float.floatToIntBits(b); + } + + /** + * Return true if both values are equal, i.e. their absolute delta < <code>epsilon</code>. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * </p> + * @see #EPSILON + */ + public static boolean isEqual(final float a, final float b, final float epsilon) { + if ( Math.abs(a - b) < epsilon ) { + return true; + } else { + // Values are equal (Inf, Nan .. ) + return Float.floatToIntBits(a) == Float.floatToIntBits(b); + } + } + + /** + * Return true if both values are equal w/o regarding an epsilon. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * <li>NaN > 0</li> + * <li>+Inf > -Inf</li> + * </ul> + * </p> + * @see #compare(float, float, float) + */ + public static int compare(final float a, final float b) { + if (a < b) { + return -1; // Neither is NaN, a is smaller + } + if (a > b) { + return 1; // Neither is NaN, a is larger + } + final int aBits = Float.floatToIntBits(a); + final int bBits = Float.floatToIntBits(b); + if( aBits == bBits ) { + return 0; // Values are equal (Inf, Nan .. ) + } else if( aBits < bBits ) { + return -1; // (-0.0, 0.0) or (!NaN, NaN) + } else { + return 1; // ( 0.0, -0.0) or ( NaN, !NaN) + } + } + + /** + * Return true if both values are equal, i.e. their absolute delta < <code>epsilon</code>. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * <li>NaN > 0</li> + * <li>+Inf > -Inf</li> + * </ul> + * </p> + * @see #EPSILON + */ + public static int compare(final float a, final float b, final float epsilon) { + if ( Math.abs(a - b) < epsilon ) { + return 0; + } else { + return compare(a, b); + } + } + + /** + * Return true if value is zero, i.e. it's absolute value < <code>epsilon</code>. + * @see #EPSILON + */ + public static boolean isZero(final float a, final float epsilon) { + return Math.abs(a) < epsilon; + } + + public static float abs(final float a) { return java.lang.Math.abs(a); } + + public static float pow(final float a, final float b) { return (float) java.lang.Math.pow(a, b); } + + public static float sin(final float a) { return (float) java.lang.Math.sin(a); } + + public static float asin(final float a) { return (float) java.lang.Math.asin(a); } + + public static float cos(final float a) { return (float) java.lang.Math.cos(a); } + + public static float acos(final float a) { return (float) java.lang.Math.acos(a); } + + public static float tan(final float a) { return (float) java.lang.Math.tan(a); } + + public static float atan(final float a) { return (float) java.lang.Math.atan(a); } + + public static float atan2(final float y, final float x) { return (float) java.lang.Math.atan2(y, x); } + + public static float sqrt(final float a) { return (float) java.lang.Math.sqrt(a); } + + /** + * Returns resolution of Z buffer of given parameter, + * see <a href="http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html">Love Your Z-Buffer</a>. + * <pre> + * return z * z / ( zNear * (1<<zBits) - z ) + * </pre> + * @param zBits number of bits of Z precision, i.e. z-buffer depth + * @param z distance from the eye to the object + * @param zNear distance from eye to near clip plane + * @return smallest resolvable Z separation at this range. + */ + public static float getZBufferEpsilon(final int zBits, final float z, final float zNear) { + return z * z / ( zNear * ( 1 << zBits ) - z ); + } + + /** + * Returns Z buffer value of given parameter, + * see <a href="http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html">Love Your Z-Buffer</a>. + * <pre> + * float a = zFar / ( zFar - zNear ) + * float b = zFar * zNear / ( zNear - zFar ) + * return (int) ( (1<<zBits) * ( a + b / z ) ) + * </pre> + * @param zBits number of bits of Z precision, i.e. z-buffer depth + * @param z distance from the eye to the object + * @param zNear distance from eye to near clip plane + * @param zFar distance from eye to far clip plane + * @return z buffer value + */ + public static int getZBufferValue(final int zBits, final float z, final float zNear, final float zFar) { + final float a = zFar / ( zFar - zNear ); + final float b = zFar * zNear / ( zNear - zFar ); + return (int) ( (1<<zBits) * ( a + b / z ) ); + } + + /** + * Returns orthogonal distance + * (1f/zNear-1f/orthoDist)/(1f/zNear-1f/zFar); + */ + public static float getOrthoWinZ(final float orthoZ, final float zNear, final float zFar) { + return (1f/zNear-1f/orthoZ)/(1f/zNear-1f/zFar); + } + +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/math/FovHVHalves.java b/src/jogl/classes/com/jogamp/opengl/math/FovHVHalves.java new file mode 100644 index 000000000..26ed57009 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/FovHVHalves.java @@ -0,0 +1,182 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +/** + * Horizontal and vertical field of view (FOV) halves, + * allowing a non-centered projection. + * <p> + * The values might be either in tangent or radians. + * </p> + */ +public final class FovHVHalves { + /** Half horizontal FOV from center to left. */ + public final float left; + /** Half horizontal FOV from center to right. */ + public final float right; + /** Half vertical FOV from center to top. */ + public final float top; + /** Half vertical FOV from center to bottom. */ + public final float bottom; + /** If true, values are in tangent, otherwise radians.*/ + public final boolean inTangents; + + /** + * Constructor for one {@link FovHVHalves} instance. + * <p> + * It is recommended to pass and store values in tangent + * if used for perspective FOV calculations, since it will avoid conversion to tangent later on. + * </p> + * @param left half horizontal FOV, left side, in tangent or radians + * @param right half horizontal FOV, right side, in tangent or radians + * @param top half vertical FOV, top side, in tangent or radians + * @param bottom half vertical FOV, bottom side, in tangent or radians + * @param inTangents if true, values are in tangent, otherwise radians + */ + public FovHVHalves(final float left, final float right, final float top, final float bottom, final boolean inTangents) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + this.inTangents = inTangents; + } + + /** + * Returns a symmetrical centered {@link FovHVHalves} instance in tangents, using: + * <pre> + halfHorizFovTan = tan( horizontalFov / 2f ); + halfVertFovTan = tan( verticalFov / 2f ); + * </pre> + * @param horizontalFov whole horizontal FOV in radians + * @param verticalFov whole vertical FOV in radians + */ + public static FovHVHalves byRadians(final float horizontalFov, final float verticalFov) { + final float halfHorizFovTan = FloatUtil.tan(horizontalFov/2f); + final float halfVertFovTan = FloatUtil.tan(verticalFov/2f); + return new FovHVHalves(halfHorizFovTan, halfHorizFovTan, halfVertFovTan, halfVertFovTan, true); + } + + /** + * Returns a symmetrical centered {@link FovHVHalves} instance in tangents, using: + * <pre> + top = bottom = tan( verticalFov / 2f ); + left = right = aspect * top; + * </pre> + * + * @param verticalFov vertical FOV in radians + * @param aspect aspect ration width / height + */ + public static FovHVHalves byFovyRadianAndAspect(final float verticalFov, final float aspect) { + final float halfVertFovTan = FloatUtil.tan(verticalFov/2f); + final float halfHorizFovTan = aspect * halfVertFovTan; + return new FovHVHalves(halfHorizFovTan, halfHorizFovTan, + halfVertFovTan, halfVertFovTan, true); + } + + /** + * Returns a custom symmetry {@link FovHVHalves} instance in tangents, using: + * <pre> + left = tan( horizontalFov * horizCenterFromLeft ) + right = tan( horizontalFov * ( 1f - horizCenterFromLeft ) ) + top = tan( verticalFov * vertCenterFromTop ) + bottom = tan( verticalFov * (1f - vertCenterFromTop ) ) + * </pre> + * @param horizontalFov whole horizontal FOV in radians + * @param horizCenterFromLeft horizontal center from left in [0..1] + * @param verticalFov whole vertical FOV in radians + * @param vertCenterFromTop vertical center from top in [0..1] + */ + public static FovHVHalves byRadians(final float horizontalFov, final float horizCenterFromLeft, + final float verticalFov, final float vertCenterFromTop) { + return new FovHVHalves(FloatUtil.tan(horizontalFov * horizCenterFromLeft), + FloatUtil.tan(horizontalFov * ( 1f - horizCenterFromLeft )), + FloatUtil.tan(verticalFov * vertCenterFromTop), + FloatUtil.tan(verticalFov * (1f - vertCenterFromTop )), + true); + } + + /** + * Returns a custom symmetry {@link FovHVHalves} instance in tangents, + * via computing the <code>horizontalFov</code> using: + * <pre> + halfVertFovTan = tan( verticalFov / 2f ); + halfHorizFovTan = aspect * halfVertFovTan; + horizontalFov = atan( halfHorizFovTan ) * 2f; + return {@link #byRadians(float, float, float, float) byRadians}(horizontalFov, horizCenterFromLeft, verticalFov, vertCenterFromTop) + * </pre> + * @param verticalFov whole vertical FOV in radians + * @param vertCenterFromTop vertical center from top in [0..1] + * @param aspect aspect ration width / height + * @param horizCenterFromLeft horizontal center from left in [0..1] + */ + public static FovHVHalves byFovyRadianAndAspect(final float verticalFov, final float vertCenterFromTop, + final float aspect, final float horizCenterFromLeft) { + final float halfVertFovTan = FloatUtil.tan(verticalFov/2f); + final float halfHorizFovTan = aspect * halfVertFovTan; + final float horizontalFov = FloatUtil.atan(halfHorizFovTan) * 2f; + return byRadians(horizontalFov, horizCenterFromLeft, verticalFov, vertCenterFromTop); + } + + /** + * Returns this instance <i>in tangent</i> values. + * <p> + * If this instance is {@link #inTangents} already, method returns this instance, + * otherwise a newly created instance w/ converted values to tangent. + * </p> + */ + public final FovHVHalves toTangents() { + if( inTangents ) { + return this; + } else { + return new FovHVHalves(FloatUtil.tan(left), FloatUtil.tan(right), FloatUtil.tan(top), FloatUtil.tan(bottom), true); + } + } + + /** Returns the full horizontal FOV, i.e. {@link #left} + {@link #right}. */ + public final float horzFov() { return left+right; } + + /** Returns the full vertical FOV, i.e. {@link #top} + {@link #bottom}. */ + public final float vertFov() { return top+bottom; } + + public final String toString() { + return "FovHVH["+(inTangents?"tangents":"radians")+": "+left+" l, "+right+" r, "+top+" t, "+bottom+" b]"; + } + public final String toStringInDegrees() { + final float f = 180.0f / FloatUtil.PI; + final String storedAs = inTangents?"tangents":"radians"; + if( inTangents ) { + final float aleft = FloatUtil.atan(left); + final float aright = FloatUtil.atan(right); + final float atop = FloatUtil.atan(top); + final float abottom = FloatUtil.atan(bottom); + return "FovHVH[degrees: "+aleft*f+" l, "+aright*f+" r, "+atop*f+" t, "+abottom*f+" b, stored-as: "+storedAs+"]"; + } else { + return "FovHVH[degrees: "+left*f+" l, "+right*f+" r, "+top*f+" t, "+bottom*f+" b, stored-as: "+storedAs+"]"; + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java b/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java new file mode 100644 index 000000000..830f1a882 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java @@ -0,0 +1,152 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.opengl.util.PMVMatrix; + +/** + * Simple float array-backed float 4x4 matrix + * exposing {@link FloatUtil} matrix functionality in an object oriented manner. + * <p> + * Unlike {@link PMVMatrix}, this class only represents one single matrix + * without a complete {@link GLMatrixFunc} implementation, + * allowing this class to be more lightweight. + * </p> + * <p> + * Implementation is not mature - WIP and subject to change. + * </p> + */ +public class Matrix4 { + + public Matrix4() { + matrix = new float[16]; + matrixTxSx = new float[16]; + mat4Tmp1 = new float[16]; + vec4Tmp1 = new float[4]; + FloatUtil.makeIdentity(matrixTxSx); + loadIdentity(); + } + + public final float[] getMatrix() { + return matrix; + } + + public final void loadIdentity() { + FloatUtil.makeIdentity(matrix); + } + + /** + * Multiply matrix: [this] = [this] x [m] + * @param m 4x4 matrix in column-major order + */ + public final void multMatrix(final float[] m, final int m_offset) { + FloatUtil.multMatrix(matrix, 0, m, m_offset); + } + + /** + * Multiply matrix: [this] = [this] x [m] + * @param m 4x4 matrix in column-major order + */ + public final void multMatrix(final float[] m) { + FloatUtil.multMatrix(matrix, m); + } + + /** + * Multiply matrix: [this] = [this] x [m] + * @param m 4x4 matrix in column-major order + */ + public final void multMatrix(final Matrix4 m) { + FloatUtil.multMatrix(matrix, m.getMatrix()); + } + + /** + * @param v_in 4-component column-vector + * @param v_out this * v_in + */ + public final void multVec(final float[] v_in, final float[] v_out) { + FloatUtil.multMatrixVec(matrix, v_in, v_out); + } + + /** + * @param v_in 4-component column-vector + * @param v_out this * v_in + */ + public final void multVec(final float[] v_in, final int v_in_offset, final float[] v_out, final int v_out_offset) { + FloatUtil.multMatrixVec(matrix, 0, v_in, v_in_offset, v_out, v_out_offset); + } + + public final void translate(final float x, final float y, final float z) { + multMatrix(FloatUtil.makeTranslation(matrixTxSx, false, x, y, z)); + } + + public final void scale(final float x, final float y, final float z) { + multMatrix(FloatUtil.makeScale(matrixTxSx, false, x, y, z)); + } + + public final void rotate(final float angrad, final float x, final float y, final float z) { + multMatrix(FloatUtil.makeRotationAxis(mat4Tmp1, 0, angrad, x, y, z, vec4Tmp1)); + } + + /** + * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. + */ + public final void rotate(final Quaternion quat) { + multMatrix(quat.toMatrix(mat4Tmp1, 0)); + } + + public final void transpose() { + System.arraycopy(matrix, 0, mat4Tmp1, 0, 16); + FloatUtil.transposeMatrix(mat4Tmp1, matrix); + } + + public final float determinant() { + return FloatUtil.matrixDeterminant(matrix); + } + + public final boolean invert() { + return null != FloatUtil.invertMatrix(matrix, matrix); + } + + public final void makeOrtho(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { + multMatrix( FloatUtil.makeOrtho(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar) ); + } + + public final void makeFrustum(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { + multMatrix( FloatUtil.makeFrustum(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar) ); + } + + public final void makePerspective(final float fovy_rad, final float aspect, final float zNear, final float zFar) { + multMatrix( FloatUtil.makePerspective(mat4Tmp1, 0, true, fovy_rad, aspect, zNear, zFar) ); + } + + private final float[] matrix, matrixTxSx; + private final float[] mat4Tmp1, vec4Tmp1; +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java b/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java new file mode 100644 index 000000000..430450767 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java @@ -0,0 +1,1209 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +/** + * Quaternion implementation supporting + * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a> free rotations. + * <p> + * All matrix operation provided are in column-major order, + * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile. + * See {@link FloatUtil}. + * </p> + * <p> + * See <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix-FAQ</a> + * </p> + * <p> + * See <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/index.htm">euclideanspace.com-Quaternion</a> + * </p> + */ +public class Quaternion { + private float x, y, z, w; + + /** + * Quaternion Epsilon, used with equals method to determine if two Quaternions are close enough to be considered equal. + * <p> + * Using {@value}, which is ~10 times {@link FloatUtil#EPSILON}. + * </p> + */ + public static final float ALLOWED_DEVIANCE = 1.0E-6f; // FloatUtil.EPSILON == 1.1920929E-7f; double ALLOWED_DEVIANCE: 1.0E-8f + + public Quaternion() { + x = y = z = 0; w = 1; + } + + public Quaternion(final Quaternion q) { + set(q); + } + + public Quaternion(final float x, final float y, final float z, final float w) { + set(x, y, z, w); + } + + /** + * See {@link #magnitude()} for special handling of {@link FloatUtil#EPSILON epsilon}, + * which is not applied here. + * @return the squared magnitude of this quaternion. + */ + public final float magnitudeSquared() { + return w*w + x*x + y*y + z*z; + } + + /** + * Return the magnitude of this quaternion, i.e. sqrt({@link #magnitude()}) + * <p> + * A magnitude of zero shall equal {@link #isIdentity() identity}, + * as performed by {@link #normalize()}. + * </p> + * <p> + * Implementation Details: + * <ul> + * <li> returns 0f if {@link #magnitudeSquared()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * <li> returns 1f if {@link #magnitudeSquared()} is {@link FloatUtil#isEqual(float, float, float) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + */ + public final float magnitude() { + final float magnitudeSQ = magnitudeSquared(); + if ( FloatUtil.isZero(magnitudeSQ, FloatUtil.EPSILON) ) { + return 0f; + } + if ( FloatUtil.isEqual(1f, magnitudeSQ, FloatUtil.EPSILON) ) { + return 1f; + } + return FloatUtil.sqrt(magnitudeSQ); + } + + public final float getW() { + return w; + } + + public final void setW(final float w) { + this.w = w; + } + + public final float getX() { + return x; + } + + public final void setX(final float x) { + this.x = x; + } + + public final float getY() { + return y; + } + + public final void setY(final float y) { + this.y = y; + } + + public final float getZ() { + return z; + } + + public final void setZ(final float z) { + this.z = z; + } + + /** + * Returns the dot product of this quaternion with the given x,y,z and w components. + */ + public final float dot(final float x, final float y, final float z, final float w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + /** + * Returns the dot product of this quaternion with the given quaternion + */ + public final float dot(final Quaternion quat) { + return dot(quat.getX(), quat.getY(), quat.getZ(), quat.getW()); + } + + /** + * Returns <code>true</code> if this quaternion has identity. + * <p> + * Implementation uses {@link FloatUtil#EPSILON epsilon} to compare + * {@link #getW() W} {@link FloatUtil#isEqual(float, float, float) against 1f} and + * {@link #getX() X}, {@link #getY() Y} and {@link #getZ() Z} + * {@link FloatUtil#isZero(float, float) against zero}. + * </p> + */ + public final boolean isIdentity() { + return FloatUtil.isEqual(1f, w, FloatUtil.EPSILON) && VectorUtil.isZero(x, y, z, FloatUtil.EPSILON); + // return w == 1f && x == 0f && y == 0f && z == 0f; + } + + /*** + * Set this quaternion to identity (x=0,y=0,z=0,w=1) + * @return this quaternion for chaining. + */ + public final Quaternion setIdentity() { + x = y = z = 0f; w = 1f; + return this; + } + + /** + * Normalize a quaternion required if to be used as a rotational quaternion. + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if {@link #magnitude()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @return this quaternion for chaining. + */ + public final Quaternion normalize() { + final float norm = magnitude(); + if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { + setIdentity(); + } else { + final float invNorm = 1f/norm; + w *= invNorm; + x *= invNorm; + y *= invNorm; + z *= invNorm; + } + return this; + } + + /** + * Conjugates this quaternion <code>[-x, -y, -z, w]</code>. + * @return this quaternion for chaining. + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q49">Matrix-FAQ Q49</a> + */ + public Quaternion conjugate() { + x = -x; + y = -y; + z = -z; + return this; + } + + /** + * Invert the quaternion If rotational, will produce a the inverse rotation + * <p> + * Implementation Details: + * <ul> + * <li> {@link #conjugate() conjugates} if {@link #magnitudeSquared()} is is {@link FloatUtil#isEqual(float, float, float) equals 1f} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @return this quaternion for chaining. + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q50">Matrix-FAQ Q50</a> + */ + public final Quaternion invert() { + final float magnitudeSQ = magnitudeSquared(); + if ( FloatUtil.isEqual(1.0f, magnitudeSQ, FloatUtil.EPSILON) ) { + conjugate(); + } else { + final float invmsq = 1f/magnitudeSQ; + w *= invmsq; + x = -x * invmsq; + y = -y * invmsq; + z = -z * invmsq; + } + return this; + } + + /** + * Set all values of this quaternion using the given src. + * @return this quaternion for chaining. + */ + public final Quaternion set(final Quaternion src) { + this.x = src.x; + this.y = src.y; + this.z = src.z; + this.w = src.w; + return this; + } + + /** + * Set all values of this quaternion using the given components. + * @return this quaternion for chaining. + */ + public final Quaternion set(final float x, final float y, final float z, final float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** + * Add a quaternion + * + * @param q quaternion + * @return this quaternion for chaining. + * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#add">euclideanspace.com-QuaternionAdd</a> + */ + public final Quaternion add(final Quaternion q) { + x += q.x; + y += q.y; + z += q.z; + w += q.w; + return this; + } + + /** + * Subtract a quaternion + * + * @param q quaternion + * @return this quaternion for chaining. + * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#add">euclideanspace.com-QuaternionAdd</a> + */ + public final Quaternion subtract(final Quaternion q) { + x -= q.x; + y -= q.y; + z -= q.z; + w -= q.w; + return this; + } + + /** + * Multiply this quaternion by the param quaternion + * + * @param q a quaternion to multiply with + * @return this quaternion for chaining. + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53">Matrix-FAQ Q53</a> + * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#mul">euclideanspace.com-QuaternionMul</a> + */ + public final Quaternion mult(final Quaternion q) { + return set( w * q.x + x * q.w + y * q.z - z * q.y, + w * q.y - x * q.z + y * q.w + z * q.x, + w * q.z + x * q.y - y * q.x + z * q.w, + w * q.w - x * q.x - y * q.y - z * q.z ); + } + + /** + * Scale this quaternion by a constant + * + * @param n a float constant + * @return this quaternion for chaining. + * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm#scale">euclideanspace.com-QuaternionScale</a> + */ + public final Quaternion scale(final float n) { + x *= n; + y *= n; + z *= n; + w *= n; + return this; + } + + /** + * Rotate this quaternion by the given angle and axis. + * <p> + * The axis must be a normalized vector. + * </p> + * <p> + * A rotational quaternion is made from the given angle and axis. + * </p> + * + * @param angle in radians + * @param axisX x-coord of rotation axis + * @param axisY y-coord of rotation axis + * @param axisZ z-coord of rotation axis + * @return this quaternion for chaining. + */ + public Quaternion rotateByAngleNormalAxis(final float angle, final float axisX, final float axisY, final float axisZ) { + if( VectorUtil.isZero(axisX, axisY, axisZ, FloatUtil.EPSILON) ) { + // no change + return this; + } + final float halfAngle = 0.5f * angle; + final float sin = FloatUtil.sin(halfAngle); + final float qw = FloatUtil.cos(halfAngle); + final float qx = sin * axisX; + final float qy = sin * axisY; + final float qz = sin * axisZ; + return set( x * qw + y * qz - z * qy + w * qx, + -x * qz + y * qw + z * qx + w * qy, + x * qy - y * qx + z * qw + w * qz, + -x * qx - y * qy - z * qz + w * qw); + } + + /** + * Rotate this quaternion around X axis with the given angle in radians + * + * @param angle in radians + * @return this quaternion for chaining. + */ + public Quaternion rotateByAngleX(final float angle) { + final float halfAngle = 0.5f * angle; + final float sin = FloatUtil.sin(halfAngle); + final float cos = FloatUtil.cos(halfAngle); + return set( x * cos + w * sin, + y * cos + z * sin, + -y * sin + z * cos, + -x * sin + w * cos); + } + + /** + * Rotate this quaternion around Y axis with the given angle in radians + * + * @param angle in radians + * @return this quaternion for chaining. + */ + public Quaternion rotateByAngleY(final float angle) { + final float halfAngle = 0.5f * angle; + final float sin = FloatUtil.sin(halfAngle); + final float cos = FloatUtil.cos(halfAngle); + return set( x * cos - z * sin, + y * cos + w * sin, + x * sin + z * cos, + -y * sin + w * cos); + } + + /** + * Rotate this quaternion around Z axis with the given angle in radians + * + * @param angle in radians + * @return this quaternion for chaining. + */ + public Quaternion rotateByAngleZ(final float angle) { + final float halfAngle = 0.5f * angle; + final float sin = FloatUtil.sin(halfAngle); + final float cos = FloatUtil.cos(halfAngle); + return set( x * cos + y * sin, + -x * sin + y * cos, + z * cos + w * sin, + -z * sin + w * cos); + } + + /** + * Rotates this quaternion from the given Euler rotation array <code>angradXYZ</code> in radians. + * <p> + * The <code>angradXYZ</code> array is laid out in natural order: + * <ul> + * <li>x - bank</li> + * <li>y - heading</li> + * <li>z - attitude</li> + * </ul> + * </p> + * For details see {@link #rotateByEuler(float, float, float)}. + * @param angradXYZ euler angel array in radians + * @return this quaternion for chaining. + * @see #rotateByEuler(float, float, float) + */ + public final Quaternion rotateByEuler(final float[] angradXYZ) { + return rotateByEuler(angradXYZ[0], angradXYZ[1], angradXYZ[2]); + } + + /** + * Rotates this quaternion from the given Euler rotation angles in radians. + * <p> + * The rotations are applied in the given order and using chained rotation per axis: + * <ul> + * <li>y - heading - {@link #rotateByAngleY(float)}</li> + * <li>z - attitude - {@link #rotateByAngleZ(float)}</li> + * <li>x - bank - {@link #rotateByAngleX(float)}</li> + * </ul> + * </p> + * <p> + * Implementation Details: + * <ul> + * <li> NOP if all angles are {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * <li> result is {@link #normalize()}ed</li> + * </ul> + * </p> + * @param bankX the Euler pitch angle in radians. (rotation about the X axis) + * @param headingY the Euler yaw angle in radians. (rotation about the Y axis) + * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis) + * @return this quaternion for chaining. + * @see #rotateByAngleY(float) + * @see #rotateByAngleZ(float) + * @see #rotateByAngleX(float) + * @see #setFromEuler(float, float, float) + */ + public final Quaternion rotateByEuler(final float bankX, final float headingY, final float attitudeZ) { + if ( VectorUtil.isZero(bankX, headingY, attitudeZ, FloatUtil.EPSILON) ) { + return this; + } else { + // setFromEuler muls: ( 8 + 4 ) , + quat muls 24 = 36 + // this: 8 + 8 + 8 + 4 = 28 muls + return rotateByAngleY(headingY).rotateByAngleZ(attitudeZ).rotateByAngleX(bankX).normalize(); + } + } + + /*** + * Rotate the given vector by this quaternion + * + * @param vecOut result float[3] storage for rotated vector, maybe equal to vecIn for in-place rotation + * @param vecOutOffset offset in result storage + * @param vecIn float[3] vector to be rotated + * @param vecInOffset offset in vecIn + * @return the given vecOut store for chaining + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q63">Matrix-FAQ Q63</a> + */ + public final float[] rotateVector(final float[] vecOut, final int vecOutOffset, final float[] vecIn, final int vecInOffset) { + if ( VectorUtil.isVec3Zero(vecIn, vecInOffset, FloatUtil.EPSILON) ) { + vecOut[0+vecOutOffset] = 0f; + vecOut[1+vecOutOffset] = 0f; + vecOut[2+vecOutOffset] = 0f; + } else { + final float vecX = vecIn[0+vecInOffset]; + final float vecY = vecIn[1+vecInOffset]; + final float vecZ = vecIn[2+vecInOffset]; + final float x_x = x*x; + final float y_y = y*y; + final float z_z = z*z; + final float w_w = w*w; + + vecOut[0+vecOutOffset] = w_w * vecX + + x_x * vecX + - z_z * vecX + - y_y * vecX + + 2f * ( y*w*vecZ - z*w*vecY + y*x*vecY + z*x*vecZ ); + ; + + vecOut[1+vecOutOffset] = y_y * vecY + - z_z * vecY + + w_w * vecY + - x_x * vecY + + 2f * ( x*y*vecX + z*y*vecZ + w*z*vecX - x*w*vecZ ); + ; + + vecOut[2+vecOutOffset] = z_z * vecZ + - y_y * vecZ + - x_x * vecZ + + w_w * vecZ + + 2f * ( x*z*vecX + y*z*vecY - w*y*vecX + w*x*vecY ); + ; + } + return vecOut; + } + + /** + * Set this quaternion to a spherical linear interpolation + * between the given start and end quaternions by the given change amount. + * <p> + * Note: Method <i>does not</i> normalize this quaternion! + * </p> + * + * @param a start quaternion + * @param b end quaternion + * @param changeAmnt float between 0 and 1 representing interpolation. + * @return this quaternion for chaining. + * @see <a href="http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/">euclideanspace.com-QuaternionSlerp</a> + */ + public final Quaternion setSlerp(final Quaternion a, final Quaternion b, final float changeAmnt) { + // System.err.println("Slerp.0: A "+a+", B "+b+", t "+changeAmnt); + if (changeAmnt == 0.0f) { + set(a); + } else if (changeAmnt == 1.0f) { + set(b); + } else { + float bx = b.x; + float by = b.y; + float bz = b.z; + float bw = b.w; + + // Calculate angle between them (quat dot product) + float cosHalfTheta = a.x * bx + a.y * by + a.z * bz + a.w * bw; + + final float scale0, scale1; + + if( cosHalfTheta >= 0.95f ) { + // quaternions are close, just use linear interpolation + scale0 = 1.0f - changeAmnt; + scale1 = changeAmnt; + // System.err.println("Slerp.1: Linear Interpol; cosHalfTheta "+cosHalfTheta); + } else if ( cosHalfTheta <= -0.99f ) { + // the quaternions are nearly opposite, + // we can pick any axis normal to a,b to do the rotation + scale0 = 0.5f; + scale1 = 0.5f; + // System.err.println("Slerp.2: Any; cosHalfTheta "+cosHalfTheta); + } else { + // System.err.println("Slerp.3: cosHalfTheta "+cosHalfTheta); + if( cosHalfTheta <= -FloatUtil.EPSILON ) { // FIXME: .. or shall we use the upper bound 'cosHalfTheta < FloatUtil.EPSILON' ? + // Negate the second quaternion and the result of the dot product (Inversion) + bx *= -1f; + by *= -1f; + bz *= -1f; + bw *= -1f; + cosHalfTheta *= -1f; + // System.err.println("Slerp.4: Inverted cosHalfTheta "+cosHalfTheta); + } + final float halfTheta = FloatUtil.acos(cosHalfTheta); + final float sinHalfTheta = FloatUtil.sqrt(1.0f - cosHalfTheta*cosHalfTheta); + // if theta = 180 degrees then result is not fully defined + // we could rotate around any axis normal to qa or qb + if ( Math.abs(sinHalfTheta) < 0.001f ){ // fabs is floating point absolute + scale0 = 0.5f; + scale1 = 0.5f; + // throw new InternalError("XXX"); // FIXME should not be reached due to above inversion ? + } else { + // Calculate the scale for q1 and q2, according to the angle and + // it's sine value + scale0 = FloatUtil.sin((1f - changeAmnt) * halfTheta) / sinHalfTheta; + scale1 = FloatUtil.sin(changeAmnt * halfTheta) / sinHalfTheta; + } + } + + x = a.x * scale0 + bx * scale1; + y = a.y * scale0 + by * scale1; + z = a.z * scale0 + bz * scale1; + w = a.w * scale0 + bw * scale1; + } + // System.err.println("Slerp.X: Result "+this); + return this; + } + + /** + * Set this quaternion to equal the rotation required + * to point the z-axis at <i>direction</i> and the y-axis to <i>up</i>. + * <p> + * Implementation generates a 3x3 matrix + * and is equal with ProjectFloat's lookAt(..).<br/> + * </p> + * Implementation Details: + * <ul> + * <li> result is {@link #normalize()}ed</li> + * </ul> + * </p> + * @param directionIn where to <i>look</i> at + * @param upIn a vector indicating the local <i>up</i> direction. + * @param xAxisOut vector storing the <i>orthogonal</i> x-axis of the coordinate system. + * @param yAxisOut vector storing the <i>orthogonal</i> y-axis of the coordinate system. + * @param zAxisOut vector storing the <i>orthogonal</i> z-axis of the coordinate system. + * @return this quaternion for chaining. + * @see <a href="http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm">euclideanspace.com-LookUp</a> + */ + public Quaternion setLookAt(final float[] directionIn, final float[] upIn, + final float[] xAxisOut, final float[] yAxisOut, final float[] zAxisOut) { + // Z = norm(dir) + VectorUtil.normalizeVec3(zAxisOut, directionIn); + + // X = upIn x Z + // (borrow yAxisOut for upNorm) + VectorUtil.normalizeVec3(yAxisOut, upIn); + VectorUtil.crossVec3(xAxisOut, yAxisOut, zAxisOut); + VectorUtil.normalizeVec3(xAxisOut); + + // Y = Z x X + // + VectorUtil.crossVec3(yAxisOut, zAxisOut, xAxisOut); + VectorUtil.normalizeVec3(yAxisOut); + + /** + final float m00 = xAxisOut[0]; + final float m01 = yAxisOut[0]; + final float m02 = zAxisOut[0]; + final float m10 = xAxisOut[1]; + final float m11 = yAxisOut[1]; + final float m12 = zAxisOut[1]; + final float m20 = xAxisOut[2]; + final float m21 = yAxisOut[2]; + final float m22 = zAxisOut[2]; + */ + return setFromAxes(xAxisOut, yAxisOut, zAxisOut).normalize(); + } + + // + // Conversions + // + + /** + * Initialize this quaternion from two vectors + * <pre> + * q = (s,v) = (v1•v2 , v1 × v2), + * angle = angle(v1, v2) = v1•v2 + * axis = normal(v1 x v2) + * </pre> + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if square vector-length is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @param v1 not normalized + * @param v2 not normalized + * @param tmpPivotVec float[3] temp storage for cross product + * @param tmpNormalVec float[3] temp storage to normalize vector + * @return this quaternion for chaining. + */ + public final Quaternion setFromVectors(final float[] v1, final float[] v2, final float[] tmpPivotVec, final float[] tmpNormalVec) { + final float factor = VectorUtil.normVec3(v1) * VectorUtil.normVec3(v2); + if ( FloatUtil.isZero(factor, FloatUtil.EPSILON ) ) { + return setIdentity(); + } else { + final float dot = VectorUtil.dotVec3(v1, v2) / factor; // normalize + final float theta = FloatUtil.acos(Math.max(-1.0f, Math.min(dot, 1.0f))); // clipping [-1..1] + + VectorUtil.crossVec3(tmpPivotVec, v1, v2); + + if ( dot < 0.0f && FloatUtil.isZero( VectorUtil.normVec3(tmpPivotVec), FloatUtil.EPSILON ) ) { + // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector + // perpendicular to this vector will rotate vector a onto vector b. + // + // The following guarantees the dot-product will be 0.0. + int dominantIndex; + if (Math.abs(v1[0]) > Math.abs(v1[1])) { + if (Math.abs(v1[0]) > Math.abs(v1[2])) { + dominantIndex = 0; + } else { + dominantIndex = 2; + } + } else { + if (Math.abs(v1[1]) > Math.abs(v1[2])) { + dominantIndex = 1; + } else { + dominantIndex = 2; + } + } + tmpPivotVec[dominantIndex] = -v1[(dominantIndex + 1) % 3]; + tmpPivotVec[(dominantIndex + 1) % 3] = v1[dominantIndex]; + tmpPivotVec[(dominantIndex + 2) % 3] = 0f; + } + return setFromAngleAxis(theta, tmpPivotVec, tmpNormalVec); + } + } + + /** + * Initialize this quaternion from two normalized vectors + * <pre> + * q = (s,v) = (v1•v2 , v1 × v2), + * angle = angle(v1, v2) = v1•v2 + * axis = v1 x v2 + * </pre> + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if square vector-length is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @param v1 normalized + * @param v2 normalized + * @param tmpPivotVec float[3] temp storage for cross product + * @return this quaternion for chaining. + */ + public final Quaternion setFromNormalVectors(final float[] v1, final float[] v2, final float[] tmpPivotVec) { + final float factor = VectorUtil.normVec3(v1) * VectorUtil.normVec3(v2); + if ( FloatUtil.isZero(factor, FloatUtil.EPSILON ) ) { + return setIdentity(); + } else { + final float dot = VectorUtil.dotVec3(v1, v2) / factor; // normalize + final float theta = FloatUtil.acos(Math.max(-1.0f, Math.min(dot, 1.0f))); // clipping [-1..1] + + VectorUtil.crossVec3(tmpPivotVec, v1, v2); + + if ( dot < 0.0f && FloatUtil.isZero( VectorUtil.normVec3(tmpPivotVec), FloatUtil.EPSILON ) ) { + // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector + // perpendicular to this vector will rotate vector a onto vector b. + // + // The following guarantees the dot-product will be 0.0. + int dominantIndex; + if (Math.abs(v1[0]) > Math.abs(v1[1])) { + if (Math.abs(v1[0]) > Math.abs(v1[2])) { + dominantIndex = 0; + } else { + dominantIndex = 2; + } + } else { + if (Math.abs(v1[1]) > Math.abs(v1[2])) { + dominantIndex = 1; + } else { + dominantIndex = 2; + } + } + tmpPivotVec[dominantIndex] = -v1[(dominantIndex + 1) % 3]; + tmpPivotVec[(dominantIndex + 1) % 3] = v1[dominantIndex]; + tmpPivotVec[(dominantIndex + 2) % 3] = 0f; + } + return setFromAngleNormalAxis(theta, tmpPivotVec); + } + } + + /*** + * Initialize this quaternion with given non-normalized axis vector and rotation angle + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if axis is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @param angle rotation angle (rads) + * @param vector axis vector not normalized + * @param tmpV3f float[3] temp storage to normalize vector + * @return this quaternion for chaining. + * + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a> + * @see #toAngleAxis(float[]) + */ + public final Quaternion setFromAngleAxis(final float angle, final float[] vector, final float[] tmpV3f) { + VectorUtil.normalizeVec3(tmpV3f, vector); + return setFromAngleNormalAxis(angle, tmpV3f); + } + + /*** + * Initialize this quaternion with given normalized axis vector and rotation angle + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if axis is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * @param angle rotation angle (rads) + * @param vector axis vector normalized + * @return this quaternion for chaining. + * + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a> + * @see #toAngleAxis(float[]) + */ + public final Quaternion setFromAngleNormalAxis(final float angle, final float[] vector) { + if ( VectorUtil.isVec3Zero(vector, 0, FloatUtil.EPSILON) ) { + setIdentity(); + } else { + final float halfangle = angle * 0.5f; + final float sin = FloatUtil.sin(halfangle); + x = vector[0] * sin; + y = vector[1] * sin; + z = vector[2] * sin; + w = FloatUtil.cos(halfangle); + } + return this; + } + + /** + * Transform the rotational quaternion to axis based rotation angles + * + * @param axis float[3] storage for computed axis + * @return the rotation angle in radians + * @see #setFromAngleAxis(float, float[], float[]) + */ + public final float toAngleAxis(final float[] axis) { + final float sqrLength = x*x + y*y + z*z; + float angle; + if ( FloatUtil.isZero(sqrLength, FloatUtil.EPSILON) ) { // length is ~0 + angle = 0.0f; + axis[0] = 1.0f; + axis[1] = 0.0f; + axis[2] = 0.0f; + } else { + angle = FloatUtil.acos(w) * 2.0f; + final float invLength = 1.0f / FloatUtil.sqrt(sqrLength); + axis[0] = x * invLength; + axis[1] = y * invLength; + axis[2] = z * invLength; + } + return angle; + } + + /** + * Initializes this quaternion from the given Euler rotation array <code>angradXYZ</code> in radians. + * <p> + * The <code>angradXYZ</code> array is laid out in natural order: + * <ul> + * <li>x - bank</li> + * <li>y - heading</li> + * <li>z - attitude</li> + * </ul> + * </p> + * For details see {@link #setFromEuler(float, float, float)}. + * @param angradXYZ euler angel array in radians + * @return this quaternion for chaining. + * @see #setFromEuler(float, float, float) + */ + public final Quaternion setFromEuler(final float[] angradXYZ) { + return setFromEuler(angradXYZ[0], angradXYZ[1], angradXYZ[2]); + } + + /** + * Initializes this quaternion from the given Euler rotation angles in radians. + * <p> + * The rotations are applied in the given order: + * <ul> + * <li>y - heading</li> + * <li>z - attitude</li> + * <li>x - bank</li> + * </ul> + * </p> + * <p> + * Implementation Details: + * <ul> + * <li> {@link #setIdentity()} if all angles are {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * <li> result is {@link #normalize()}ed</li> + * </ul> + * </p> + * @param bankX the Euler pitch angle in radians. (rotation about the X axis) + * @param headingY the Euler yaw angle in radians. (rotation about the Y axis) + * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis) + * @return this quaternion for chaining. + * + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60">Matrix-FAQ Q60</a> + * @see <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Gems</a> + * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">euclideanspace.com-eulerToQuaternion</a> + * @see #toEuler(float[]) + */ + public final Quaternion setFromEuler(final float bankX, final float headingY, final float attitudeZ) { + if ( VectorUtil.isZero(bankX, headingY, attitudeZ, FloatUtil.EPSILON) ) { + return setIdentity(); + } else { + float angle = headingY * 0.5f; + final float sinHeadingY = FloatUtil.sin(angle); + final float cosHeadingY = FloatUtil.cos(angle); + angle = attitudeZ * 0.5f; + final float sinAttitudeZ = FloatUtil.sin(angle); + final float cosAttitudeZ = FloatUtil.cos(angle); + angle = bankX * 0.5f; + final float sinBankX = FloatUtil.sin(angle); + final float cosBankX = FloatUtil.cos(angle); + + // variables used to reduce multiplication calls. + final float cosHeadingXcosAttitude = cosHeadingY * cosAttitudeZ; + final float sinHeadingXsinAttitude = sinHeadingY * sinAttitudeZ; + final float cosHeadingXsinAttitude = cosHeadingY * sinAttitudeZ; + final float sinHeadingXcosAttitude = sinHeadingY * cosAttitudeZ; + + w = cosHeadingXcosAttitude * cosBankX - sinHeadingXsinAttitude * sinBankX; + x = cosHeadingXcosAttitude * sinBankX + sinHeadingXsinAttitude * cosBankX; + y = sinHeadingXcosAttitude * cosBankX + cosHeadingXsinAttitude * sinBankX; + z = cosHeadingXsinAttitude * cosBankX - sinHeadingXcosAttitude * sinBankX; + return normalize(); + } + } + + /** + * Transform this quaternion to Euler rotation angles in radians (pitchX, yawY and rollZ). + * + * @param result the float[] array storing the computed angles. + * @return the double[] array, filled with heading, attitude and bank in that order.. + * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">euclideanspace.com-quaternionToEuler</a> + * @see #setFromEuler(float, float, float) + */ + public float[] toEuler(final float[] result) { + final float sqw = w*w; + final float sqx = x*x; + final float sqy = y*y; + final float sqz = z*z; + final float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise + // is correction factor + final float test = x*y + z*w; + + if (test > 0.499f * unit) { // singularity at north pole + result[0] = 0f; + result[1] = 2f * FloatUtil.atan2(x, w); + result[2] = FloatUtil.HALF_PI; + } else if (test < -0.499f * unit) { // singularity at south pole + result[0] = 0f; + result[1] = -2 * FloatUtil.atan2(x, w); + result[2] = -FloatUtil.HALF_PI; + } else { + result[0] = FloatUtil.atan2(2f * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); + result[1] = FloatUtil.atan2(2f * y * w - 2 * x * z, sqx - sqy - sqz + sqw); + result[2] = FloatUtil.asin( 2f * test / unit); + } + return result; + } + + /** + * Initializes this quaternion from a 4x4 column rotation matrix + * <p> + * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/> + * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>. + * </p> + * <p> + * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a> + * </p> + * + * @param m 4x4 column matrix + * @return this quaternion for chaining. + * @see #toMatrix(float[], int) + */ + public final Quaternion setFromMatrix(final float[] m, final int m_off) { + return setFromMatrix(m[0+0*4+m_off], m[0+1*4+m_off], m[0+2*4+m_off], + m[1+0*4+m_off], m[1+1*4+m_off], m[1+2*4+m_off], + m[2+0*4+m_off], m[2+1*4+m_off], m[2+2*4+m_off]); + } + + /** + * Compute the quaternion from a 3x3 column rotation matrix + * <p> + * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/> + * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>. + * </p> + * <p> + * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a> + * </p> + * + * @return this quaternion for chaining. + * @see #toMatrix(float[], int) + */ + public Quaternion setFromMatrix(final float m00, final float m01, final float m02, + final float m10, final float m11, final float m12, + final float m20, final float m21, final float m22) { + // Note: Other implementations uses 'T' w/o '+1f' and compares 'T >= 0' while adding missing 1f in sqrt expr. + // However .. this causes setLookAt(..) to fail and actually violates the 'trace definition'. + + // The trace T is the sum of the diagonal elements; see + // http://mathworld.wolfram.com/MatrixTrace.html + final float T = m00 + m11 + m22 + 1f; + // System.err.println("setFromMatrix.0 T "+T+", m00 "+m00+", m11 "+m11+", m22 "+m22); + if ( T > 0f ) { + // System.err.println("setFromMatrix.1"); + final float S = 0.5f / FloatUtil.sqrt(T); // S = 1 / ( 2 t ) + w = 0.25f / S; // w = 1 / ( 4 S ) = t / 2 + x = ( m21 - m12 ) * S; + y = ( m02 - m20 ) * S; + z = ( m10 - m01 ) * S; + } else if ( m00 > m11 && m00 > m22) { + // System.err.println("setFromMatrix.2"); + final float S = 0.5f / FloatUtil.sqrt(1.0f + m00 - m11 - m22); // S=4*qx + w = ( m21 - m12 ) * S; + x = 0.25f / S; + y = ( m10 + m01 ) * S; + z = ( m02 + m20 ) * S; + } else if ( m11 > m22 ) { + // System.err.println("setFromMatrix.3"); + final float S = 0.5f / FloatUtil.sqrt(1.0f + m11 - m00 - m22); // S=4*qy + w = ( m02 - m20 ) * S; + x = ( m20 + m01 ) * S; + y = 0.25f / S; + z = ( m21 + m12 ) * S; + } else { + // System.err.println("setFromMatrix.3"); + final float S = 0.5f / FloatUtil.sqrt(1.0f + m22 - m00 - m11); // S=4*qz + w = ( m10 - m01 ) * S; + x = ( m02 + m20 ) * S; + y = ( m21 + m12 ) * S; + z = 0.25f / S; + } + return this; + } + + /** + * Transform this quaternion to a normalized 4x4 column matrix representing the rotation. + * <p> + * Implementation Details: + * <ul> + * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * + * @param matrix float[16] store for the resulting normalized column matrix 4x4 + * @param mat_offset + * @return the given matrix store + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a> + * @see #setFromMatrix(float[], int) + */ + public final float[] toMatrix(final float[] matrix, final int mat_offset) { + // pre-multiply scaled-reciprocal-magnitude to reduce multiplications + final float norm = magnitudeSquared(); + if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { + // identity matrix -> srecip = 0f + return FloatUtil.makeIdentity(matrix, mat_offset); + } + final float srecip; + if ( FloatUtil.isEqual(1f, norm, FloatUtil.EPSILON) ) { + srecip = 2f; + } else { + srecip = 2.0f / norm; + } + + final float xs = srecip * x; + final float ys = srecip * y; + final float zs = srecip * z; + + final float xx = x * xs; + final float xy = x * ys; + final float xz = x * zs; + final float xw = xs * w; + final float yy = y * ys; + final float yz = y * zs; + final float yw = ys * w; + final float zz = z * zs; + final float zw = zs * w; + + matrix[0+0*4+mat_offset] = 1f - ( yy + zz ); + matrix[0+1*4+mat_offset] = ( xy - zw ); + matrix[0+2*4+mat_offset] = ( xz + yw ); + matrix[0+3*4+mat_offset] = 0f; + + matrix[1+0*4+mat_offset] = ( xy + zw ); + matrix[1+1*4+mat_offset] = 1f - ( xx + zz ); + matrix[1+2*4+mat_offset] = ( yz - xw ); + matrix[1+3*4+mat_offset] = 0f; + + matrix[2+0*4+mat_offset] = ( xz - yw ); + matrix[2+1*4+mat_offset] = ( yz + xw ); + matrix[2+2*4+mat_offset] = 1f - ( xx + yy ); + matrix[2+3*4+mat_offset] = 0f; + + matrix[3+0*4+mat_offset] = 0f; + matrix[3+1*4+mat_offset] = 0f; + matrix[3+2*4+mat_offset] = 0f; + matrix[3+3*4+mat_offset] = 1f; + return matrix; + } + + /** + * @param index the 3x3 rotation matrix column to retrieve from this quaternion (normalized). Must be between 0 and 2. + * @param result the vector object to store the result in. + * @return the result column-vector for chaining. + */ + public float[] copyMatrixColumn(final int index, final float[] result, final int resultOffset) { + // pre-multipliy scaled-reciprocal-magnitude to reduce multiplications + final float norm = magnitudeSquared(); + final float srecip; + if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { + srecip= 0f; + } else if ( FloatUtil.isEqual(1f, norm, FloatUtil.EPSILON) ) { + srecip= 2f; + } else { + srecip= 2.0f / norm; + } + + // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs + // will be used 2-4 times each. + final float xs = x * srecip; + final float ys = y * srecip; + final float zs = z * srecip; + final float xx = x * xs; + final float xy = x * ys; + final float xz = x * zs; + final float xw = w * xs; + final float yy = y * ys; + final float yz = y * zs; + final float yw = w * ys; + final float zz = z * zs; + final float zw = w * zs; + + // using s=2/norm (instead of 1/norm) saves 3 multiplications by 2 here + switch (index) { + case 0: + result[0+resultOffset] = 1.0f - (yy + zz); + result[1+resultOffset] = xy + zw; + result[2+resultOffset] = xz - yw; + break; + case 1: + result[0+resultOffset] = xy - zw; + result[1+resultOffset] = 1.0f - (xx + zz); + result[2+resultOffset] = yz + xw; + break; + case 2: + result[0+resultOffset] = xz + yw; + result[1+resultOffset] = yz - xw; + result[2+resultOffset] = 1.0f - (xx + yy); + break; + default: + throw new IllegalArgumentException("Invalid column index. " + index); + } + return result; + } + + /** + * Initializes this quaternion to represent a rotation formed by the given three <i>orthogonal</i> axes. + * <p> + * No validation whether the axes are <i>orthogonal</i> is performed. + * </p> + * + * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system. + * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system. + * @param zAxis vector representing the <i>orthogonal</i> z-axis of the coordinate system. + * @return this quaternion for chaining. + */ + public final Quaternion setFromAxes(final float[] xAxis, final float[] yAxis, final float[] zAxis) { + return setFromMatrix(xAxis[0], yAxis[0], zAxis[0], + xAxis[1], yAxis[1], zAxis[1], + xAxis[2], yAxis[2], zAxis[2]); + } + + /** + * Extracts this quaternion's <i>orthogonal</i> rotation axes. + * + * @param xAxis vector representing the <i>orthogonal</i> x-axis of the coordinate system. + * @param yAxis vector representing the <i>orthogonal</i> y-axis of the coordinate system. + * @param zAxis vector representing the <i>orthogonal</i> z-axis of the coordinate system. + * @param tmpMat4 temporary float[4] matrix, used to transform this quaternion to a matrix. + */ + public void toAxes(final float[] xAxis, final float[] yAxis, final float[] zAxis, final float[] tmpMat4) { + toMatrix(tmpMat4, 0); + FloatUtil.copyMatrixColumn(tmpMat4, 0, 2, zAxis, 0); + FloatUtil.copyMatrixColumn(tmpMat4, 0, 1, yAxis, 0); + FloatUtil.copyMatrixColumn(tmpMat4, 0, 0, xAxis, 0); + } + + /** + * Check if the the 3x3 matrix (param) is in fact an affine rotational + * matrix + * + * @param m 3x3 column matrix + * @return true if representing a rotational matrix, false otherwise + */ + public final boolean isRotationMatrix3f(final float[] m) { + final float epsilon = 0.01f; // margin to allow for rounding errors + if (FloatUtil.abs(m[0] * m[3] + m[3] * m[4] + m[6] * m[7]) > epsilon) + return false; + if (FloatUtil.abs(m[0] * m[2] + m[3] * m[5] + m[6] * m[8]) > epsilon) + return false; + if (FloatUtil.abs(m[1] * m[2] + m[4] * m[5] + m[7] * m[8]) > epsilon) + return false; + if (FloatUtil.abs(m[0] * m[0] + m[3] * m[3] + m[6] * m[6] - 1) > epsilon) + return false; + if (FloatUtil.abs(m[1] * m[1] + m[4] * m[4] + m[7] * m[7] - 1) > epsilon) + return false; + if (FloatUtil.abs(m[2] * m[2] + m[5] * m[5] + m[8] * m[8] - 1) > epsilon) + return false; + return (FloatUtil.abs(determinant3f(m) - 1) < epsilon); + } + + private final float determinant3f(final float[] m) { + return m[0] * m[4] * m[8] + m[3] * m[7] * m[2] + m[6] * m[1] * m[5] + - m[0] * m[7] * m[5] - m[3] * m[1] * m[8] - m[6] * m[4] * m[2]; + } + + // + // std java overrides + // + + /** + * @param o the object to compare for equality + * @return true if this quaternion and the provided quaternion have roughly the same x, y, z and w values. + */ + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Quaternion)) { + return false; + } + final Quaternion comp = (Quaternion) o; + return Math.abs(x - comp.getX()) <= ALLOWED_DEVIANCE && + Math.abs(y - comp.getY()) <= ALLOWED_DEVIANCE && + Math.abs(z - comp.getZ()) <= ALLOWED_DEVIANCE && + Math.abs(w - comp.getW()) <= ALLOWED_DEVIANCE; + } + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + + public String toString() { + return "Quaternion[x "+x+", y "+y+", z "+z+", w "+w+"]"; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Ray.java b/src/jogl/classes/com/jogamp/opengl/math/Ray.java new file mode 100644 index 000000000..4d651d1c3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Ray.java @@ -0,0 +1,59 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +import com.jogamp.opengl.math.geom.AABBox; + +/** + * Simple compound denoting a ray. + * <p> + * A ray, also known as a half line, consists out of it's <i>origin</i> + * and <i>direction</i>. Hence it is bound to only the <i>origin</i> side, + * where the other end is +infinitive. + * <pre> + * R(t) = R0 + Rd * t with R0 origin, Rd direction and t > 0.0 + * </pre> + * </p> + * <p> + * A {@link Ray} maybe used for <i>picking</i> + * using a {@link AABBox bounding box} via + * {@link AABBox#intersectsRay(Ray) fast probe} or + * {@link AABBox#getRayIntersection(float[], Ray, float, boolean, float[], float[], float[]) returning the intersection}. + * </p> + */ +public class Ray { + /** Origin of Ray, float[3]. */ + public final float[] orig = new float[3]; + + /** Normalized direction vector of ray, float[3]. */ + public final float[] dir = new float[3]; + + public String toString() { + return "Ray[orig["+orig[0]+", "+orig[1]+", "+orig[2]+"], dir["+dir[0]+", "+dir[1]+", "+dir[2]+"]]"; + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java new file mode 100644 index 000000000..36222cf4a --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java @@ -0,0 +1,1192 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +import java.util.ArrayList; + +public final class VectorUtil { + + public static final float[] VEC3_ONE = { 1f, 1f, 1f }; + public static final float[] VEC3_ZERO = { 0f, 0f, 0f }; + public static final float[] VEC3_UNIT_Y = { 0f, 1f, 0f }; + public static final float[] VEC3_UNIT_Y_NEG = { 0f, -1f, 0f }; + public static final float[] VEC3_UNIT_Z = { 0f, 0f, 1f }; + public static final float[] VEC3_UNIT_Z_NEG = { 0f, 0f, -1f }; + + public enum Winding { + CW(-1), CCW(1); + + public final int dir; + + Winding(final int dir) { + this.dir = dir; + } + } + + /** + * Copies a vector of length 2 + * @param dst output vector + * @param dstOffset offset of dst in array + * @param src input vector + * @param srcOffset offset of src in array + * @return copied output vector for chaining + */ + public static float[] copyVec2(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) + { + System.arraycopy(src, srcOffset, dst, dstOffset, 2); + return dst; + } + + /** + * Copies a vector of length 3 + * @param dst output vector + * @param dstOffset offset of dst in array + * @param src input vector + * @param srcOffset offset of src in array + * @return copied output vector for chaining + */ + public static float[] copyVec3(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) + { + System.arraycopy(src, srcOffset, dst, dstOffset, 3); + return dst; + } + + /** + * Copies a vector of length 4 + * @param dst output vector + * @param dstOffset offset of dst in array + * @param src input vector + * @param srcOffset offset of src in array + * @return copied output vector for chaining + */ + public static float[] copyVec4(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) + { + System.arraycopy(src, srcOffset, dst, dstOffset, 4); + return dst; + } + + /** + * Return true if both vectors are equal w/o regarding an epsilon. + * <p> + * Implementation uses {@link FloatUtil#isEqual(float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec2Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset) { + return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset]) && + FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset]) ; + } + + /** + * Return true if both vectors are equal w/o regarding an epsilon. + * <p> + * Implementation uses {@link FloatUtil#isEqual(float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec3Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset) { + return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset]) && + FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset]) && + FloatUtil.isEqual(vec1[2+vec1Offset], vec2[2+vec2Offset]) ; + } + + /** + * Return true if both vectors are equal, i.e. their absolute delta < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isEqual(float, float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec2Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset, final float epsilon) { + return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset], epsilon) && + FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset], epsilon) ; + } + + /** + * Return true if both vectors are equal, i.e. their absolute delta < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isEqual(float, float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec3Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset, final float epsilon) { + return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset], epsilon) && + FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset], epsilon) && + FloatUtil.isEqual(vec1[2+vec1Offset], vec2[2+vec2Offset], epsilon) ; + } + + /** + * Return true if vector is zero, no {@link FloatUtil#EPSILON} is taken into consideration. + */ + public static boolean isVec2Zero(final float[] vec, final int vecOffset) { + return 0f == vec[0+vecOffset] && 0f == vec[1+vecOffset]; + } + + /** + * Return true if vector is zero, no {@link FloatUtil#EPSILON} is taken into consideration. + */ + public static boolean isVec3Zero(final float[] vec, final int vecOffset) { + return 0f == vec[0+vecOffset] && 0f == vec[1+vecOffset] && 0f == vec[2+vecOffset]; + } + + /** + * Return true if vector is zero, i.e. it's absolute components < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec2Zero(final float[] vec, final int vecOffset, final float epsilon) { + return isZero(vec[0+vecOffset], vec[1+vecOffset], epsilon); + } + + /** + * Return true if vector is zero, i.e. it's absolute components < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. + * </p> + */ + public static boolean isVec3Zero(final float[] vec, final int vecOffset, final float epsilon) { + return isZero(vec[0+vecOffset], vec[1+vecOffset], vec[2+vecOffset], epsilon); + } + + /** + * Return true if all two vector components are zero, i.e. it's their absolute value < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. + * </p> + */ + public static boolean isZero(final float x, final float y, final float epsilon) { + return FloatUtil.isZero(x, epsilon) && + FloatUtil.isZero(y, epsilon) ; + } + + /** + * Return true if all three vector components are zero, i.e. it's their absolute value < <code>epsilon</code>. + * <p> + * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. + * </p> + */ + public static boolean isZero(final float x, final float y, final float z, final float epsilon) { + return FloatUtil.isZero(x, epsilon) && + FloatUtil.isZero(y, epsilon) && + FloatUtil.isZero(z, epsilon) ; + } + + /** + * Return the squared distance between the given two points described vector v1 and v2. + * <p> + * When comparing the relative distance between two points it is usually sufficient to compare the squared + * distances, thus avoiding an expensive square root operation. + * </p> + */ + public static float distSquareVec3(final float[] v1, final float[] v2) { + final float dx = v1[0] - v2[0]; + final float dy = v1[1] - v2[1]; + final float dz = v1[2] - v2[2]; + return dx * dx + dy * dy + dz * dz; + } + + /** + * Return the distance between the given two points described vector v1 and v2. + */ + public static float distVec3(final float[] v1, final float[] v2) { + return FloatUtil.sqrt(distSquareVec3(v1, v2)); + } + + /** + * Return the dot product of two points + * @param vec1 vector 1 + * @param vec2 vector 2 + * @return the dot product as float + */ + public static float dotVec3(final float[] vec1, final float[] vec2) { + return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; + } + + /** + * Return the cosines of the angle between to vectors + * @param vec1 vector 1 + * @param vec2 vector 2 + */ + public static float cosAngleVec3(final float[] vec1, final float[] vec2) { + return dotVec3(vec1, vec2) / ( normVec3(vec1) * normVec3(vec2) ) ; + } + + /** + * Return the angle between to vectors in radians + * @param vec1 vector 1 + * @param vec2 vector 2 + */ + public static float angleVec3(final float[] vec1, final float[] vec2) { + return FloatUtil.acos(cosAngleVec3(vec1, vec2)); + } + + /** + * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public static float normSquareVec2(final float[] vec) { + return vec[0]*vec[0] + vec[1]*vec[1]; + } + + /** + * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public static float normSquareVec2(final float[] vec, final int offset) { + float v = vec[0+offset]; + final float r = v*v; + v = vec[1+offset]; + return r + v*v; + } + + /** + * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public static float normSquareVec3(final float[] vec) { + return vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]; + } + + /** + * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public static float normSquareVec3(final float[] vec, final int offset) { + float v = vec[0+offset]; + float r = v*v; + v = vec[1+offset]; + r += v*v; + v = vec[2+offset]; + return r + v*v; + } + + /** + * Return the length of a vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public static float normVec2(final float[] vec) { + return FloatUtil.sqrt(normSquareVec2(vec)); + } + + /** + * Return the length of a vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public static float normVec3(final float[] vec) { + return FloatUtil.sqrt(normSquareVec3(vec)); + } + + /** + * Normalize a vector + * @param result output vector, may be vector (in-place) + * @param vector input vector + * @return normalized output vector + * @return result vector for chaining + */ + public static float[] normalizeVec2(final float[] result, final float[] vector) { + final float lengthSq = normSquareVec2(vector); + if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { + result[0] = 0f; + result[1] = 0f; + } else { + final float invSqr = 1f / FloatUtil.sqrt(lengthSq); + result[0] = vector[0] * invSqr; + result[1] = vector[1] * invSqr; + } + return result; + } + + /** + * Normalize a vector in place + * @param vector input vector + * @return normalized output vector + */ + public static float[] normalizeVec2(final float[] vector) { + final float lengthSq = normSquareVec2(vector); + if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { + vector[0] = 0f; + vector[1] = 0f; + } else { + final float invSqr = 1f / FloatUtil.sqrt(lengthSq); + vector[0] *= invSqr; + vector[1] *= invSqr; + } + return vector; + } + + /** + * Normalize a vector + * @param result output vector, may be vector (in-place) + * @param vector input vector + * @return normalized output vector + * @return result vector for chaining + */ + public static float[] normalizeVec3(final float[] result, final float[] vector) { + final float lengthSq = normSquareVec3(vector); + if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { + result[0] = 0f; + result[1] = 0f; + result[2] = 0f; + } else { + final float invSqr = 1f / FloatUtil.sqrt(lengthSq); + result[0] = vector[0] * invSqr; + result[1] = vector[1] * invSqr; + result[2] = vector[2] * invSqr; + } + return result; + } + + /** + * Normalize a vector in place + * @param vector input vector + * @return normalized output vector + */ + public static float[] normalizeVec3(final float[] vector) { + final float lengthSq = normSquareVec3(vector); + if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { + vector[0] = 0f; + vector[1] = 0f; + vector[2] = 0f; + } else { + final float invSqr = 1f / FloatUtil.sqrt(lengthSq); + vector[0] *= invSqr; + vector[1] *= invSqr; + vector[2] *= invSqr; + } + return vector; + } + + /** + * Normalize a vector in place + * @param vector input vector + * @return normalized output vector + */ + public static float[] normalizeVec3(final float[] vector, final int offset) { + final float lengthSq = normSquareVec3(vector, offset); + if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { + vector[0+offset] = 0f; + vector[1+offset] = 0f; + vector[2+offset] = 0f; + } else { + final float invSqr = 1f / FloatUtil.sqrt(lengthSq); + vector[0+offset] *= invSqr; + vector[1+offset] *= invSqr; + vector[2+offset] *= invSqr; + } + return vector; + } + + /** + * Scales a vector by param using given result float[], result = vector * scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale single scale constant for all vector components + * @return result vector for chaining + */ + public static float[] scaleVec2(final float[] result, final float[] vector, final float scale) { + result[0] = vector[0] * scale; + result[1] = vector[1] * scale; + return result; + } + + /** + * Scales a vector by param using given result float[], result = vector * scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale single scale constant for all vector components + * @return result vector for chaining + */ + public static float[] scaleVec3(final float[] result, final float[] vector, final float scale) { + result[0] = vector[0] * scale; + result[1] = vector[1] * scale; + result[2] = vector[2] * scale; + return result; + } + + /** + * Scales a vector by param using given result float[], result = vector * scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale 3 component scale constant for each vector component + * @return result vector for chaining + */ + public static float[] scaleVec3(final float[] result, final float[] vector, final float[] scale) + { + result[0] = vector[0] * scale[0]; + result[1] = vector[1] * scale[1]; + result[2] = vector[2] * scale[2]; + return result; + } + + /** + * Scales a vector by param using given result float[], result = vector * scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale 2 component scale constant for each vector component + * @return result vector for chaining + */ + public static float[] scaleVec2(final float[] result, final float[] vector, final float[] scale) + { + result[0] = vector[0] * scale[0]; + result[1] = vector[1] * scale[1]; + return result; + } + + /** + * Divides a vector by param using given result float[], result = vector / scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale single scale constant for all vector components + * @return result vector for chaining + */ + public static float[] divVec2(final float[] result, final float[] vector, final float scale) { + result[0] = vector[0] / scale; + result[1] = vector[1] / scale; + return result; + } + + /** + * Divides a vector by param using given result float[], result = vector / scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale single scale constant for all vector components + * @return result vector for chaining + */ + public static float[] divVec3(final float[] result, final float[] vector, final float scale) { + result[0] = vector[0] / scale; + result[1] = vector[1] / scale; + result[2] = vector[2] / scale; + return result; + } + + /** + * Divides a vector by param using given result float[], result = vector / scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale 3 component scale constant for each vector component + * @return result vector for chaining + */ + public static float[] divVec3(final float[] result, final float[] vector, final float[] scale) + { + result[0] = vector[0] / scale[0]; + result[1] = vector[1] / scale[1]; + result[2] = vector[2] / scale[2]; + return result; + } + + /** + * Divides a vector by param using given result float[], result = vector / scale + * @param result vector for the result, may be vector (in-place) + * @param vector input vector + * @param scale 2 component scale constant for each vector component + * @return result vector for chaining + */ + public static float[] divVec2(final float[] result, final float[] vector, final float[] scale) + { + result[0] = vector[0] / scale[0]; + result[1] = vector[1] / scale[1]; + return result; + } + + /** + * Adds two vectors, result = v1 + v2 + * @param result float[2] result vector, may be either v1 or v2 (in-place) + * @param v1 vector 1 + * @param v2 vector 2 + * @return result vector for chaining + */ + public static float[] addVec2(final float[] result, final float[] v1, final float[] v2) { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + return result; + } + + /** + * Adds two vectors, result = v1 + v2 + * @param result float[3] result vector, may be either v1 or v2 (in-place) + * @param v1 vector 1 + * @param v2 vector 2 + * @return result vector for chaining + */ + public static float[] addVec3(final float[] result, final float[] v1, final float[] v2) { + result[0] = v1[0] + v2[0]; + result[1] = v1[1] + v2[1]; + result[2] = v1[2] + v2[2]; + return result; + } + + /** + * Subtracts two vectors, result = v1 - v2 + * @param result float[2] result vector, may be either v1 or v2 (in-place) + * @param v1 vector 1 + * @param v2 vector 2 + * @return result vector for chaining + */ + public static float[] subVec2(final float[] result, final float[] v1, final float[] v2) { + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + return result; + } + + /** + * Subtracts two vectors, result = v1 - v2 + * @param result float[3] result vector, may be either v1 or v2 (in-place) + * @param v1 vector 1 + * @param v2 vector 2 + * @return result vector for chaining + */ + public static float[] subVec3(final float[] result, final float[] v1, final float[] v2) { + result[0] = v1[0] - v2[0]; + result[1] = v1[1] - v2[1]; + result[2] = v1[2] - v2[2]; + return result; + } + + /** + * cross product vec1 x vec2 + * @param v1 vector 1 + * @param v2 vector 2 + * @return the resulting vector + */ + public static float[] crossVec3(final float[] result, final float[] v1, final float[] v2) + { + result[0] = v1[1] * v2[2] - v1[2] * v2[1]; + result[1] = v1[2] * v2[0] - v1[0] * v2[2]; + result[2] = v1[0] * v2[1] - v1[1] * v2[0]; + return result; + } + + /** + * cross product vec1 x vec2 + * @param v1 vector 1 + * @param v2 vector 2 + * @return the resulting vector + */ + public static float[] crossVec3(final float[] r, final int r_offset, final float[] v1, final int v1_offset, final float[] v2, final int v2_offset) + { + r[0+r_offset] = v1[1+v1_offset] * v2[2+v2_offset] - v1[2+v1_offset] * v2[1+v2_offset]; + r[1+r_offset] = v1[2+v1_offset] * v2[0+v2_offset] - v1[0+v1_offset] * v2[2+v2_offset]; + r[2+r_offset] = v1[0+v1_offset] * v2[1+v2_offset] - v1[1+v1_offset] * v2[0+v2_offset]; + return r; + } + + /** + * Multiplication of column-major 4x4 matrix with vector + * @param colMatrix column matrix (4x4) + * @param vec vector(x,y,z) + * @return result + */ + public static float[] mulColMat4Vec3(final float[] result, final float[] colMatrix, final float[] vec) + { + result[0] = vec[0]*colMatrix[0] + vec[1]*colMatrix[4] + vec[2]*colMatrix[8] + colMatrix[12]; + result[1] = vec[0]*colMatrix[1] + vec[1]*colMatrix[5] + vec[2]*colMatrix[9] + colMatrix[13]; + result[2] = vec[0]*colMatrix[2] + vec[1]*colMatrix[6] + vec[2]*colMatrix[10] + colMatrix[14]; + + return result; + } + + /** + * Matrix Vector multiplication + * @param rawMatrix column matrix (4x4) + * @param vec vector(x,y,z) + * @return result + */ + public static float[] mulRowMat4Vec3(final float[] result, final float[] rawMatrix, final float[] vec) + { + result[0] = vec[0]*rawMatrix[0] + vec[1]*rawMatrix[1] + vec[2]*rawMatrix[2] + rawMatrix[3]; + result[1] = vec[0]*rawMatrix[4] + vec[1]*rawMatrix[5] + vec[2]*rawMatrix[6] + rawMatrix[7]; + result[2] = vec[0]*rawMatrix[8] + vec[1]*rawMatrix[9] + vec[2]*rawMatrix[10] + rawMatrix[11]; + + return result; + } + + /** + * Calculate the midpoint of two values + * @param p1 first value + * @param p2 second vale + * @return midpoint + */ + public static float mid(final float p1, final float p2) { + return (p1+p2)*0.5f; + } + + /** + * Calculate the midpoint of two points + * @param p1 first point vector + * @param p2 second point vector + * @return midpoint + */ + public static float[] midVec3(final float[] result, final float[] p1, final float[] p2) { + result[0] = (p1[0] + p2[0])*0.5f; + result[1] = (p1[1] + p2[1])*0.5f; + result[2] = (p1[2] + p2[2])*0.5f; + return result; + } + + /** + * Return the determinant of 3 vectors + * @param a vector 1 + * @param b vector 2 + * @param c vector 3 + * @return the determinant value + */ + public static float determinantVec3(final float[] a, final float[] b, final float[] c) { + return a[0]*b[1]*c[2] + a[1]*b[2]*c[0] + a[2]*b[0]*c[1] - a[0]*b[2]*c[1] - a[1]*b[0]*c[2] - a[2]*b[1]*c[0]; + } + + /** + * Check if three vertices are colliniear + * @param v1 vertex 1 + * @param v2 vertex 2 + * @param v3 vertex 3 + * @return true if collinear, false otherwise + */ + public static boolean isCollinearVec3(final float[] v1, final float[] v2, final float[] v3) { + return FloatUtil.isZero( determinantVec3(v1, v2, v3), FloatUtil.EPSILON ); + } + + /** + * Check if vertices in triangle circumcircle + * @param a triangle vertex 1 + * @param b triangle vertex 2 + * @param c triangle vertex 3 + * @param d vertex in question + * @return true if the vertex d is inside the circle defined by the + * vertices a, b, c. from paper by Guibas and Stolfi (1985). + */ + public static boolean isInCircleVec2(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { + final float[] A = a.getCoord(); + final float[] B = b.getCoord(); + final float[] C = c.getCoord(); + final float[] D = d.getCoord(); + return (A[0] * A[0] + A[1] * A[1]) * triAreaVec2(B, C, D) - + (B[0] * B[0] + B[1] * B[1]) * triAreaVec2(A, C, D) + + (C[0] * C[0] + C[1] * C[1]) * triAreaVec2(A, B, D) - + (D[0] * D[0] + D[1] * D[1]) * triAreaVec2(A, B, C) > 0; + } + + /** + * Computes oriented area of a triangle + * @param a first vertex + * @param b second vertex + * @param c third vertex + * @return compute twice the area of the oriented triangle (a,b,c), the area + * is positive if the triangle is oriented counterclockwise. + */ + public static float triAreaVec2(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c){ + final float[] A = a.getCoord(); + final float[] B = b.getCoord(); + final float[] C = c.getCoord(); + return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]); + } + + /** + * Computes oriented area of a triangle + * @param A first vertex + * @param B second vertex + * @param C third vertex + * @return compute twice the area of the oriented triangle (a,b,c), the area + * is positive if the triangle is oriented counterclockwise. + */ + public static float triAreaVec2(final float[] A, final float[] B, final float[] C){ + return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1])*(C[0] - A[0]); + } + + /** + * Check if a vertex is in triangle using + * barycentric coordinates computation. + * @param a first triangle vertex + * @param b second triangle vertex + * @param c third triangle vertex + * @param p the vertex in question + * @return true if p is in triangle (a, b, c), false otherwise. + */ + public static boolean isInTriangleVec3(final float[] a, final float[] b, final float[] c, + final float[] p, + final float[] ac, final float[] ab, final float[] ap){ + // Compute vectors + subVec3(ac, c, a); //v0 + subVec3(ab, b, a); //v1 + subVec3(ap, p, a); //v2 + + // Compute dot products + final float dotAC_AC = dotVec3(ac, ac); + final float dotAC_AB = dotVec3(ac, ab); + final float dotAB_AB = dotVec3(ab, ab); + final float dotAC_AP = dotVec3(ac, ap); + final float dotAB_AP = dotVec3(ab, ap); + + // Compute barycentric coordinates + final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); + final float u = (dotAB_AB * dotAC_AP - dotAC_AB * dotAB_AP) * invDenom; + final float v = (dotAC_AC * dotAB_AP - dotAC_AB * dotAC_AP) * invDenom; + + // Check if point is in triangle + return (u >= 0) && (v >= 0) && (u + v < 1); + } + + /** + * Check if one of three vertices are in triangle using + * barycentric coordinates computation. + * @param a first triangle vertex + * @param b second triangle vertex + * @param c third triangle vertex + * @param p1 the vertex in question + * @param p2 the vertex in question + * @param p3 the vertex in question + * @param tmpAC + * @param tmpAB + * @param tmpAP + * @return true if p1 or p2 or p3 is in triangle (a, b, c), false otherwise. + */ + public static boolean isVec3InTriangle3(final float[] a, final float[] b, final float[] c, + final float[] p1, final float[] p2, final float[] p3, + final float[] tmpAC, final float[] tmpAB, final float[] tmpAP){ + // Compute vectors + subVec3(tmpAC, c, a); //v0 + subVec3(tmpAB, b, a); //v1 + + // Compute dot products + final float dotAC_AC = dotVec3(tmpAC, tmpAC); + final float dotAC_AB = dotVec3(tmpAC, tmpAB); + final float dotAB_AB = dotVec3(tmpAB, tmpAB); + + // Compute barycentric coordinates + final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); + { + subVec3(tmpAP, p1, a); //v2 + final float dotAC_AP1 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP1 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP1 - dotAC_AB * dotAB_AP1) * invDenom; + final float v = (dotAC_AC * dotAB_AP1 - dotAC_AB * dotAC_AP1) * invDenom; + + // Check if point is in triangle + if ( (u >= 0) && (v >= 0) && (u + v < 1) ) { + return true; + } + } + + { + subVec3(tmpAP, p1, a); //v2 + final float dotAC_AP2 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP2 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP2 - dotAC_AB * dotAB_AP2) * invDenom; + final float v = (dotAC_AC * dotAB_AP2 - dotAC_AB * dotAC_AP2) * invDenom; + + // Check if point is in triangle + if ( (u >= 0) && (v >= 0) && (u + v < 1) ) { + return true; + } + } + + { + subVec3(tmpAP, p2, a); //v2 + final float dotAC_AP3 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP3 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP3 - dotAC_AB * dotAB_AP3) * invDenom; + final float v = (dotAC_AC * dotAB_AP3 - dotAC_AB * dotAC_AP3) * invDenom; + + // Check if point is in triangle + if ( (u >= 0) && (v >= 0) && (u + v < 1) ) { + return true; + } + } + return false; + } + /** + * Check if one of three vertices are in triangle using + * barycentric coordinates computation, using given epsilon for comparison. + * @param a first triangle vertex + * @param b second triangle vertex + * @param c third triangle vertex + * @param p1 the vertex in question + * @param p2 the vertex in question + * @param p3 the vertex in question + * @param tmpAC + * @param tmpAB + * @param tmpAP + * @return true if p1 or p2 or p3 is in triangle (a, b, c), false otherwise. + */ + public static boolean isVec3InTriangle3(final float[] a, final float[] b, final float[] c, + final float[] p1, final float[] p2, final float[] p3, + final float[] tmpAC, final float[] tmpAB, final float[] tmpAP, + final float epsilon){ + // Compute vectors + subVec3(tmpAC, c, a); //v0 + subVec3(tmpAB, b, a); //v1 + + // Compute dot products + final float dotAC_AC = dotVec3(tmpAC, tmpAC); + final float dotAC_AB = dotVec3(tmpAC, tmpAB); + final float dotAB_AB = dotVec3(tmpAB, tmpAB); + + // Compute barycentric coordinates + final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); + { + subVec3(tmpAP, p1, a); //v2 + final float dotAC_AP1 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP1 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP1 - dotAC_AB * dotAB_AP1) * invDenom; + final float v = (dotAC_AC * dotAB_AP1 - dotAC_AB * dotAC_AP1) * invDenom; + + // Check if point is in triangle + if( FloatUtil.compare(u, 0.0f, epsilon) >= 0 && + FloatUtil.compare(v, 0.0f, epsilon) >= 0 && + FloatUtil.compare(u+v, 1.0f, epsilon) < 0 ) { + return true; + } + } + + { + subVec3(tmpAP, p1, a); //v2 + final float dotAC_AP2 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP2 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP2 - dotAC_AB * dotAB_AP2) * invDenom; + final float v = (dotAC_AC * dotAB_AP2 - dotAC_AB * dotAC_AP2) * invDenom; + + // Check if point is in triangle + if( FloatUtil.compare(u, 0.0f, epsilon) >= 0 && + FloatUtil.compare(v, 0.0f, epsilon) >= 0 && + FloatUtil.compare(u+v, 1.0f, epsilon) < 0 ) { + return true; + } + } + + { + subVec3(tmpAP, p2, a); //v2 + final float dotAC_AP3 = dotVec3(tmpAC, tmpAP); + final float dotAB_AP3 = dotVec3(tmpAB, tmpAP); + final float u = (dotAB_AB * dotAC_AP3 - dotAC_AB * dotAB_AP3) * invDenom; + final float v = (dotAC_AC * dotAB_AP3 - dotAC_AB * dotAC_AP3) * invDenom; + + // Check if point is in triangle + if( FloatUtil.compare(u, 0.0f, epsilon) >= 0 && + FloatUtil.compare(v, 0.0f, epsilon) >= 0 && + FloatUtil.compare(u+v, 1.0f, epsilon) < 0 ) { + return true; + } + } + + return false; + } + + /** Check if points are in ccw order + * @param a first vertex + * @param b second vertex + * @param c third vertex + * @return true if the points a,b,c are in a ccw order + */ + public static boolean ccw(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c){ + return triAreaVec2(a,b,c) > 0; + } + + /** Compute the winding of given points + * @param a first vertex + * @param b second vertex + * @param c third vertex + * @return Winding + */ + public static Winding getWinding(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c) { + return triAreaVec2(a,b,c) > 0 ? Winding.CCW : Winding.CW ; + } + + /** Computes the area of a list of vertices to check if ccw + * @param vertices + * @return positive area if ccw else negative area value + */ + public static float area(final ArrayList<? extends Vert2fImmutable> vertices) { + final int n = vertices.size(); + float area = 0.0f; + for (int p = n - 1, q = 0; q < n; p = q++) + { + final float[] pCoord = vertices.get(p).getCoord(); + final float[] qCoord = vertices.get(q).getCoord(); + area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1]; + } + return area; + } + + /** Compute the general winding of the vertices + * @param vertices array of Vertices + * @return CCW or CW {@link Winding} + */ + public static Winding getWinding(final ArrayList<? extends Vert2fImmutable> vertices) { + return area(vertices) >= 0 ? Winding.CCW : Winding.CW ; + } + + /** + * @param result vec2 result for normal + * @param v1 vec2 + * @param v2 vec2 + * @return result for chaining + */ + public static float[] getNormalVec2(final float[] result, final float[] v1, final float[] v2 ) { + subVec2(result, v2, v1); + final float tmp = result [ 0 ] ; result [ 0 ] = -result [ 1 ] ; result [ 1 ] = tmp ; + return normalizeVec2 ( result ) ; + } + + /** + * Returns the 3d surface normal of a triangle given three vertices. + * + * @param result vec3 result for normal + * @param v1 vec3 + * @param v2 vec3 + * @param v3 vec3 + * @param tmp1Vec3 temp vec3 + * @param tmp2Vec3 temp vec3 + * @return result for chaining + */ + public static float[] getNormalVec3(final float[] result, final float[] v1, final float[] v2, final float[] v3, + final float[] tmp1Vec3, final float[] tmp2Vec3) { + subVec3 ( tmp1Vec3, v2, v1 ); + subVec3 ( tmp2Vec3, v3, v1 ) ; + return normalizeVec3 ( crossVec3(result, tmp1Vec3, tmp2Vec3) ) ; + } + + /** + * Finds the plane equation of a plane given its normal and a point on the plane. + * + * @param resultV4 vec4 plane equation + * @param normalVec3 + * @param pVec3 + * @return result for chaining + */ + public static float[] getPlaneVec3(final float[/*4*/] resultV4, final float[] normalVec3, final float[] pVec3) { + /** + Ax + By + Cz + D == 0 ; + D = - ( Ax + By + Cz ) + = - ( A*a[0] + B*a[1] + C*a[2] ) + = - vec3Dot ( normal, a ) ; + */ + System.arraycopy(normalVec3, 0, resultV4, 0, 3); + resultV4 [ 3 ] = -dotVec3(normalVec3, pVec3) ; + return resultV4; + } + + /** + * This finds the plane equation of a triangle given three vertices. + * + * @param resultVec4 vec4 plane equation + * @param v1 vec3 + * @param v2 vec3 + * @param v3 vec3 + * @param temp1V3 + * @param temp2V3 + * @return result for chaining + */ + public static float[] getPlaneVec3(final float[/*4*/] resultVec4, final float[] v1, final float[] v2, final float[] v3, + final float[] temp1V3, final float[] temp2V3) { + /** + Ax + By + Cz + D == 0 ; + D = - ( Ax + By + Cz ) + = - ( A*a[0] + B*a[1] + C*a[2] ) + = - vec3Dot ( normal, a ) ; + */ + getNormalVec3( resultVec4, v1, v2, v3, temp1V3, temp2V3 ) ; + resultVec4 [ 3 ] = -dotVec3 (resultVec4, v1) ; + return resultVec4; + } + + /** + * Return intersection of an infinite line with a plane if exists, otherwise null. + * <p> + * Thanks to <i>Norman Vine -- [email protected] (with hacks by Steve)</i> + * </p> + * + * @param result vec3 result buffer for intersecting coords + * @param ray here representing an infinite line, origin and direction. + * @param plane vec4 plane equation + * @param epsilon + * @return resulting intersecting if exists, otherwise null + */ + public static float[] line2PlaneIntersection(final float[] result, final Ray ray, final float[/*4*/] plane, final float epsilon) { + final float tmp = dotVec3(ray.dir, plane) ; + + if ( Math.abs(tmp) < epsilon ) { + return null; // ray is parallel to plane + } + scaleVec3 ( result, ray.dir, -( dotVec3(ray.orig, plane) + plane[3] ) / tmp ) ; + return addVec3(result, result, ray.orig); + } + + /** Compute intersection between two segments + * @param a vertex 1 of first segment + * @param b vertex 2 of first segment + * @param c vertex 1 of second segment + * @param d vertex 2 of second segment + * @return the intersection coordinates if the segments intersect, otherwise returns null + */ + public static float[] seg2SegIntersection(final float[] result, final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { + final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); + + if (determinant == 0) + return null; + + final float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); + final float beta = (c.getX()*d.getY()-c.getY()*d.getY()); + final float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; + final float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; + + final float gamma = (xi - a.getX())/(b.getX() - a.getX()); + final float gamma1 = (xi - c.getX())/(d.getX() - c.getX()); + if(gamma <= 0 || gamma >= 1) return null; + if(gamma1 <= 0 || gamma1 >= 1) return null; + + result[0] = xi; + result[1] = yi; + result[2] = 0; + return result; + } + + /** + * Compute intersection between two segments + * @param a vertex 1 of first segment + * @param b vertex 2 of first segment + * @param c vertex 1 of second segment + * @param d vertex 2 of second segment + * @return true if the segments intersect, otherwise returns false + */ + public static boolean testSeg2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, + final Vert2fImmutable c, final Vert2fImmutable d) { + final float[] A = a.getCoord(); + final float[] B = b.getCoord(); + final float[] C = c.getCoord(); + final float[] D = d.getCoord(); + + final float determinant = (A[0]-B[0])*(C[1]-D[1]) - (A[1]-B[1])*(C[0]-D[0]); + + if (determinant == 0) { + return false; + } + + final float alpha = (A[0]*B[1]-A[1]*B[0]); + final float beta = (C[0]*D[1]-C[1]*D[1]); + final float xi = ((C[0]-D[0])*alpha-(A[0]-B[0])*beta)/determinant; + + final float gamma0 = (xi - A[0])/(B[0] - A[0]); + final float gamma1 = (xi - C[0])/(D[0] - C[0]); + if(gamma0 <= 0 || gamma0 >= 1 || gamma1 <= 0 || gamma1 >= 1) { + return false; + } + + return true; + } + /** + * Compute intersection between two segments, using given epsilon for comparison. + * @param a vertex 1 of first segment + * @param b vertex 2 of first segment + * @param c vertex 1 of second segment + * @param d vertex 2 of second segment + * @return true if the segments intersect, otherwise returns false + */ + public static boolean testSeg2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, + final Vert2fImmutable c, final Vert2fImmutable d, + final float epsilon) { + final float[] A = a.getCoord(); + final float[] B = b.getCoord(); + final float[] C = c.getCoord(); + final float[] D = d.getCoord(); + + final float determinant = (A[0]-B[0])*(C[1]-D[1]) - (A[1]-B[1])*(C[0]-D[0]); + + if ( FloatUtil.isZero(determinant, epsilon) ) { + return false; + } + + final float alpha = (A[0]*B[1]-A[1]*B[0]); + final float beta = (C[0]*D[1]-C[1]*D[1]); + final float xi = ((C[0]-D[0])*alpha-(A[0]-B[0])*beta)/determinant; + + final float gamma0 = (xi - A[0])/(B[0] - A[0]); + final float gamma1 = (xi - C[0])/(D[0] - C[0]); + if( FloatUtil.compare(gamma0, 0.0f, epsilon) <= 0 || + FloatUtil.compare(gamma0, 1.0f, epsilon) >= 0 || + FloatUtil.compare(gamma1, 0.0f, epsilon) <= 0 || + FloatUtil.compare(gamma1, 1.0f, epsilon) >= 0 ) { + return false; + } + + if(gamma0 <= 0 || gamma0 >= 1 || gamma1 <= 0 || gamma1 >= 1) { + return false; + } + + return true; + } + + /** + * Compute intersection between two lines + * @param a vertex 1 of first line + * @param b vertex 2 of first line + * @param c vertex 1 of second line + * @param d vertex 2 of second line + * @return the intersection coordinates if the lines intersect, otherwise + * returns null + */ + public static float[] line2lineIntersection(final float[] result, + final Vert2fImmutable a, final Vert2fImmutable b, + final Vert2fImmutable c, final Vert2fImmutable d) { + final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); + + if (determinant == 0) + return null; + + final float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); + final float beta = (c.getX()*d.getY()-c.getY()*d.getY()); + final float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; + final float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; + + result[0] = xi; + result[1] = yi; + result[2] = 0; + return result; + } + + /** + * Check if a segment intersects with a triangle + * @param a vertex 1 of the triangle + * @param b vertex 2 of the triangle + * @param c vertex 3 of the triangle + * @param d vertex 1 of first segment + * @param e vertex 2 of first segment + * @return true if the segment intersects at least one segment of the triangle, false otherwise + */ + public static boolean testTri2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, + final Vert2fImmutable d, final Vert2fImmutable e){ + return testSeg2SegIntersection(a, b, d, e) || + testSeg2SegIntersection(b, c, d, e) || + testSeg2SegIntersection(a, c, d, e) ; + } + /** + * Check if a segment intersects with a triangle, using given epsilon for comparison. + * @param a vertex 1 of the triangle + * @param b vertex 2 of the triangle + * @param c vertex 3 of the triangle + * @param d vertex 1 of first segment + * @param e vertex 2 of first segment + * @return true if the segment intersects at least one segment of the triangle, false otherwise + */ + public static boolean testTri2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, + final Vert2fImmutable d, final Vert2fImmutable e, + final float epsilon){ + return testSeg2SegIntersection(a, b, d, e, epsilon) || + testSeg2SegIntersection(b, c, d, e, epsilon) || + testSeg2SegIntersection(a, c, d, e, epsilon) ; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java b/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java new file mode 100644 index 000000000..ec90b401f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java @@ -0,0 +1,39 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +public interface Vert2fImmutable { + float getX(); + + float getY(); + + int getCoordCount(); + + float[] getCoord(); + +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java b/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java new file mode 100644 index 000000000..76bd02fbc --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java @@ -0,0 +1,32 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math; + +public interface Vert3fImmutable extends Vert2fImmutable { + float getZ(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java new file mode 100644 index 000000000..29cc2b51a --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java @@ -0,0 +1,761 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math.geom; + +import jogamp.graph.geom.plane.AffineTransform; + +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.Ray; +import com.jogamp.opengl.math.VectorUtil; + + +/** + * Axis Aligned Bounding Box. Defined by two 3D coordinates (low and high) + * The low being the the lower left corner of the box, and the high being the upper + * right corner of the box. + * <p> + * A few references for collision detection, intersections: + * <pre> + * http://www.realtimerendering.com/intersections.html + * http://www.codercorner.com/RayAABB.cpp + * http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter0.htm + * http://realtimecollisiondetection.net/files/levine_swept_sat.txt + * </pre> + * </p> + * + */ +public class AABBox { + private static final boolean DEBUG = FloatUtil.DEBUG; + private final float[] low = new float[3]; + private final float[] high = new float[3]; + private final float[] center = new float[3]; + + /** + * Create an Axis Aligned bounding box (AABBox) + * where the low and and high MAX float Values. + */ + public AABBox() { + reset(); + } + + /** + * Create an AABBox copying all values from the given one + * @param src the box value to be used for the new instance + */ + public AABBox(final AABBox src) { + copy(src); + } + + /** + * Create an AABBox specifying the coordinates + * of the low and high + * @param lx min x-coordinate + * @param ly min y-coordnate + * @param lz min z-coordinate + * @param hx max x-coordinate + * @param hy max y-coordinate + * @param hz max z-coordinate + */ + public AABBox(final float lx, final float ly, final float lz, + final float hx, final float hy, final float hz) { + setSize(lx, ly, lz, hx, hy, hz); + } + + /** + * Create a AABBox defining the low and high + * @param low min xyz-coordinates + * @param high max xyz-coordinates + */ + public AABBox(final float[] low, final float[] high) { + setSize(low, high); + } + + /** + * resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. + * @return this AABBox for chaining + */ + public final AABBox reset() { + setLow(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE); + setHigh(-1*Float.MAX_VALUE,-1*Float.MAX_VALUE,-1*Float.MAX_VALUE); + center[0] = 0f; + center[1] = 0f; + center[2] = 0f; + return this; + } + + /** Get the max xyz-coordinates + * @return a float array containing the max xyz coordinates + */ + public final float[] getHigh() { + return high; + } + + private final void setHigh(final float hx, final float hy, final float hz) { + this.high[0] = hx; + this.high[1] = hy; + this.high[2] = hz; + } + + /** Get the min xyz-coordinates + * @return a float array containing the min xyz coordinates + */ + public final float[] getLow() { + return low; + } + + private final void setLow(final float lx, final float ly, final float lz) { + this.low[0] = lx; + this.low[1] = ly; + this.low[2] = lz; + } + + private final void computeCenter() { + center[0] = (high[0] + low[0])/2f; + center[1] = (high[1] + low[1])/2f; + center[2] = (high[2] + low[2])/2f; + } + + /** + * Copy given AABBox 'src' values to this AABBox. + * + * @param src source AABBox + * @return this AABBox for chaining + */ + public final AABBox copy(final AABBox src) { + System.arraycopy(src.low, 0, low, 0, 3); + System.arraycopy(src.high, 0, high, 0, 3); + System.arraycopy(src.center, 0, center, 0, 3); + return this; + } + + /** + * Set size of the AABBox specifying the coordinates + * of the low and high. + * + * @param low min xyz-coordinates + * @param high max xyz-coordinates + * @return this AABBox for chaining + */ + public final AABBox setSize(final float[] low, final float[] high) { + return setSize(low[0],low[1],low[2], high[0],high[1],high[2]); + } + + /** + * Set size of the AABBox specifying the coordinates + * of the low and high. + * + * @param lx min x-coordinate + * @param ly min y-coordnate + * @param lz min z-coordinate + * @param hx max x-coordinate + * @param hy max y-coordinate + * @param hz max z-coordinate + * @return this AABBox for chaining + */ + public final AABBox setSize(final float lx, final float ly, final float lz, + final float hx, final float hy, final float hz) { + this.low[0] = lx; + this.low[1] = ly; + this.low[2] = lz; + this.high[0] = hx; + this.high[1] = hy; + this.high[2] = hz; + computeCenter(); + return this; + } + + /** + * Resize the AABBox to encapsulate another AABox + * @param newBox AABBox to be encapsulated in + * @return this AABBox for chaining + */ + public final AABBox resize(final AABBox newBox) { + final float[] newLow = newBox.getLow(); + final float[] newHigh = newBox.getHigh(); + + /** test low */ + if (newLow[0] < low[0]) + low[0] = newLow[0]; + if (newLow[1] < low[1]) + low[1] = newLow[1]; + if (newLow[2] < low[2]) + low[2] = newLow[2]; + + /** test high */ + if (newHigh[0] > high[0]) + high[0] = newHigh[0]; + if (newHigh[1] > high[1]) + high[1] = newHigh[1]; + if (newHigh[2] > high[2]) + high[2] = newHigh[2]; + + computeCenter(); + return this; + } + + /** + * Resize the AABBox to encapsulate another AABox, which will be <i>transformed</i> on the fly first. + * @param newBox AABBox to be encapsulated in + * @param t the {@link AffineTransform} applied on <i>newBox</i> on the fly + * @param tmpV3 temp float[3] storage + * @return this AABBox for chaining + */ + public final AABBox resize(final AABBox newBox, final AffineTransform t, final float[] tmpV3) { + /** test low */ + { + final float[] newBoxLow = newBox.getLow(); + t.transform(newBoxLow, tmpV3); + tmpV3[2] = newBoxLow[2]; + if (tmpV3[0] < low[0]) + low[0] = tmpV3[0]; + if (tmpV3[1] < low[1]) + low[1] = tmpV3[1]; + if (tmpV3[2] < low[2]) + low[2] = tmpV3[2]; + } + + /** test high */ + { + final float[] newBoxHigh = newBox.getHigh(); + t.transform(newBoxHigh, tmpV3); + tmpV3[2] = newBoxHigh[2]; + if (tmpV3[0] > high[0]) + high[0] = tmpV3[0]; + if (tmpV3[1] > high[1]) + high[1] = tmpV3[1]; + if (tmpV3[2] > high[2]) + high[2] = tmpV3[2]; + } + + computeCenter(); + return this; + } + + /** + * Resize the AABBox to encapsulate the passed + * xyz-coordinates. + * @param x x-axis coordinate value + * @param y y-axis coordinate value + * @param z z-axis coordinate value + * @return this AABBox for chaining + */ + public final AABBox resize(final float x, final float y, final float z) { + /** test low */ + if (x < low[0]) { + low[0] = x; + } + if (y < low[1]) { + low[1] = y; + } + if (z < low[2]) { + low[2] = z; + } + + /** test high */ + if (x > high[0]) { + high[0] = x; + } + if (y > high[1]) { + high[1] = y; + } + if (z > high[2]) { + high[2] = z; + } + + computeCenter(); + return this; + } + + /** + * Resize the AABBox to encapsulate the passed + * xyz-coordinates. + * @param xyz xyz-axis coordinate values + * @param offset of the array + * @return this AABBox for chaining + */ + public final AABBox resize(final float[] xyz, final int offset) { + return resize(xyz[0+offset], xyz[1+offset], xyz[2+offset]); + } + + /** + * Resize the AABBox to encapsulate the passed + * xyz-coordinates. + * @param xyz xyz-axis coordinate values + * @return this AABBox for chaining + */ + public final AABBox resize(final float[] xyz) { + return resize(xyz[0], xyz[1], xyz[2]); + } + + /** + * Check if the x & y coordinates are bounded/contained + * by this AABBox + * @param x x-axis coordinate value + * @param y y-axis coordinate value + * @return true if x belong to (low.x, high.x) and + * y belong to (low.y, high.y) + */ + public final boolean contains(final float x, final float y) { + if(x<low[0] || x>high[0]){ + return false; + } + if(y<low[1]|| y>high[1]){ + return false; + } + return true; + } + + /** + * Check if the xyz coordinates are bounded/contained + * by this AABBox. + * @param x x-axis coordinate value + * @param y y-axis coordinate value + * @param z z-axis coordinate value + * @return true if x belong to (low.x, high.x) and + * y belong to (low.y, high.y) and z belong to (low.z, high.z) + */ + public final boolean contains(final float x, final float y, final float z) { + if(x<low[0] || x>high[0]){ + return false; + } + if(y<low[1]|| y>high[1]){ + return false; + } + if(z<low[2] || z>high[2]){ + return false; + } + return true; + } + + /** + * Check if there is a common region between this AABBox and the passed + * 2D region irrespective of z range + * @param x lower left x-coord + * @param y lower left y-coord + * @param w width + * @param h hight + * @return true if this AABBox might have a common region with this 2D region + */ + public final boolean intersects2DRegion(final float x, final float y, final float w, final float h) { + if (w <= 0 || h <= 0) { + return false; + } + + final float _w = getWidth(); + final float _h = getHeight(); + if (_w <= 0 || _h <= 0) { + return false; + } + + final float x0 = getMinX(); + final float y0 = getMinY(); + return (x + w > x0 && + y + h > y0 && + x < x0 + _w && + y < y0 + _h); + } + + /** + * Check if {@link Ray} intersects this bounding box. + * <p> + * Versions uses the SAT[1], testing 6 axes. + * Original code for OBBs from MAGIC. + * Rewritten for AABBs and reorganized for early exits[2]. + * </p> + * <pre> + * [1] SAT = Separating Axis Theorem + * [2] http://www.codercorner.com/RayAABB.cpp + * </pre> + * @param ray + * @return + */ + public final boolean intersectsRay(final Ray ray) { + // diff[XYZ] -> VectorUtil.subVec3(diff, ray.orig, center); + // ext[XYZ] -> extend VectorUtil.subVec3(ext, high, center); + + final float dirX = ray.dir[0]; + final float diffX = ray.orig[0] - center[0]; + final float extX = high[0] - center[0]; + if( Math.abs(diffX) > extX && diffX*dirX >= 0f ) return false; + + final float dirY = ray.dir[1]; + final float diffY = ray.orig[1] - center[1]; + final float extY = high[1] - center[1]; + if( Math.abs(diffY) > extY && diffY*dirY >= 0f ) return false; + + final float dirZ = ray.dir[2]; + final float diffZ = ray.orig[2] - center[2]; + final float extZ = high[2] - center[2]; + if( Math.abs(diffZ) > extZ && diffZ*dirZ >= 0f ) return false; + + final float absDirY = Math.abs(dirY); + final float absDirZ = Math.abs(dirZ); + + float f = dirY * diffZ - dirZ * diffY; + if( Math.abs(f) > extY*absDirZ + extZ*absDirY ) return false; + + final float absDirX = Math.abs(dirX); + + f = dirZ * diffX - dirX * diffZ; + if( Math.abs(f) > extX*absDirZ + extZ*absDirX ) return false; + + f = dirX * diffY - dirY * diffX; + if( Math.abs(f) > extX*absDirY + extY*absDirX ) return false; + + return true; + } + + /** + * Return intersection of a {@link Ray} with this bounding box, + * or null if none exist. + * <p> + * <ul> + * <li>Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 [2]</li> + * <li>Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)</li> + * <li>Epsilon value added by Klaus Hartmann.</li> + * </ul> + * </p> + * <p> + * Method is based on the requirements: + * <ul> + * <li>the integer representation of 0.0f is 0x00000000</li> + * <li>the sign bit of the float is the most significant one</li> + * </ul> + * </p> + * <p> + * Report bugs: [email protected] (original author) + * </p> + * <pre> + * [1] http://www.codercorner.com/RayAABB.cpp + * [2] http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c + * </pre> + * @param result vec3 + * @param ray + * @param epsilon + * @param assumeIntersection if true, method assumes an intersection, i.e. by pre-checking via {@link #intersectsRay(Ray)}. + * In this case method will not validate a possible non-intersection and just computes + * coordinates. + * @param tmp1V3 temp vec3 + * @param tmp2V3 temp vec3 + * @param tmp3V3 temp vec3 + * @return float[3] result of intersection coordinates, or null if none exists + */ + public final float[] getRayIntersection(final float[] result, final Ray ray, final float epsilon, + final boolean assumeIntersection, + final float[] tmp1V3, final float[] tmp2V3, final float[] tmp3V3) { + final float[] maxT = { -1f, -1f, -1f }; + + final float[] origin = ray.orig; + final float[] dir = ray.dir; + + boolean inside = true; + + // Find candidate planes. + for(int i=0; i<3; i++) { + if(origin[i] < low[i]) { + result[i] = low[i]; + inside = false; + + // Calculate T distances to candidate planes + if( 0 != Float.floatToIntBits(dir[i]) ) { + maxT[i] = (low[i] - origin[i]) / dir[i]; + } + } else if(origin[i] > high[i]) { + result[i] = high[i]; + inside = false; + + // Calculate T distances to candidate planes + if( 0 != Float.floatToIntBits(dir[i]) ) { + maxT[i] = (high[i] - origin[i]) / dir[i]; + } + } + } + + // Ray origin inside bounding box + if(inside) { + System.arraycopy(origin, 0, result, 0, 3); + return result; + } + + // Get largest of the maxT's for final choice of intersection + int whichPlane = 0; + if(maxT[1] > maxT[whichPlane]) { whichPlane = 1; } + if(maxT[2] > maxT[whichPlane]) { whichPlane = 2; } + + if( !assumeIntersection ) { + // Check final candidate actually inside box + if( 0 != ( Float.floatToIntBits(maxT[whichPlane]) & 0x80000000 ) ) { + return null; + } + + /** Use unrolled version below .. + for(int i=0; i<3; i++) { + if( i!=whichPlane ) { + result[i] = origin[i] + maxT[whichPlane] * dir[i]; + if(result[i] < minB[i] - epsilon || result[i] > maxB[i] + epsilon) { return null; } + // if(result[i] < minB[i] || result[i] > maxB[i] ) { return null; } + } + } */ + switch( whichPlane ) { + case 0: + result[1] = origin[1] + maxT[whichPlane] * dir[1]; + if(result[1] < low[1] - epsilon || result[1] > high[1] + epsilon) { return null; } + result[2] = origin[2] + maxT[whichPlane] * dir[2]; + if(result[2] < low[2] - epsilon || result[2] > high[2] + epsilon) { return null; } + break; + case 1: + result[0] = origin[0] + maxT[whichPlane] * dir[0]; + if(result[0] < low[0] - epsilon || result[0] > high[0] + epsilon) { return null; } + result[2] = origin[2] + maxT[whichPlane] * dir[2]; + if(result[2] < low[2] - epsilon || result[2] > high[2] + epsilon) { return null; } + break; + case 2: + result[0] = origin[0] + maxT[whichPlane] * dir[0]; + if(result[0] < low[0] - epsilon || result[0] > high[0] + epsilon) { return null; } + result[1] = origin[1] + maxT[whichPlane] * dir[1]; + if(result[1] < low[1] - epsilon || result[1] > high[1] + epsilon) { return null; } + break; + default: + throw new InternalError("XXX"); + } + } else { + switch( whichPlane ) { + case 0: + result[1] = origin[1] + maxT[whichPlane] * dir[1]; + result[2] = origin[2] + maxT[whichPlane] * dir[2]; + break; + case 1: + result[0] = origin[0] + maxT[whichPlane] * dir[0]; + result[2] = origin[2] + maxT[whichPlane] * dir[2]; + break; + case 2: + result[0] = origin[0] + maxT[whichPlane] * dir[0]; + result[1] = origin[1] + maxT[whichPlane] * dir[1]; + break; + default: + throw new InternalError("XXX"); + } + } + return result; // ray hits box + } + + /** + * Get the size of this AABBox where the size is represented by the + * length of the vector between low and high. + * @return a float representing the size of the AABBox + */ + public final float getSize() { + return VectorUtil.distVec3(low, high); + } + + /** + * Get the Center of this AABBox + * @return the xyz-coordinates of the center of the AABBox + */ + public final float[] getCenter() { + return center; + } + + /** + * Scale this AABBox by a constant + * @param size a constant float value + * @param tmpV3 caller provided temporary 3-component vector + * @return this AABBox for chaining + */ + public final AABBox scale(final float size, final float[] tmpV3) { + tmpV3[0] = high[0] - center[0]; + tmpV3[1] = high[1] - center[1]; + tmpV3[2] = high[2] - center[2]; + + VectorUtil.scaleVec3(tmpV3, tmpV3, size); // in-place scale + VectorUtil.addVec3(high, center, tmpV3); + + tmpV3[0] = low[0] - center[0]; + tmpV3[1] = low[1] - center[1]; + tmpV3[2] = low[2] - center[2]; + + VectorUtil.scaleVec3(tmpV3, tmpV3, size); // in-place scale + VectorUtil.addVec3(low, center, tmpV3); + return this; + } + + /** + * Translate this AABBox by a float[3] vector + * @param t the float[3] translation vector + * @return this AABBox for chaining + */ + public final AABBox translate(final float[] t) { + VectorUtil.addVec3(low, low, t); // in-place translate + VectorUtil.addVec3(high, high, t); // in-place translate + computeCenter(); + return this; + } + + /** + * Rotate this AABBox by a float[3] vector + * @param quat the {@link Quaternion} used for rotation + * @return this AABBox for chaining + */ + public final AABBox rotate(final Quaternion quat) { + quat.rotateVector(low, 0, low, 0); + quat.rotateVector(high, 0, high, 0); + computeCenter(); + return this; + } + + public final float getMinX() { + return low[0]; + } + + public final float getMinY() { + return low[1]; + } + + public final float getMinZ() { + return low[2]; + } + + public final float getMaxX() { + return high[0]; + } + + public final float getMaxY() { + return high[1]; + } + + public final float getMaxZ() { + return high[2]; + } + + public final float getWidth(){ + return high[0] - low[0]; + } + + public final float getHeight() { + return high[1] - low[1]; + } + + public final float getDepth() { + return high[2] - low[2]; + } + + @Override + public final boolean equals(final Object obj) { + if( obj == this ) { + return true; + } + if( null == obj || !(obj instanceof AABBox) ) { + return false; + } + final AABBox other = (AABBox) obj; + return VectorUtil.isVec2Equal(low, 0, other.low, 0, FloatUtil.EPSILON) && + VectorUtil.isVec3Equal(high, 0, other.high, 0, FloatUtil.EPSILON) ; + } + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + + /** + * Assume this bounding box as being in object space and + * compute the window bounding box. + * <p> + * If <code>useCenterZ</code> is <code>true</code>, + * only 4 {@link FloatUtil#mapObjToWinCoords(float, float, float, float[], int[], int, float[], int, float[], float[]) mapObjToWinCoords} + * operations are made on points [1..4] using {@link #getCenter()}'s z-value. + * Otherwise 8 {@link FloatUtil#mapObjToWinCoords(float, float, float, float[], int[], int, float[], int, float[], float[]) mapObjToWinCoords} + * operation on all 8 points are performed. + * </p> + * <pre> + * [2] ------ [4] + * | | + * | | + * [1] ------ [3] + * </pre> + * @param mat4PMv P x Mv matrix + * @param view + * @param useCenterZ + * @param vec3Tmp0 3 component vector for temp storage + * @param vec4Tmp1 4 component vector for temp storage + * @param vec4Tmp2 4 component vector for temp storage + * @return + */ + public AABBox mapToWindow(final AABBox result, final float[/*16*/] mat4PMv, final int[] view, final boolean useCenterZ, + final float[] vec3Tmp0, final float[] vec4Tmp1, final float[] vec4Tmp2) { + { + // System.err.printf("AABBox.mapToWindow.0: view[%d, %d, %d, %d], this %s%n", view[0], view[1], view[2], view[3], toString()); + final float objZ = useCenterZ ? center[2] : getMinZ(); + FloatUtil.mapObjToWinCoords(getMinX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + // System.err.printf("AABBox.mapToWindow.p1: %f, %f, %f -> %f, %f, %f%n", getMinX(), getMinY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); + // System.err.println("AABBox.mapToWindow.p1:"); + // System.err.println(FloatUtil.matrixToString(null, " mat4PMv", "%10.5f", mat4PMv, 0, 4, 4, false /* rowMajorOrder */)); + + result.reset(); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMinX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + // System.err.printf("AABBox.mapToWindow.p2: %f, %f, %f -> %f, %f, %f%n", getMinX(), getMaxY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMaxX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + // System.err.printf("AABBox.mapToWindow.p3: %f, %f, %f -> %f, %f, %f%n", getMaxX(), getMinY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMaxX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + // System.err.printf("AABBox.mapToWindow.p4: %f, %f, %f -> %f, %f, %f%n", getMaxX(), getMaxY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); + result.resize(vec3Tmp0, 0); + } + + if( !useCenterZ ) { + final float objZ = getMaxZ(); + FloatUtil.mapObjToWinCoords(getMinX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMinX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMaxX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + result.resize(vec3Tmp0, 0); + + FloatUtil.mapObjToWinCoords(getMaxX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); + result.resize(vec3Tmp0, 0); + } + if( DEBUG ) { + System.err.printf("AABBox.mapToWindow: view[%d, %d], this %s -> %s%n", view[0], view[1], toString(), result.toString()); + } + return result; + } + + @Override + public final String toString() { + return "[ dim "+getWidth()+" x "+getHeight()+" x "+getDepth()+ + ", box "+low[0]+" / "+low[1]+" / "+low[2]+" .. "+high[0]+" / "+high[1]+" / "+high[2]+ + ", ctr "+center[0]+" / "+center[1]+" / "+center[2]+" ]"; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java new file mode 100644 index 000000000..b73bad613 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java @@ -0,0 +1,398 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.math.geom; + +import jogamp.common.os.PlatformPropsImpl; + +import com.jogamp.common.os.Platform; + +/** + * Providing frustum {@link #getPlanes() planes} derived by different inputs + * ({@link #updateByPMV(float[], int) P*MV}, ..) used to classify objects + * <ul> + * <li> {@link #classifyPoint(float[]) point} </li> + * <li> {@link #classifySphere(float[], float) sphere} </li> + * </ul> + * and to test whether they are outside + * <ul> + * <li> {@link #isPointOutside(float[]) point} </li> + * <li> {@link #isSphereOutside(float[], float) sphere} </li> + * <li> {@link #isAABBoxOutside(AABBox) bounding-box} </li> + * </ul> + * + * <p> + * Extracting the world-frustum planes from the P*Mv: + * <pre> + * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix + * Gil Gribb <[email protected]> + * Klaus Hartmann <[email protected]> + * http://graphics.cs.ucf.edu/cap4720/fall2008/plane_extraction.pdf + * </pre> + * Classifying Point, Sphere and AABBox: + * <pre> + * Efficient View Frustum Culling + * Daniel Sýkora <[email protected]> + * Josef JelÃnek <[email protected]> + * http://www.cg.tuwien.ac.at/hostings/cescg/CESCG-2002/DSykoraJJelinek/index.html + * </pre> + * <pre> + * Lighthouse3d.com + * http://www.lighthouse3d.com/tutorials/view-frustum-culling/ + * </pre> + * + * Fundamentals about Planes, Half-Spaces and Frustum-Culling:<br/> + * <pre> + * Planes and Half-Spaces, Max Wagner <[email protected]> + * http://www.emeyex.com/site/tuts/PlanesHalfSpaces.pdf + * </pre> + * <pre> + * Frustum Culling, Max Wagner <[email protected]> + * http://www.emeyex.com/site/tuts/FrustumCulling.pdf + * </pre> + * </p> + */ +public class Frustum { + /** Normalized planes[l, r, b, t, n, f] */ + protected final Plane[] planes = new Plane[6]; + + /** + * Creates an undefined instance w/o calculating the frustum. + * <p> + * Use one of the <code>update(..)</code> methods to set the {@link #getPlanes() planes}. + * </p> + * @see #updateByPlanes(Plane[]) + * @see #updateByPMV(float[], int) + */ + public Frustum() { + for (int i = 0; i < 6; ++i) { + planes[i] = new Plane(); + } + } + + /** + * Plane equation := dot(n, x - p) = 0 -> ax + bc + cx + d == 0 + * <p> + * In order to work w/ {@link Frustum#isOutside(AABBox) isOutside(..)} methods, + * the normals have to point to the inside of the frustum. + * </p> + */ + public static class Plane { + /** Normal of the plane */ + public final float[] n = new float[3]; + + /** Distance to origin */ + public float d; + + /** + * Return signed distance of plane to given point. + * <ul> + * <li>If dist < 0 , then the point p lies in the negative halfspace.</li> + * <li>If dist = 0 , then the point p lies in the plane.</li> + * <li>If dist > 0 , then the point p lies in the positive halfspace.</li> + * </ul> + * A plane cuts 3D space into 2 half spaces. + * <p> + * Positive halfspace is where the plane’s normals vector points into. + * </p> + * <p> + * Negative halfspace is the <i>other side</i> of the plane, i.e. *-1 + * </p> + **/ + public final float distanceTo(final float x, final float y, final float z) { + return n[0] * x + n[1] * y + n[2] * z + d; + } + + /** Return distance of plane to given point, see {@link #distanceTo(float, float, float)}. */ + public final float distanceTo(final float[] p) { + return n[0] * p[0] + n[1] * p[1] + n[2] * p[2] + d; + } + + @Override + public String toString() { + return "Plane[ [ " + n[0] + ", " + n[1] + ", " + n[2] + " ], " + d + "]"; + } + } + + /** Index for left plane: {@value} */ + public static final int LEFT = 0; + /** Index for right plane: {@value} */ + public static final int RIGHT = 1; + /** Index for bottom plane: {@value} */ + public static final int BOTTOM = 2; + /** Index for top plane: {@value} */ + public static final int TOP = 3; + /** Index for near plane: {@value} */ + public static final int NEAR = 4; + /** Index for far plane: {@value} */ + public static final int FAR = 5; + + /** + * {@link Plane}s are ordered in the returned array as follows: + * <ul> + * <li>{@link #LEFT}</li> + * <li>{@link #RIGHT}</li> + * <li>{@link #BOTTOM}</li> + * <li>{@link #TOP}</li> + * <li>{@link #NEAR}</li> + * <li>{@link #FAR}</li> + * </ul> + * <p> + * {@link Plane}'s normals are pointing to the inside of the frustum + * in order to work w/ {@link #isOutside(AABBox) isOutside(..)} methods. + * </p> + * + * @return array of normalized {@link Plane}s, order see above. + */ + public final Plane[] getPlanes() { return planes; } + + /** + * Copy the given <code>src</code> planes into this this instance's planes. + * @param src the 6 source planes + */ + public final void updateByPlanes(final Plane[] src) { + for (int i = 0; i < 6; ++i) { + final Plane p0 = planes[i]; + final float[] p0_n = p0.n; + final Plane p1 = src[i]; + final float[] p1_n = p1.n; + p0_n[0] = p1_n[0]; + p0_n[1] = p1_n[1]; + p0_n[2] = p1_n[2]; + p0.d = p1.d; + } + } + + /** + * Calculate the frustum planes in world coordinates + * using the passed float[16] as premultiplied P*MV (column major order). + * <p> + * Frustum plane's normals will point to the inside of the viewing frustum, + * as required by this class. + * </p> + */ + public void updateByPMV(final float[] pmv, final int pmv_off) { + // Left: a = m41 + m11, b = m42 + m12, c = m43 + m13, d = m44 + m14 - [1..4] column-major + // Left: a = m30 + m00, b = m31 + m01, c = m32 + m02, d = m33 + m03 - [0..3] column-major + { + final Plane p = planes[LEFT]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 0 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 0 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 0 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 0 + 3 * 4 ]; + } + + // Right: a = m41 - m11, b = m42 - m12, c = m43 - m13, d = m44 - m14 - [1..4] column-major + // Right: a = m30 - m00, b = m31 - m01, c = m32 - m02, d = m33 - m03 - [0..3] column-major + { + final Plane p = planes[RIGHT]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 0 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 0 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 0 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 0 + 3 * 4 ]; + } + + // Bottom: a = m41 + m21, b = m42 + m22, c = m43 + m23, d = m44 + m24 - [1..4] column-major + // Bottom: a = m30 + m10, b = m31 + m11, c = m32 + m12, d = m33 + m13 - [0..3] column-major + { + final Plane p = planes[BOTTOM]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 1 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 1 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 1 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 1 + 3 * 4 ]; + } + + // Top: a = m41 - m21, b = m42 - m22, c = m43 - m23, d = m44 - m24 - [1..4] column-major + // Top: a = m30 - m10, b = m31 - m11, c = m32 - m12, d = m33 - m13 - [0..3] column-major + { + final Plane p = planes[TOP]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 1 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 1 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 1 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 1 + 3 * 4 ]; + } + + // Near: a = m41 + m31, b = m42 + m32, c = m43 + m33, d = m44 + m34 - [1..4] column-major + // Near: a = m30 + m20, b = m31 + m21, c = m32 + m22, d = m33 + m23 - [0..3] column-major + { + final Plane p = planes[NEAR]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 2 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 2 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 2 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 2 + 3 * 4 ]; + } + + // Far: a = m41 - m31, b = m42 - m32, c = m43 - m33, d = m44 - m34 - [1..4] column-major + // Far: a = m30 - m20, b = m31 - m21, c = m32 + m22, d = m33 + m23 - [0..3] column-major + { + final Plane p = planes[FAR]; + final float[] p_n = p.n; + p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 2 + 0 * 4 ]; + p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 2 + 1 * 4 ]; + p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 2 + 2 * 4 ]; + p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 2 + 3 * 4 ]; + } + + // Normalize all planes + for (int i = 0; i < 6; ++i) { + final Plane p = planes[i]; + final float[] p_n = p.n; + final double invl = Math.sqrt(p_n[0] * p_n[0] + p_n[1] * p_n[1] + p_n[2] * p_n[2]); + + p_n[0] /= invl; + p_n[1] /= invl; + p_n[2] /= invl; + p.d /= invl; + } + } + + private static final boolean isOutsideImpl(final Plane p, final AABBox box) { + final float[] low = box.getLow(); + final float[] high = box.getHigh(); + + if ( p.distanceTo(low[0], low[1], low[2]) > 0.0f || + p.distanceTo(high[0], low[1], low[2]) > 0.0f || + p.distanceTo(low[0], high[1], low[2]) > 0.0f || + p.distanceTo(high[0], high[1], low[2]) > 0.0f || + p.distanceTo(low[0], low[1], high[2]) > 0.0f || + p.distanceTo(high[0], low[1], high[2]) > 0.0f || + p.distanceTo(low[0], high[1], high[2]) > 0.0f || + p.distanceTo(high[0], high[1], high[2]) > 0.0f ) { + return false; + } + return true; + } + + /** + * Check to see if an axis aligned bounding box is completely outside of the frustum. + * <p> + * Note: If method returns false, the box may only be partially inside. + * </p> + */ + public final boolean isAABBoxOutside(final AABBox box) { + for (int i = 0; i < 6; ++i) { + if ( isOutsideImpl(planes[i], box) ) { + // fully outside + return true; + } + } + // We make no attempt to determine whether it's fully inside or not. + return false; + } + + + public static enum Location { OUTSIDE, INSIDE, INTERSECT }; + + /** + * Check to see if a point is outside, inside or on a plane of the frustum. + * + * @param p the point + * @return {@link Location} of point related to frustum planes + */ + public final Location classifyPoint(final float[] p) { + Location res = Location.INSIDE; + + for (int i = 0; i < 6; ++i) { + final float d = planes[i].distanceTo(p); + if ( d < 0.0f ) { + return Location.OUTSIDE; + } else if ( d == 0.0f ) { + res = Location.INTERSECT; + } + } + return res; + } + + /** + * Check to see if a point is outside of the frustum. + * + * @param p the point + * @return true if outside of the frustum, otherwise inside or on a plane + */ + public final boolean isPointOutside(final float[] p) { + return Location.OUTSIDE == classifyPoint(p); + } + + /** + * Check to see if a sphere is outside, intersecting or inside of the frustum. + * + * @param p center of the sphere + * @param radius radius of the sphere + * @return {@link Location} of point related to frustum planes + */ + public final Location classifySphere(final float[] p, final float radius) { + Location res = Location.INSIDE; // fully inside + + for (int i = 0; i < 6; ++i) { + final float d = planes[i].distanceTo(p); + if ( d < -radius ) { + // fully outside + return Location.OUTSIDE; + } else if (d < radius ) { + // intersecting + res = Location.INTERSECT; + } + } + return res; + } + + /** + * Check to see if a sphere is outside of the frustum. + * + * @param p center of the sphere + * @param radius radius of the sphere + * @return true if outside of the frustum, otherwise inside or intersecting + */ + public final boolean isSphereOutside(final float[] p, final float radius) { + return Location.OUTSIDE == classifySphere(p, radius); + } + + public StringBuilder toString(StringBuilder sb) { + if( null == sb ) { + sb = new StringBuilder(); + } + sb.append("Frustum[ Planes[ ").append(PlatformPropsImpl.NEWLINE) + .append(" L: ").append(planes[0]).append(", ").append(PlatformPropsImpl.NEWLINE) + .append(" R: ").append(planes[1]).append(", ").append(PlatformPropsImpl.NEWLINE) + .append(" B: ").append(planes[2]).append(", ").append(PlatformPropsImpl.NEWLINE) + .append(" T: ").append(planes[3]).append(", ").append(PlatformPropsImpl.NEWLINE) + .append(" N: ").append(planes[4]).append(", ").append(PlatformPropsImpl.NEWLINE) + .append(" F: ").append(planes[5]).append("], ").append(PlatformPropsImpl.NEWLINE) + .append("]"); + return sb; + } + + @Override + public String toString() { + return toString(null).toString(); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 02f62daec..28f572d7d 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -27,10 +27,18 @@ */ package com.jogamp.opengl.swt; +import java.util.List; + +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.ProxySurface; import javax.media.nativewindow.UpstreamSurfaceHook; +import javax.media.nativewindow.VisualIDHolder; +import javax.media.nativewindow.VisualIDHolder.VIDType; import javax.media.opengl.GL; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; @@ -44,23 +52,23 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; import javax.media.opengl.Threading; +import jogamp.nativewindow.x11.X11Util; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; import jogamp.opengl.GLDrawableImpl; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import com.jogamp.common.GlueGenVersion; @@ -69,15 +77,22 @@ import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.swt.SWTAccessor; +import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.opengl.JoglVersion; /** * Native SWT Canvas implementing GLAutoDrawable - * - * <p>Note: To employ custom GLCapabilities, NewtCanvasSWT shall be used instead.</p> - * + * <p> + * Implementation allows use of custom {@link GLCapabilities}. + * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ -public class GLCanvas extends Canvas implements GLAutoDrawable { +public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextSetter { private static final boolean DEBUG = Debug.debug("GLCanvas"); /* @@ -94,16 +109,19 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { /* GL Stuff */ private final RecursiveLock lock = LockFactory.createRecursiveLock(); private final GLDrawableHelper helper = new GLDrawableHelper(); - - private final GLContext shareWith; + private final GLCapabilitiesImmutable capsRequested; - private final GLCapabilitiesChooser capsChooser; - + private final GLCapabilitiesChooser capsChooser; + + private volatile Rectangle clientArea; private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access - private GLContextImpl context; + private volatile GLContextImpl context; // volatile: avoid locking for read-only access /* Native window surface */ - private AbstractGraphicsDevice device; + private final boolean useX11GTK; + private volatile long gdkWindow; // either GDK child window .. + private volatile long x11Window; // .. or X11 child window (for GL rendering) + private final AbstractGraphicsScreen screen; /* Construction parameters stored for GLAutoDrawable accessor methods */ private int additionalCtxCreationFlags = 0; @@ -112,13 +130,17 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { /* Flag indicating whether an unprocessed reshape is pending. */ private volatile boolean sendReshape; // volatile: maybe written by WindowManager thread w/o locking + private static String getThreadName() { return Thread.currentThread().getName(); } + private static String toHexString(final int v) { return "0x"+Integer.toHexString(v); } + private static String toHexString(final long v) { return "0x"+Long.toHexString(v); } + /* * Invokes init(...) on all GLEventListeners. Assumes context is current when run. */ private final Runnable initAction = new Runnable() { @Override public void run() { - helper.init(GLCanvas.this); + helper.init(GLCanvas.this, !sendReshape); } }; @@ -131,7 +153,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void run() { if (sendReshape) { - helper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + helper.reshape(GLCanvas.this, 0, 0, clientArea.width, clientArea.height); sendReshape = false; } helper.display(GLCanvas.this); @@ -139,13 +161,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Action to make specified context current prior to running displayAction */ - private final Runnable makeCurrentAndDisplayOnEDTAction = new Runnable() { + private final Runnable makeCurrentAndDisplayOnGLAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); - try { - helper.invokeGL(drawable, context, displayAction, initAction); + try { + if( !GLCanvas.this.isDisposed() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } } finally { _lock.unlock(); } @@ -153,84 +177,126 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { }; /* Swaps buffers, assuming the GLContext is current */ - private final Runnable swapBuffersAction = new Runnable() { + private final Runnable swapBuffersOnGLAction = new Runnable() { @Override public void run() { - drawable.swapBuffers(); - } - }; - - /* Swaps buffers, making the GLContext current first */ - private final Runnable makeCurrentAndSwapBuffersOnEDTAction = new Runnable() { - @Override - public void run() { - final RecursiveLock _lock = lock; - _lock.lock(); - try { - helper.invokeGL(drawable, context, swapBuffersAction, initAction); - } finally { - _lock.unlock(); - } + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final boolean drawableOK = null != drawable && drawable.isRealized(); + if( drawableOK && !GLCanvas.this.isDisposed() ) { + drawable.swapBuffers(); + } + } finally { + _lock.unlock(); + } } }; /* * Disposes of OpenGL resources */ - private final Runnable postDisposeGLAction = new Runnable() { - @Override - public void run() { - context = null; - if (null != drawable) { - drawable.setRealized(false); - drawable = null; - } - } - }; - private final Runnable disposeOnEDTGLAction = new Runnable() { @Override public void run() { final RecursiveLock _lock = lock; _lock.lock(); try { - if (null != drawable && null != context) { - boolean animatorPaused = false; - final GLAnimatorControl animator = getAnimator(); - if (null != animator) { - animatorPaused = animator.pause(); - } - - if(context.isCreated()) { - helper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); - } - - if (animatorPaused) { - animator.resume(); - } + final GLAnimatorControl animator = getAnimator(); + final boolean animatorPaused; + if(null!=animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } else { + animatorPaused = false; + } + + GLException exceptionOnDisposeGL = null; + if( null != context ) { + if( context.isCreated() ) { + try { + if( !GLCanvas.this.isDisposed() ) { + helper.disposeGL(GLCanvas.this, context, true); + } else { + context.destroy(); + } + } catch (final GLException gle) { + exceptionOnDisposeGL = gle; + } + } + context = null; + } + + Throwable exceptionOnUnrealize = null; + if( null != drawable ) { + try { + drawable.setRealized(false); + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } + drawable = null; + } + + Throwable exceptionOnDeviceClose = null; + try { + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; + } + screen.getDevice().close(); + } catch (final Throwable re) { + exceptionOnDeviceClose = re; + } + + if (animatorPaused) { + animator.resume(); + } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; } - // SWT is owner of the device handle, not us. - // Hence close() operation is a NOP. - if (null != device) { - device.close(); - device = null; + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if( null != exceptionOnDeviceClose ) { + throw GLException.newGLException(exceptionOnDeviceClose); } - SWTAccessor.setRealized(GLCanvas.this, false); // unrealize .. } finally { _lock.unlock(); } } }; - /** - * Storage for the client area rectangle so that it may be accessed from outside of the SWT thread. - */ - private volatile Rectangle clientArea; + private class DisposeGLEventListenerAction implements Runnable { + private GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(final GLEventListener listener, final boolean remove) { + this.listener = listener; + this.remove = remove; + } + + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !GLCanvas.this.isDisposed() ) { + listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); + } + } finally { + _lock.unlock(); + } + } + }; - /** - * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser, GLContext)} + /** + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} * on the SWT thread. - * + * * @param parent * Required (non-null) parent Composite. * @param style @@ -243,16 +309,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * @param chooser * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the * requested GLCapabilities, and the available capabilities of the graphics device. - * @param shareWith - * Optional GLContext to share state (textures, vbos, shaders, etc.) with. * @return a new instance */ public static GLCanvas create(final Composite parent, final int style, final GLCapabilitiesImmutable caps, - final GLCapabilitiesChooser chooser, final GLContext shareWith) { - final GLCanvas[] res = new GLCanvas[] { null }; + final GLCapabilitiesChooser chooser) { + final GLCanvas[] res = new GLCanvas[] { null }; parent.getDisplay().syncExec(new Runnable() { + @Override public void run() { - res[0] = new GLCanvas( parent, style, caps, chooser, shareWith ); + res[0] = new GLCanvas( parent, style, caps, chooser ); } }); return res[0]; @@ -266,18 +331,16 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * @param style * Optional SWT style bit-field. The {@link SWT#NO_BACKGROUND} bit is set before passing this up to the * Canvas constructor, so OpenGL handles the background. - * @param caps + * @param capsReqUser * Optional GLCapabilities. If not provided, the default capabilities for the default GLProfile for the * graphics device determined by the parent Composite are used. Note that the GLCapabilities that are * actually used may differ based on the capabilities of the graphics device. - * @param chooser + * @param capsChooser * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the * requested GLCapabilities, and the available capabilities of the graphics device. - * @param shareWith - * Optional GLContext to share state (textures, vbos, shaders, etc.) with. */ - public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable caps, - final GLCapabilitiesChooser chooser, final GLContext shareWith) { + public GLCanvas(final Composite parent, final int style, final GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser) { /* NO_BACKGROUND required to avoid clearing bg in native SWT widget (we do this in the GL display) */ super(parent, style | SWT.NO_BACKGROUND); @@ -287,54 +350,82 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { clientArea = GLCanvas.this.getClientArea(); - /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). - * Note: SWT is owner of the native handle, hence no closing operation will be a NOP. */ - device = SWTAccessor.getDevice(this); + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite). + * Note: SWT is owner of the native handle, hence closing operation will be a NOP. */ + final AbstractGraphicsDevice swtDevice = SWTAccessor.getDevice(this); - /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ - if(null == caps) { - caps = new GLCapabilities(GLProfile.getDefault(device)); + useX11GTK = SWTAccessor.useX11GTK(); + if(useX11GTK) { + // Decoupled X11 Device/Screen allowing X11 display lock-free off-thread rendering + final long x11DeviceHandle = X11Util.openDisplay(swtDevice.getConnection()); + if( 0 == x11DeviceHandle ) { + throw new RuntimeException("Error creating display(EDT): "+swtDevice.getConnection()); + } + final AbstractGraphicsDevice x11Device = new X11GraphicsDevice(x11DeviceHandle, AbstractGraphicsDevice.DEFAULT_UNIT, true /* owner */); + screen = SWTAccessor.getScreen(x11Device, -1 /* default */); + } else { + screen = SWTAccessor.getScreen(swtDevice, -1 /* default */); } - this.capsRequested = caps; - this.capsChooser = chooser; - this.shareWith = shareWith; + + /* Select default GLCapabilities if none was provided, otherwise use cloned provided caps */ + if(null == capsReqUser) { + this.capsRequested = new GLCapabilities(GLProfile.getDefault(screen.getDevice())); + } else { + this.capsRequested = (GLCapabilitiesImmutable) capsReqUser.cloneMutable(); + } + this.capsChooser = capsChooser; // post create .. when ready + gdkWindow = 0; + x11Window = 0; drawable = null; context = null; - - /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ - /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ - addPaintListener(new PaintListener() { - @Override - public void paintControl(final PaintEvent arg0) { - if ( !helper.isExternalAnimatorAnimating() ) { - display(); // checks: null != drawable - } - } - }); - addControlListener(new ControlAdapter() { - @Override - public void controlResized(final ControlEvent arg0) { - updateSizeCheck(); - } - }); + final Listener listener = new Listener () { + @Override + public void handleEvent (final Event event) { + switch (event.type) { + case SWT.Paint: + displayIfNoAnimatorNoCheck(); + break; + case SWT.Resize: + updateSizeCheck(); + break; + case SWT.Dispose: + GLCanvas.this.dispose(); + break; + } + } + }; + addListener (SWT.Resize, listener); + addListener (SWT.Paint, listener); + addListener (SWT.Dispose, listener); } + + @Override + public final void setSharedContext(final GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + private final UpstreamSurfaceHook swtCanvasUpStreamHook = new UpstreamSurfaceHook() { @Override - public final void create(ProxySurface s) { /* nop */ } + public final void create(final ProxySurface s) { /* nop */ } @Override - public final void destroy(ProxySurface s) { /* nop */ } + public final void destroy(final ProxySurface s) { /* nop */ } @Override - public final int getWidth(ProxySurface s) { + public final int getSurfaceWidth(final ProxySurface s) { return clientArea.width; } @Override - public final int getHeight(ProxySurface s) { + public final int getSurfaceHeight(final ProxySurface s) { return clientArea.height; } @@ -342,116 +433,301 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { public String toString() { return "SWTCanvasUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+", "+clientArea.width+"x"+clientArea.height+"]"; } + + /** + * {@inheritDoc} + * <p> + * Returns <code>null</code>. + * </p> + */ + @Override + public final NativeSurface getUpstreamSurface() { + return null; + } }; protected final void updateSizeCheck() { final Rectangle oClientArea = clientArea; final Rectangle nClientArea = GLCanvas.this.getClientArea(); - if ( nClientArea != null && + if ( nClientArea != null && ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) ) { clientArea = nClientArea; // write back new value - - GLDrawableImpl _drawable = drawable; - if( null != _drawable ) { - if(DEBUG) { - System.err.println("GLCanvas.sizeChanged: ("+Thread.currentThread().getName()+"): "+nClientArea.width+"x"+nClientArea.height+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle())); - } + + final GLDrawableImpl _drawable = drawable; + final boolean drawableOK = null != _drawable && _drawable.isRealized(); + if(DEBUG) { + final long dh = drawableOK ? _drawable.getHandle() : 0; + System.err.println(getThreadName()+": GLCanvas.sizeChanged: ("+Thread.currentThread().getName()+"): "+nClientArea.x+"/"+nClientArea.y+" "+nClientArea.width+"x"+nClientArea.height+" - drawableHandle "+toHexString(dh)); + } + if( drawableOK ) { if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { final RecursiveLock _lock = lock; _lock.lock(); try { final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, nClientArea.width, nClientArea.height); if(_drawable != _drawableNew) { - // write back + // write back drawable = _drawableNew; } } finally { _lock.unlock(); } - sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock } - } + } + if(0 != x11Window) { + SWTAccessor.resizeX11Window(screen.getDevice(), clientArea, x11Window); + } else if(0 != gdkWindow) { + SWTAccessor.resizeGDKWindow(clientArea, gdkWindow); + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock } } - - @Override - public void display() { - if( null != drawable || validateDrawableAndContext() ) { - runInGLThread(makeCurrentAndDisplayOnEDTAction); - } + + private boolean isValidAndVisibleOnEDTActionResult; + private final Runnable isValidAndVisibleOnEDTAction = new Runnable() { + @Override + public void run() { + isValidAndVisibleOnEDTActionResult = !GLCanvas.this.isDisposed() && GLCanvas.this.isVisible(); + } }; + + private final boolean isValidAndVisibleOnEDT() { + synchronized(isValidAndVisibleOnEDTAction) { + runOnEDTIfAvail(true, isValidAndVisibleOnEDTAction); + return isValidAndVisibleOnEDTActionResult; + } } - - /** assumes drawable == null ! */ - protected final boolean validateDrawableAndContext() { - if( GLCanvas.this.isDisposed() ) { + /** assumes drawable == null (implying !drawable.isRealized()) ! Checks of !isDispose() and isVisible() */ + protected final boolean validateDrawableAndContextWithCheck() { + if( !isValidAndVisibleOnEDT() ) { return false; } - final Rectangle nClientArea = clientArea; - if(0 == nClientArea.width * nClientArea.height) { - return false; - } - + return validateDrawableAndContextPostCheck(); + } + + private final boolean isDrawableAndContextValid() { + // drawable != null implies drawable.isRealized()==true + return null != drawable && null != context; + } + + /** assumes drawable == null (implying !drawable.isRealized()) || context == null ! No check of !isDispose() and isVisible() */ + private final boolean validateDrawableAndContextPostCheck() { + boolean res; final RecursiveLock _lock = lock; _lock.lock(); try { - final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); - - /* Native handle for the control, used to associate with GLContext */ - final long nativeWindowHandle = SWTAccessor.getWindowHandle(this); - - /* Create a NativeWindow proxy for the SWT canvas */ - ProxySurface proxySurface = null; - try { - proxySurface = glFactory.createProxySurface(device, 0 /* screenIdx */, nativeWindowHandle, - capsRequested, capsChooser, swtCanvasUpStreamHook); - } catch (GLException gle) { - // not ready yet .. - if(DEBUG) { System.err.println(gle.getMessage()); } + if(null == drawable) { + // 'displayable' (isValidAndVisibleOnEDT()) must have been checked upfront if appropriate! + createDrawableImpl(); // checks clientArea size (i.e. drawable size) and perf. realization + } + final GLDrawable _drawable = drawable; + if ( null != _drawable ) { + // drawable realization goes in-hand w/ it's construction + if( null == context ) { + // re-try context creation + res = createContextImpl(_drawable); // pending creation. + } else { + res = true; + } + if(res) { + sendReshape = true; + } + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": null drawable"); + } + res = false; } - - if(null != proxySurface) { - /* Associate a GL surface with the proxy */ - drawable = (GLDrawableImpl) glFactory.createGLDrawable(proxySurface); - drawable.setRealized(true); - - context = (GLContextImpl) drawable.createContext(shareWith); + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": "+res+", drawable-realized "+drawable.isRealized()+", has context "+(null!=context)); } } finally { _lock.unlock(); } - final boolean res = null != drawable; - if(DEBUG && res) { - System.err.println("SWT GLCanvas realized! "+this+", "+drawable); - Thread.dumpStack(); - } return res; } - + private final void createDrawableImpl() { + final Rectangle nClientArea = clientArea; + if(0 >= nClientArea.width || 0 >= nClientArea.height) { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": drawable could not be created: size < 0x0"); + } + return; // early out + } + final AbstractGraphicsDevice device = screen.getDevice(); + device.open(); + + final long nativeWindowHandle; + if( useX11GTK ) { + final GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(device, capsRequested); + final AbstractGraphicsConfiguration cfg = factory.chooseGraphicsConfiguration( + capsRequested, capsRequested, capsChooser, screen, VisualIDHolder.VID_UNDEFINED); + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.X11 "+toHexString(hashCode())+": factory: "+factory+", chosen config: "+cfg); + } + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + final int visualID = cfg.getVisualID(VIDType.NATIVE); + if( VisualIDHolder.VID_UNDEFINED != visualID ) { + // gdkWindow = SWTAccessor.createCompatibleGDKChildWindow(this, visualID, clientArea.width, clientArea.height); + // nativeWindowHandle = SWTAccessor.gdk_window_get_xwindow(gdkWindow); + x11Window = SWTAccessor.createCompatibleX11ChildWindow(screen, this, visualID, clientArea.width, clientArea.height); + nativeWindowHandle = x11Window; + } else { + throw new GLException("Could not choose valid visualID: "+toHexString(visualID)+", "+this); + } + } else { + nativeWindowHandle = SWTAccessor.getWindowHandle(this); + } + final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(capsRequested.getGLProfile()); + + // Create a NativeWindow proxy for the SWT canvas + final ProxySurface proxySurface = glFactory.createProxySurface(device, screen.getIndex(), nativeWindowHandle, + capsRequested, capsChooser, swtCanvasUpStreamHook); + // Associate a GL surface with the proxy + final GLDrawableImpl _drawable = (GLDrawableImpl) glFactory.createGLDrawable(proxySurface); + _drawable.setRealized(true); + if(!_drawable.isRealized()) { + // oops + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": Drawable could not be realized: "+_drawable); + } + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Drawable created and realized"); + } + drawable = _drawable; + } + } + private boolean createContextImpl(final GLDrawable drawable) { + final GLContext[] shareWith = { null }; + if( !helper.isSharedGLContextPending(shareWith) ) { + context = (GLContextImpl) drawable.createContext(shareWith[0]); + context.setContextCreationFlags(additionalCtxCreationFlags); + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Context created: has shared "+(null != shareWith[0])); + } + return true; + } else { + if(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate "+toHexString(hashCode())+": Context !created: pending share"); + } + return false; + } + } + + @Override + public void update() { + // don't paint background etc .. nop avoids flickering + // super.update(); + } + + /** + @Override + public boolean forceFocus() { + final boolean r = super.forceFocus(); + if(r && 0 != gdkWindow) { + SWTGTKUtil.focusGDKWindow(gdkWindow); + } + return r; + } */ + + @Override + public void dispose() { + runInGLThread(disposeOnEDTGLAction); + super.dispose(); + } + + private final void displayIfNoAnimatorNoCheck() { + if ( !helper.isAnimatorAnimatingOnOtherThread() ) { + if( isDrawableAndContextValid() || validateDrawableAndContextPostCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + } + + // + // GL[Auto]Drawable + // + + @Override + public void display() { + if( isDrawableAndContextValid() || validateDrawableAndContextWithCheck() ) { + runInGLThread(makeCurrentAndDisplayOnGLAction); + } + } + @Override public final Object getUpstreamWidget() { return this; } - + @Override - public int getWidth() { + public final RecursiveLock getUpstreamLock() { return lock; } + + @Override + public int getSurfaceWidth() { return clientArea.width; } @Override - public int getHeight() { + public int getSurfaceHeight() { return clientArea.height; } @Override - public void addGLEventListener(final GLEventListener arg0) { - helper.addGLEventListener(arg0); + public boolean isGLOriented() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isGLOriented() : true; + } + + @Override + public void addGLEventListener(final GLEventListener listener) { + helper.addGLEventListener(listener); + } + + @Override + public void addGLEventListener(final int idx, final GLEventListener listener) throws IndexOutOfBoundsException { + helper.addGLEventListener(idx, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(final int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); } @Override - public void addGLEventListener(final int arg0, final GLEventListener arg1) throws IndexOutOfBoundsException { - helper.addGLEventListener(arg0, arg1); + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(final GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(final GLEventListener listener, final boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); + } + + @Override + public GLEventListener disposeGLEventListener(final GLEventListener listener, final boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + runInGLThread(r); + return r.listener; + } + + @Override + public GLEventListener removeGLEventListener(final GLEventListener listener) { + return helper.removeGLEventListener(listener); } /** @@ -473,6 +749,16 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { } @Override + public final Thread setExclusiveContextThread(final Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); + } + + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override public boolean getAutoSwapBufferMode() { return helper.getAutoSwapBufferMode(); } @@ -481,10 +767,10 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { public final GLDrawable getDelegatedDrawable() { return drawable; } - + @Override public GLContext getContext() { - return null != drawable ? context : null; + return context; } @Override @@ -499,20 +785,20 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { } @Override - public boolean invoke(final boolean wait, final GLRunnable run) { - return helper.invoke(this, wait, run); + public boolean invoke(final boolean wait, final GLRunnable runnable) throws IllegalStateException { + return helper.invoke(this, wait, runnable); } @Override - public void removeGLEventListener(final GLEventListener arg0) { - helper.removeGLEventListener(arg0); + public boolean invoke(final boolean wait, final List<GLRunnable> runnables) throws IllegalStateException { + return helper.invoke(this, wait, runnables); } @Override - public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException { - return helper.removeGLEventListener(index); + public void flushGLRunnables() { + helper.flushGLRunnables(); } - + @Override public void setAnimator(final GLAnimatorControl arg0) throws GLException { helper.setAnimator(arg0); @@ -524,16 +810,13 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { } @Override - public GLContext setContext(GLContext newCtx) { + public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { final RecursiveLock _lock = lock; _lock.lock(); - try { + try { final GLContext oldCtx = context; - final boolean newCtxCurrent = GLDrawableHelper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags); + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); context=(GLContextImpl)newCtx; - if(newCtxCurrent) { - context.makeCurrent(); - } return oldCtx; } finally { _lock.unlock(); @@ -577,18 +860,14 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - final GLDrawable _drawable = drawable; + final GLDrawable _drawable = drawable; return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getChosenGLCapabilities() : null; } - /** - * Accessor for the GLCapabilities that were requested (via the constructor parameter). - * - * @return Non-null GLCapabilities. - */ + @Override public GLCapabilitiesImmutable getRequestedGLCapabilities() { - final GLDrawable _drawable = drawable; - return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities() : null; + final GLDrawable _drawable = drawable; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getRequestedGLCapabilities() : null; } @Override @@ -604,7 +883,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public long getHandle() { - final GLDrawable _drawable = drawable; + final GLDrawable _drawable = drawable; return (_drawable != null) ? _drawable.getHandle() : 0; } @@ -627,19 +906,17 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { @Override public void swapBuffers() throws GLException { - runInGLThread(makeCurrentAndSwapBuffersOnEDTAction); - } - - @Override - public void update() { - // don't paint background etc .. nop avoids flickering + runInGLThread(swapBuffersOnGLAction); } + /** + * {@inheritDoc} + * <p> + * Implementation always supports multithreading, hence method always returns <code>true</code>. + * </p> + */ @Override - public void dispose() { - runInGLThread(disposeOnEDTGLAction); - super.dispose(); - } + public final boolean isThreadGLCapable() { return true; } /** * Runs the specified action in an SWT compatible thread, which is: @@ -647,22 +924,64 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * <li>Mac OSX * <ul> * <!--li>AWT EDT: In case AWT is available, the AWT EDT is the OSX UI main thread</li--> - * <li><i>Main Thread</i>: Run on OSX UI main thread.</li> + * <!--li><i>Main Thread</i>: Run on OSX UI main thread.</li--> + * <li>Current thread</li> * </ul></li> * <li>Linux, Windows, .. * <ul> - * <li>Use {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}</li> - * </ul></li> + * <!--li>Use {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}</li--> + * <li>Current thread</li> + * </ul></li> * </ul> + * The current thread seems to be valid for all platforms, + * since no SWT lifecycle tasks are being performed w/ this call. + * Only GL task, which are independent from the SWT threading model. + * * @see Platform#AWT_AVAILABLE * @see Platform#getOSType() */ - private static void runInGLThread(final Runnable action) { + private void runInGLThread(final Runnable action) { + /** if(Platform.OSType.MACOS == Platform.OS_TYPE) { SWTAccessor.invoke(true, action); } else { Threading.invokeOnOpenGLThread(true, action); - } + } */ + /** + if( !isDisposed() ) { + final Display d = getDisplay(); + if( d.getThread() == Thread.currentThread() ) { + action.run(); + } else { + d.syncExec(action); + } + } */ + action.run(); + } + + private void runOnEDTIfAvail(final boolean wait, final Runnable action) { + final Display d = isDisposed() ? null : getDisplay(); + if( null == d || d.isDisposed() || d.getThread() == Thread.currentThread() ) { + action.run(); + } else if(wait) { + d.syncExec(action); + } else { + d.asyncExec(action); + } + } + + @Override + public String toString() { + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -1; + + return "SWT-GLCanvas[Realized "+isRealized()+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ + ",\n\tFactory "+getFactory()+ + ",\n\thandle "+toHexString(getHandle())+ + ",\n\tDrawable size "+dw+"x"+dh+ + ",\n\tSWT size "+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"; } public static void main(final String[] args) { @@ -679,12 +998,12 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { shell.setSize(128,128); shell.setLayout(new FillLayout()); - final GLCanvas canvas = new GLCanvas(shell, 0, caps, null, null); + final GLCanvas canvas = new GLCanvas(shell, 0, caps, null); canvas.addGLEventListener(new GLEventListener() { @Override public void init(final GLAutoDrawable drawable) { - GL gl = drawable.getGL(); + final GL gl = drawable.getGL(); System.err.println(JoglVersion.getGLInfo(gl, null)); } @Override diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index 26d299663..62df3faca 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -41,12 +41,14 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import javax.swing.JComponent; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; - import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -54,15 +56,16 @@ import javax.media.opengl.GLAutoDrawable; class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { // For efficient rendering of Swing components, in particular when // they overlap one another - private List<JComponent> lightweights = new ArrayList<JComponent>(); - private Map<RepaintManager,RepaintManager> repaintManagers = new IdentityHashMap<RepaintManager,RepaintManager>(); - private Map<JComponent,Rectangle> dirtyRegions = new IdentityHashMap<JComponent,Rectangle>(); + private final List<JComponent> lightweights = new ArrayList<JComponent>(); + private final Map<RepaintManager,RepaintManager> repaintManagers = new IdentityHashMap<RepaintManager,RepaintManager>(); + private final Map<JComponent,Rectangle> dirtyRegions = new IdentityHashMap<JComponent,Rectangle>(); - public void display(ArrayList<GLAutoDrawable> drawables, - boolean ignoreExceptions, - boolean printExceptions) { + @Override + public void display(final ArrayList<GLAutoDrawable> drawables, + final boolean ignoreExceptions, + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { - GLAutoDrawable drawable = drawables.get(i); + final GLAutoDrawable drawable = drawables.get(i); if (drawable instanceof JComponent) { // Lightweight components need a more efficient drawing // scheme than simply forcing repainting of each one in @@ -72,13 +75,13 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } else { try { drawable.display(); - } catch (RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } @@ -87,7 +90,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { if (lightweights.size() > 0) { try { SwingUtilities.invokeAndWait(drawWithRepaintManagerRunnable); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } lightweights.clear(); @@ -96,9 +99,10 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { // Uses RepaintManager APIs to implement more efficient redrawing of // the Swing widgets we're animating - private Runnable drawWithRepaintManagerRunnable = new Runnable() { + private final Runnable drawWithRepaintManagerRunnable = new Runnable() { + @Override public void run() { - for (Iterator<JComponent> iter = lightweights.iterator(); iter.hasNext(); ) { + for (final Iterator<JComponent> iter = lightweights.iterator(); iter.hasNext(); ) { JComponent comp = iter.next(); RepaintManager rm = RepaintManager.currentManager(comp); rm.markCompletelyDirty(comp); @@ -115,13 +119,13 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { // Walk up the hierarchy trying to find a non-optimizable // ancestor - Rectangle visible = comp.getVisibleRect(); + final Rectangle visible = comp.getVisibleRect(); int x = visible.x; int y = visible.y; while (comp != null) { x += comp.getX(); y += comp.getY(); - Component c = comp.getParent(); + final Component c = comp.getParent(); if ((c == null) || (!(c instanceof JComponent))) { comp = null; } else { @@ -130,7 +134,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { rm = RepaintManager.currentManager(comp); repaintManagers.put(rm, rm); // Need to dirty this region - Rectangle dirty = (Rectangle) dirtyRegions.get(comp); + Rectangle dirty = dirtyRegions.get(comp); if (dirty == null) { dirty = new Rectangle(x, y, visible.width, visible.height); dirtyRegions.put(comp, dirty); @@ -148,15 +152,15 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } // Dirty any needed regions on non-optimizable components - for (Iterator<JComponent> iter = dirtyRegions.keySet().iterator(); iter.hasNext(); ) { - JComponent comp = iter.next(); - Rectangle rect = dirtyRegions.get(comp); - RepaintManager rm = RepaintManager.currentManager(comp); + for (final Iterator<JComponent> iter = dirtyRegions.keySet().iterator(); iter.hasNext(); ) { + final JComponent comp = iter.next(); + final Rectangle rect = dirtyRegions.get(comp); + final RepaintManager rm = RepaintManager.currentManager(comp); rm.addDirtyRegion(comp, rect.x, rect.y, rect.width, rect.height); } // Draw all dirty regions - for (Iterator<RepaintManager> iter = repaintManagers.keySet().iterator(); iter.hasNext(); ) { + for (final Iterator<RepaintManager> iter = repaintManagers.keySet().iterator(); iter.hasNext(); ) { iter.next().paintDirtyRegions(); } dirtyRegions.clear(); @@ -164,7 +168,8 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } }; - public boolean blockUntilDone(Thread thread) { - return ((Thread.currentThread() != thread) && !EventQueue.isDispatchThread()); + @Override + public boolean blockUntilDone(final Thread thread) { + return Thread.currentThread() != thread && !EventQueue.isDispatchThread(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index 20ba27c16..b38a42ee3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,7 +41,7 @@ package com.jogamp.opengl.util; import javax.media.opengl.GLAutoDrawable; - +import javax.media.opengl.GLException; /** <P> An Animator can be attached to one or more {@link GLAutoDrawable}s to drive their display() methods in a loop. </P> @@ -56,19 +56,18 @@ import javax.media.opengl.GLAutoDrawable; * so it is able to keep an application from terminating.<br> * Call {@link #stop() } to terminate the animation and it's execution thread. * </p> -*/ - + */ public class Animator extends AnimatorBase { - /** timeout in milliseconds, 15 frames @ 60Hz = 240ms, limiting {@link #finishLifecycleAction(Condition)} */ - private static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 15*16; - - protected ThreadGroup threadGroup; + private ThreadGroup threadGroup; private Runnable runnable; private boolean runAsFastAsPossible; - protected boolean isAnimating; - protected boolean pauseIssued; - protected volatile boolean stopIssued; + boolean isAnimating; + volatile boolean pauseIssued; + volatile boolean stopIssued; + /** + * Creates a new, empty Animator. + */ public Animator() { super(); if(DEBUG) { @@ -76,28 +75,42 @@ public class Animator extends AnimatorBase { } } - public Animator(ThreadGroup tg) { + /** + * Creates a new Animator w/ an associated ThreadGroup. + */ + public Animator(final ThreadGroup tg) { super(); - threadGroup = tg; - + setThreadGroup(tg); if(DEBUG) { System.err.println("Animator created, ThreadGroup: "+threadGroup); } } - /** Creates a new Animator for a particular drawable. */ - public Animator(GLAutoDrawable drawable) { + /** + * Creates a new Animator for a particular drawable. + */ + public Animator(final GLAutoDrawable drawable) { super(); add(drawable); + if(DEBUG) { + System.err.println("Animator created, w/ "+drawable); + } } - /** Creates a new Animator for a particular drawable. */ - public Animator(ThreadGroup tg, GLAutoDrawable drawable) { - this(tg); + /** + * Creates a new Animator w/ an associated ThreadGroup for a particular drawable. + */ + public Animator(final ThreadGroup tg, final GLAutoDrawable drawable) { + super(); + setThreadGroup(tg); add(drawable); + if(DEBUG) { + System.err.println("Animator created, ThreadGroup: "+threadGroup+" and "+drawable); + } } - protected String getBaseName(String prefix) { + @Override + protected final String getBaseName(final String prefix) { return prefix + "Animator" ; } @@ -107,252 +120,239 @@ public class Animator extends AnimatorBase { * animation loop which prevents the CPU from getting swamped. * This method may not have an effect on subclasses. */ - public final void setRunAsFastAsPossible(boolean runFast) { - stateSync.lock(); - try { - runAsFastAsPossible = runFast; - } finally { - stateSync.unlock(); - } - } - - private final void setIsAnimatingSynced(boolean v) { - stateSync.lock(); - try { - isAnimating = v; - } finally { - stateSync.unlock(); - } + public final synchronized void setRunAsFastAsPossible(final boolean runFast) { + runAsFastAsPossible = runFast; } class MainLoop implements Runnable { + @Override public String toString() { - return "[started "+isStartedImpl()+", animating "+isAnimatingImpl()+", paused "+isPausedImpl()+", drawable "+drawables.size()+"]"; + return "[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; } + @Override public void run() { + ThreadDeath caughtThreadDeath = null; + UncaughtAnimatorException caughtException = null; + try { synchronized (Animator.this) { if(DEBUG) { - System.err.println("Animator start:" + Thread.currentThread() + ": " + toString()); + System.err.println("Animator start on " + getThreadName() + ": " + toString()); } fpsCounter.resetFPSCounter(); animThread = Thread.currentThread(); - setIsAnimatingSynced(false); // barrier - Animator.this.notifyAll(); + isAnimating = false; + // 'waitForStartedCondition' wake-up is handled below! } while (!stopIssued) { synchronized (Animator.this) { - // Don't consume CPU unless there is work to be done and not paused - while (!stopIssued && (pauseIssued || drawablesEmpty)) { - boolean wasPaused = pauseIssued; + // Pause; Also don't consume CPU unless there is work to be done and not paused + boolean ectCleared = false; + while ( !stopIssued && ( pauseIssued || drawablesEmpty ) ) { + if( drawablesEmpty ) { + pauseIssued = true; + } + final boolean wasPaused = pauseIssued; if (DEBUG) { - System.err.println("Animator pause:" + Thread.currentThread() + ": " + toString()); + System.err.println("Animator pause on " + animThread.getName() + ": " + toString()); + } + if ( exclusiveContext && !drawablesEmpty && !ectCleared ) { + ectCleared = true; + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end pause loop + } } - setIsAnimatingSynced(false); // barrier + isAnimating = false; Animator.this.notifyAll(); try { Animator.this.wait(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { } - if (wasPaused) { // resume from pause -> reset counter fpsCounter.resetFPSCounter(); if (DEBUG) { - System.err.println("Animator resume:" + Thread.currentThread() + ": " + toString()); + System.err.println("Animator resume on " + animThread.getName() + ": " + toString()); } } } if (!stopIssued && !isAnimating) { - // resume from pause or drawablesEmpty, + // Wakes up 'waitForStartedCondition' sync + // - and - + // Resume from pause or drawablesEmpty, // implies !pauseIssued and !drawablesEmpty - setIsAnimatingSynced(true); + isAnimating = true; + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context Animator.this.notifyAll(); } } // sync Animator.this - if (!stopIssued) { - display(); + if ( !pauseIssued && !stopIssued ) { + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end animation loop + } + if ( !runAsFastAsPossible ) { + // Avoid swamping the CPU + Thread.yield(); + } } - if (!stopIssued && !runAsFastAsPossible) { - // Avoid swamping the CPU - Thread.yield(); + } + } catch(final ThreadDeath td) { + if(DEBUG) { + System.err.println("Animator caught: "+td.getClass().getName()+": "+td.getMessage()); + td.printStackTrace(); + } + caughtThreadDeath = td; + } + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("Animator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); } } - } finally { - synchronized (Animator.this) { - if(DEBUG) { - System.err.println("Animator stop " + Thread.currentThread() + ": " + toString()); + } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; + synchronized (Animator.this) { + if(DEBUG) { + System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); } - stopIssued = false; - pauseIssued = false; - animThread = null; - setIsAnimatingSynced(false); // barrier - Animator.this.notifyAll(); } + stopIssued = false; + pauseIssued = false; + isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; + Animator.this.notifyAll(); + } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } + if( null != caughtThreadDeath ) { + throw caughtThreadDeath; } } } - private final boolean isStartedImpl() { - return animThread != null ; - } - public final boolean isStarted() { - stateSync.lock(); - try { - return animThread != null ; - } finally { - stateSync.unlock(); - } - } - - private final boolean isAnimatingImpl() { + @Override + public final synchronized boolean isAnimating() { return animThread != null && isAnimating ; } - public final boolean isAnimating() { - stateSync.lock(); - try { - return animThread != null && isAnimating ; - } finally { - stateSync.unlock(); - } - } - private final boolean isPausedImpl() { + @Override + public final synchronized boolean isPaused() { return animThread != null && pauseIssued ; } - public final boolean isPaused() { - stateSync.lock(); - try { - return animThread != null && pauseIssued ; - } finally { - stateSync.unlock(); - } - } - interface Condition { - /** - * @return true if branching (cont waiting, action), otherwise false - */ - boolean result(); - } - - private synchronized void finishLifecycleAction(Condition condition) { - // It's hard to tell whether the thread which changes the lifecycle has - // dependencies on the Animator's internal thread. Currently we - // use a couple of heuristics to determine whether we should do - // the blocking wait(). - final boolean blocking = impl.blockUntilDone(animThread); - long remaining = blocking ? TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION : 0; - while (remaining>0 && condition.result()) { - long td = System.currentTimeMillis(); - try { - wait(remaining); - } catch (InterruptedException ie) { } - remaining -= (System.currentTimeMillis() - td) ; - } - if(DEBUG) { - if(remaining<0) { - System.err.println("finishLifecycleAction(" + condition.getClass().getName() + "): ++++++ timeout reached ++++++ " + Thread.currentThread().getName()); - } - System.err.println("finishLifecycleAction(" + condition.getClass().getName() + "): finished "+ - "- blocking "+blocking+ - ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION + - ", started: " + isStartedImpl() +", animating: " + isAnimatingImpl() + - ", paused: " + isPausedImpl() + ", drawables " + drawables.size() + " - " + Thread.currentThread().getName()); + /** + * Set a {@link ThreadGroup} for the {@link #getThread() animation thread}. + * + * @param tg the {@link ThreadGroup} + * @throws GLException if the animator has already been started + */ + public final synchronized void setThreadGroup(final ThreadGroup tg) throws GLException { + if ( isStarted() ) { + throw new GLException("Animator already started."); } + threadGroup = tg; } - public synchronized boolean start() { - if ( isStartedImpl() ) { + @Override + public final synchronized boolean start() { + if ( isStarted() ) { return false; } if (runnable == null) { runnable = new MainLoop(); } fpsCounter.resetFPSCounter(); - String threadName = Thread.currentThread().getName()+"-"+baseName; + final String threadName = getThreadName()+"-"+baseName; Thread thread; if(null==threadGroup) { thread = new Thread(runnable, threadName); } else { thread = new Thread(threadGroup, runnable, threadName); } - thread.setDaemon(false); // force to be non daemon, regardless of parent thread + thread.setDaemon(false); // force to be non daemon, regardless of parent thread if(DEBUG) { final Thread ct = Thread.currentThread(); System.err.println("Animator "+ct.getName()+"[daemon "+ct.isDaemon()+"]: starting "+thread.getName()+"[daemon "+thread.isDaemon()+"]"); } thread.start(); - finishLifecycleAction(waitForStartedCondition); - return true; - } - - private class WaitForStartedCondition implements Condition { - public boolean result() { - return !isStartedImpl() || (!drawablesEmpty && !isAnimating) ; - } + return finishLifecycleAction(waitForStartedCondition, 0); } - Condition waitForStartedCondition = new WaitForStartedCondition(); + private final Condition waitForStartedCondition = new Condition() { + @Override + public boolean eval() { + return !isStarted() || (!drawablesEmpty && !isAnimating) ; + } }; - public synchronized boolean stop() { - if ( !isStartedImpl() ) { + @Override + public final synchronized boolean stop() { + if ( !isStarted() ) { return false; } stopIssued = true; - notifyAll(); - finishLifecycleAction(waitForStoppedCondition); - return true; + return finishLifecycleAction(waitForStoppedCondition, 0); } - private class WaitForStoppedCondition implements Condition { - public boolean result() { - return isStartedImpl(); - } - } - Condition waitForStoppedCondition = new WaitForStoppedCondition(); + private final Condition waitForStoppedCondition = new Condition() { + @Override + public boolean eval() { + return isStarted(); + } }; - public synchronized boolean pause() { - if ( !isStartedImpl() || pauseIssued ) { + @Override + public final synchronized boolean pause() { + if ( !isStarted() || pauseIssued ) { return false; } - stateSync.lock(); - try { - pauseIssued = true; - } finally { - stateSync.unlock(); - } - notifyAll(); - finishLifecycleAction(waitForPausedCondition); - return true; + pauseIssued = true; + return finishLifecycleAction(waitForPausedCondition, 0); } - private class WaitForPausedCondition implements Condition { - public boolean result() { + private final Condition waitForPausedCondition = new Condition() { + @Override + public boolean eval() { // end waiting if stopped as well - return isAnimating && isStartedImpl(); - } - } - Condition waitForPausedCondition = new WaitForPausedCondition(); + return isStarted() && isAnimating; + } }; - public synchronized boolean resume() { - if ( !isStartedImpl() || !pauseIssued ) { + @Override + public final synchronized boolean resume() { + if ( !isStarted() || !pauseIssued ) { return false; } - stateSync.lock(); - try { - pauseIssued = false; - } finally { - stateSync.unlock(); - } - notifyAll(); - finishLifecycleAction(waitForResumeCondition); - return true; + pauseIssued = false; + return finishLifecycleAction(waitForResumeCondition, 0); } - private class WaitForResumeCondition implements Condition { - public boolean result() { + private final Condition waitForResumeCondition = new Condition() { + @Override + public boolean eval() { // end waiting if stopped as well - return !drawablesEmpty && !isAnimating && isStartedImpl(); - } - } - Condition waitForResumeCondition = new WaitForResumeCondition(); + return isStarted() && ( !drawablesEmpty && !isAnimating || drawablesEmpty && !pauseIssued ) ; + } }; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index 46fc1d991..5f2db20bd 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -28,8 +28,6 @@ package com.jogamp.opengl.util; -import com.jogamp.common.util.locks.LockFactory; -import com.jogamp.common.util.locks.RecursiveLock; import jogamp.opengl.Debug; import jogamp.opengl.FPSCounterImpl; @@ -38,6 +36,7 @@ import java.util.ArrayList; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; /** @@ -52,150 +51,459 @@ import javax.media.opengl.GLProfile; public abstract class AnimatorBase implements GLAnimatorControl { protected static final boolean DEBUG = Debug.debug("Animator"); - private static int animatorCount = 0; + /** A 1s timeout while waiting for a native action response, limiting {@link #finishLifecycleAction(Condition, long)} */ + protected static final long TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 1000; - public interface AnimatorImpl { - void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions); - boolean blockUntilDone(Thread thread); + protected static final long POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION = 32; // 2 frames @ 60Hz + + /** + * If present in <code>modeBits</code> field and + * {@link GLProfile#isAWTAvailable() AWT is available}, + * implementation is aware of the AWT EDT, otherwise not. + * <p> + * This is the <i>default</i>. + * </p> + * @see #setModeBits(boolean, int) + */ + public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0; + + + @SuppressWarnings("serial") + public static class UncaughtAnimatorException extends RuntimeException { + final GLAutoDrawable drawable; + public UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause) { + super(cause); + this.drawable = drawable; + } + public final GLAutoDrawable getGLAutoDrawable() { return drawable; } } - protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>(); - protected boolean drawablesEmpty; + public static interface AnimatorImpl { + /** + * @param drawables + * @param ignoreExceptions + * @param printExceptions + * @throws UncaughtAnimatorException as caused by {@link GLAutoDrawable#display()} + */ + void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, final boolean printExceptions) throws UncaughtAnimatorException; + boolean blockUntilDone(final Thread thread); + } + + private static int seqInstanceNumber = 0; + + protected int modeBits; protected AnimatorImpl impl; protected String baseName; + + protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>(); + protected boolean drawablesEmpty; protected Thread animThread; protected boolean ignoreExceptions; protected boolean printExceptions; - protected FPSCounterImpl fpsCounter = new FPSCounterImpl(); - protected RecursiveLock stateSync = LockFactory.createRecursiveLock(); + protected boolean exclusiveContext; + protected Thread userExclusiveContextThread; + protected UncaughtExceptionHandler uncaughtExceptionHandler; + protected FPSCounterImpl fpsCounter = new FPSCounterImpl(); - /** Creates a new, empty Animator. */ - public AnimatorBase() { - if(GLProfile.isAWTAvailable()) { + private final static Class<?> awtAnimatorImplClazz; + static { + GLProfile.initSingleton(); + if( GLProfile.isAWTAvailable() ) { + Class<?> clazz; try { - impl = (AnimatorImpl) Class.forName("com.jogamp.opengl.util.AWTAnimatorImpl").newInstance(); - baseName = "AWTAnimator"; - } catch (Exception e) { e.printStackTrace(); } + clazz = Class.forName("com.jogamp.opengl.util.AWTAnimatorImpl"); + } catch (final Exception e) { + clazz = null; + } + awtAnimatorImplClazz = clazz; + } else { + awtAnimatorImplClazz = null; } - if(null==impl) { - impl = new DefaultAnimatorImpl(); - baseName = "Animator"; + } + + /** + * Creates a new, empty Animator instance + * while expecting an AWT rendering thread if AWT is available. + * + * @see GLProfile#isAWTAvailable() + */ + public AnimatorBase() { + modeBits = MODE_EXPECT_AWT_RENDERING_THREAD; // default! + drawablesEmpty = true; + } + + private static final boolean useAWTAnimatorImpl(final int modeBits) { + return 0 != ( MODE_EXPECT_AWT_RENDERING_THREAD & modeBits ) && null != awtAnimatorImplClazz; + } + + /** + * Initializes implementation details post setup, + * invoked at {@link #add(GLAutoDrawable)}, {@link #start()}, .. + * <p> + * Operation is a NOP if <code>force</code> is <code>false</code> + * and this instance is already initialized. + * </p> + * + * @throws GLException if Animator is {@link #isStarted()} + */ + protected final synchronized void initImpl(final boolean force) { + if( force || null == impl ) { + final String seqSuffix = String.format("#%02d", seqInstanceNumber++); + if( useAWTAnimatorImpl( modeBits ) ) { + try { + impl = (AnimatorImpl) awtAnimatorImplClazz.newInstance(); + baseName = getBaseName("AWT")+seqSuffix; + } catch (final Exception e) { e.printStackTrace(); } + } + if( null == impl ) { + impl = new DefaultAnimatorImpl(); + baseName = getBaseName("")+seqSuffix; + } + if(DEBUG) { + System.err.println("Animator.initImpl: baseName "+baseName+", implClazz "+impl.getClass().getName()+" - "+toString()+" - "+getThreadName()); + } + } + } + protected abstract String getBaseName(String prefix); + + /** + * Enables or disables the given <code>bitValues</code> + * in this Animators <code>modeBits</code>. + * @param enable + * @param bitValues + * + * @throws GLException if Animator is {@link #isStarted()} and {@link #MODE_EXPECT_AWT_RENDERING_THREAD} about to change + * @see AnimatorBase#MODE_EXPECT_AWT_RENDERING_THREAD + */ + public final synchronized void setModeBits(final boolean enable, final int bitValues) throws GLException { + final int _oldModeBits = modeBits; + if(enable) { + modeBits |= bitValues; + } else { + modeBits &= ~bitValues; } - synchronized (Animator.class) { - animatorCount++; - baseName = baseName.concat("-"+animatorCount); - drawablesEmpty = true; + if( useAWTAnimatorImpl( _oldModeBits ) != useAWTAnimatorImpl( modeBits ) ) { + if( isStarted() ) { + throw new GLException("Animator already started"); + } + initImpl(true); } } + public synchronized int getModeBits() { return modeBits; } - protected abstract String getBaseName(String prefix); - public synchronized void add(GLAutoDrawable drawable) { + @Override + public final synchronized void add(final GLAutoDrawable drawable) { if(DEBUG) { - System.err.println("Animator add: "+drawable.hashCode()+" - "+Thread.currentThread().getName()); + System.err.println("Animator add: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+getThreadName()); + } + if( drawables.contains(drawable) ) { + throw new IllegalArgumentException("Drawable already added to animator: "+this+", "+drawable); + } + initImpl(false); + pause(); + if( isStarted() ) { + drawable.setExclusiveContextThread( exclusiveContext ? getExclusiveContextThread() : null ); // if already running .. } - boolean paused = pause(); drawables.add(drawable); drawablesEmpty = drawables.size() == 0; drawable.setAnimator(this); - if(paused) { + if( isPaused() ) { // either paused by pause() above, or if previously drawablesEmpty==true resume(); } - if(impl.blockUntilDone(animThread)) { - while(isStarted() && !isPaused() && !isAnimating()) { - try { - wait(); - } catch (InterruptedException ie) { } - } + final Condition waitForAnimatingAndECTCondition = new Condition() { + @Override + public boolean eval() { + final Thread dect = drawable.getExclusiveContextThread(); + return isStarted() && !isPaused() && !isAnimating() && ( exclusiveContext && null == dect || !exclusiveContext && null != dect ); + } }; + final boolean res = finishLifecycleAction(waitForAnimatingAndECTCondition, 0); + if(DEBUG) { + System.err.println("Animator add: Wait for Animating/ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread()); } notifyAll(); } - public synchronized void remove(GLAutoDrawable drawable) { + @Override + public final synchronized void remove(final GLAutoDrawable drawable) { if(DEBUG) { - System.err.println("Animator remove: "+drawable.hashCode()+" - "+Thread.currentThread().getName() + ": "+toString()); + System.err.println("Animator remove: 0x"+Integer.toHexString(drawable.hashCode())+" - "+toString()+" - "+getThreadName()); + } + if( !drawables.contains(drawable) ) { + throw new IllegalArgumentException("Drawable not added to animator: "+this+", "+drawable); + } + + if( exclusiveContext && isAnimating() ) { + drawable.setExclusiveContextThread( null ); + final Condition waitForNullECTCondition = new Condition() { + @Override + public boolean eval() { + return null != drawable.getExclusiveContextThread(); + } }; + final boolean res = finishLifecycleAction(waitForNullECTCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); + if(DEBUG) { + System.err.println("Animator remove: Wait for Null-ECT OK: "+res+", "+toString()+", dect "+drawable.getExclusiveContextThread()); + } } - boolean paused = pause(); + final boolean paused = pause(); drawables.remove(drawable); drawablesEmpty = drawables.size() == 0; drawable.setAnimator(null); if(paused) { resume(); } - if(impl.blockUntilDone(animThread)) { - while(isStarted() && drawablesEmpty && isAnimating()) { - try { - wait(); - } catch (InterruptedException ie) { } - } + final boolean res = finishLifecycleAction(waitForNotAnimatingIfEmptyCondition, 0); + if(DEBUG) { + System.err.println("Animator remove: Wait for !Animating-if-empty OK: "+res+", "+toString()); } notifyAll(); } + private final Condition waitForNotAnimatingIfEmptyCondition = new Condition() { + @Override + public boolean eval() { + return isStarted() && drawablesEmpty && isAnimating(); + } }; + + + /** + * Dedicate all {@link GLAutoDrawable}'s context to the given exclusive context thread. + * <p> + * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}. + * </p> + * <p> + * If already started and disabling, method waits + * until change is propagated to all {@link GLAutoDrawable} if not + * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}. + * </p> + * <p> + * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation. + * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas. + * </p> + * + * @param enable + * @return previous value + * @see #setExclusiveContext(boolean) + * @see #getExclusiveContextThread() + * @see #isExclusiveContextEnabled() + */ + // @Override + public final synchronized Thread setExclusiveContext(final Thread t) { + final boolean enable = null != t; + final Thread old = userExclusiveContextThread; + if( enable && t != animThread ) { // disable: will be cleared at end after propagation && filter out own animThread usae + userExclusiveContextThread=t; + } + setExclusiveContext(enable); + return old; + } + + /** + * Dedicate all {@link GLAutoDrawable}'s context to this animator thread. + * <p> + * The given thread will be exclusive to all {@link GLAutoDrawable}'s context while {@link #isAnimating()}. + * </p> + * <p> + * If already started and disabling, method waits + * until change is propagated to all {@link GLAutoDrawable} if not + * called from the animator thread or {@link #getExclusiveContextThread() exclusive context thread}. + * </p> + * <p> + * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation. + * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas. + * </p> + * + * @param enable + * @return previous value + * @see #setExclusiveContext(Thread) + * @see #getExclusiveContextThread() + * @see #isExclusiveContextEnabled() + */ + // @Override + public final boolean setExclusiveContext(final boolean enable) { + final boolean propagateState; + final boolean oldExclusiveContext; + final Thread _exclusiveContextThread; + synchronized (AnimatorBase.this) { + propagateState = isStarted() && !drawablesEmpty; + _exclusiveContextThread = userExclusiveContextThread; + oldExclusiveContext = exclusiveContext; + exclusiveContext = enable; + if(DEBUG) { + System.err.println("AnimatorBase.setExclusiveContextThread: "+oldExclusiveContext+" -> "+exclusiveContext+", propagateState "+propagateState+", "+this); + } + } + final Thread dECT = enable ? ( null != _exclusiveContextThread ? _exclusiveContextThread : animThread ) : null ; + UncaughtAnimatorException displayCaught = null; + if( propagateState ) { + setDrawablesExclCtxState(enable); + if( !enable ) { + if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) { + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + } + } else { + final boolean resumed = isAnimating() ? false : resume(); + int counter = 10; + while( 0<counter && isAnimating() && !validateDrawablesExclCtxState(dECT) ) { + try { + Thread.sleep(20); + } catch (final InterruptedException e) { } + counter--; + } + if(resumed) { + pause(); + } + } + synchronized(AnimatorBase.this) { + userExclusiveContextThread=null; + } + } + } + if(DEBUG) { + System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this); + if( null != displayCaught ) { + System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage()); + displayCaught.printStackTrace(); + } + } + if( null != displayCaught ) { + throw displayCaught; + } + return oldExclusiveContext; + } + + /** + * Returns <code>true</code>, if the exclusive context thread is enabled, otherwise <code>false</code>. + * + * @see #setExclusiveContext(boolean) + * @see #setExclusiveContext(Thread) + */ + // @Override + public final synchronized boolean isExclusiveContextEnabled() { + return exclusiveContext; + } + + /** + * Returns the exclusive context thread if {@link #isExclusiveContextEnabled()} and {@link #isStarted()}, otherwise <code>null</code>. + * <p> + * If exclusive context is enabled via {@link #setExclusiveContext(boolean)} + * the {@link #getThread() animator thread} is returned if above conditions are met. + * </p> + * <p> + * If exclusive context is enabled via {@link #setExclusiveContext(Thread)} + * the user passed thread is returned if above conditions are met. + * </p> + * @see #setExclusiveContext(boolean) + * @see #setExclusiveContext(Thread) + */ + // @Override + public final synchronized Thread getExclusiveContextThread() { + return ( isStarted() && exclusiveContext ) ? ( null != userExclusiveContextThread ? userExclusiveContextThread : animThread ) : null ; + } + + /** + * Should be called at {@link #start()} and {@link #stop()} + * from within the animator thread. + * <p> + * At {@link #stop()} an additional {@link #display()} call shall be issued + * to allow propagation of releasing the exclusive thread. + * </p> + */ + protected final synchronized void setDrawablesExclCtxState(final boolean enable) { + if(DEBUG) { + System.err.println("AnimatorBase.setExclusiveContextImpl exlusive "+exclusiveContext+": Enable "+enable+" for "+this+" - "+Thread.currentThread()); + // Thread.dumpStack(); + } + final Thread ect = getExclusiveContextThread(); + for (int i=0; i<drawables.size(); i++) { + try { + drawables.get(i).setExclusiveContextThread( enable ? ect : null ); + } catch (final RuntimeException e) { + e.printStackTrace(); + } + } + } + protected final boolean validateDrawablesExclCtxState(final Thread expected) { + for (int i=0; i<drawables.size(); i++) { + if( expected != drawables.get(i).getExclusiveContextThread() ) { + return false; + } + } + return true; + } + + @Override + public final synchronized Thread getThread() { + return animThread; + } /** Called every frame to cause redrawing of all of the GLAutoDrawables this Animator manages. Subclasses should call this to get the most optimized painting behavior for the set of components this Animator manages, in particular when multiple lightweight widgets are continually being redrawn. */ - protected void display() { + protected final void display() throws UncaughtAnimatorException { impl.display(drawables, ignoreExceptions, printExceptions); fpsCounter.tickFPS(); } - public final void setUpdateFPSFrames(int frames, PrintStream out) { + @Override + public final void setUpdateFPSFrames(final int frames, final PrintStream out) { fpsCounter.setUpdateFPSFrames(frames, out); } - + + @Override public final void resetFPSCounter() { fpsCounter.resetFPSCounter(); } + @Override public final int getUpdateFPSFrames() { return fpsCounter.getUpdateFPSFrames(); } - + + @Override public final long getFPSStartTime() { return fpsCounter.getFPSStartTime(); } + @Override public final long getLastFPSUpdateTime() { return fpsCounter.getLastFPSUpdateTime(); } + @Override public final long getLastFPSPeriod() { return fpsCounter.getLastFPSPeriod(); } - + + @Override public final float getLastFPS() { return fpsCounter.getLastFPS(); } - + + @Override public final int getTotalFPSFrames() { return fpsCounter.getTotalFPSFrames(); } + @Override public final long getTotalFPSDuration() { return fpsCounter.getTotalFPSDuration(); } - + + @Override public final float getTotalFPS() { return fpsCounter.getTotalFPS(); - } - - public final Thread getThread() { - stateSync.lock(); - try { - return animThread; - } finally { - stateSync.unlock(); - } } /** Sets a flag causing this Animator to ignore exceptions produced while redrawing the drawables. By default this flag is set to false, causing any exception thrown to halt the Animator. */ - public void setIgnoreExceptions(boolean ignoreExceptions) { + public final void setIgnoreExceptions(final boolean ignoreExceptions) { this.ignoreExceptions = ignoreExceptions; } @@ -203,11 +511,137 @@ public abstract class AnimatorBase implements GLAnimatorControl { this Animator (see {@link #setIgnoreExceptions}), to print the exceptions' stack traces for diagnostic information. Defaults to false. */ - public void setPrintExceptions(boolean printExceptions) { + public final void setPrintExceptions(final boolean printExceptions) { this.printExceptions = printExceptions; } + @Override + public final UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + @Override + public final void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler) { + uncaughtExceptionHandler = handler; + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread, throws given exception if no handler has been installed. + * @return {@code true} if handled, otherwise {@code false}. In case of {@code false}, + * caller needs to propagate the exception. + */ + protected final synchronized boolean handleUncaughtException(final UncaughtAnimatorException ue) { + if( null != uncaughtExceptionHandler ) { + try { + uncaughtExceptionHandler.uncaughtException(this, ue.getGLAutoDrawable(), ue.getCause()); + } catch (final Throwable t) { /* ignore intentionally */ } + return true; + } else { + return false; + } + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread to flush all animator. + * <p> + * The animator instance shall not be locked when calling this method! + * </p> + */ + protected final void flushGLRunnables() { + for (int i=0; i<drawables.size(); i++) { + drawables.get(i).flushGLRunnables(); + } + } + + protected static interface Condition { + /** + * @return true if branching (continue waiting, action), otherwise false + */ + boolean eval(); + } + + /** + * @param waitCondition method will wait until TO is reached or {@link Condition#eval() waitCondition.eval()} returns <code>false</code>. + * @param pollPeriod if <code>0</code>, method will wait until TO is reached or being notified. + * if > <code>0</code>, method will wait for the given <code>pollPeriod</code> in milliseconds. + * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code> + * or if {@link AnimatorImpl#blockUntilDone(Thread) non-blocking}. Otherwise returns <code>false</code>. + */ + protected final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod) { + /** + * It's hard to tell whether the thread which changes the lifecycle has + * dependencies on the Animator's internal thread. Currently we + * use a couple of heuristics to determine whether we should do + * the blocking wait(). + */ + initImpl(false); + final boolean blocking; + long remaining; + boolean nok; + + if( impl.blockUntilDone(animThread) ) { + blocking = true; + remaining = TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION; + if( 0 >= pollPeriod ) { + pollPeriod = remaining; + } + nok = waitCondition.eval(); + while ( nok && remaining>0 ) { + final long t1 = System.currentTimeMillis(); + if( pollPeriod > remaining ) { pollPeriod = remaining; } + notifyAll(); + try { + wait(pollPeriod); + } catch (final InterruptedException ie) { } + remaining -= System.currentTimeMillis() - t1 ; + nok = waitCondition.eval(); + } + } else { + /** + * Even though we are not able to block until operation is completed at this point, + * best effort shall be made to preserve functionality. + * Here: Issue notifyAll() if waitCondition still holds and test again. + * + * Non blocking reason could be utilizing AWT Animator while operation is performed on AWT-EDT. + */ + blocking = false; + remaining = 0; + nok = waitCondition.eval(); + if( nok ) { + notifyAll(); + nok = waitCondition.eval(); + } + } + final boolean res = !nok || !blocking; + if(DEBUG || blocking && nok) { // Info only if DEBUG or ( blocking && not-ok ) ; !blocking possible if AWT + if( blocking && remaining<=0 && nok ) { + System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + getThreadName()); + } + System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): OK "+(!nok)+ + "- pollPeriod "+pollPeriod+", blocking "+blocking+" -> res "+res+ + ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION + + " - " + getThreadName()); + System.err.println(" - "+toString()); + if(nok) { + Thread.dumpStack(); + } + } + return res; + } + + @Override + public synchronized boolean isStarted() { + return animThread != null ; + } + + protected static String getThreadName() { return Thread.currentThread().getName(); } + + @Override public String toString() { - return getClass().getName()+"[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+", totals[dt "+getTotalFPSDuration()+", frames "+getTotalFPSFrames()+", fps "+getTotalFPS()+"]]"; + return getClass().getName()+"[started "+isStarted()+", animating "+isAnimating()+", paused "+isPaused()+", drawable "+drawables.size()+ + ", totals[dt "+getTotalFPSDuration()+", frames "+getTotalFPSFrames()+", fps "+getTotalFPS()+ + "], modeBits "+modeBits+", init'ed "+(null!=impl)+", animThread "+getThread()+", exclCtxThread "+exclusiveContext+"("+getExclusiveContextThread()+")]"; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java b/src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java new file mode 100644 index 000000000..86443087e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/CustomGLEventListener.java @@ -0,0 +1,63 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +/** + * Extended {@link GLEventListener} interface + * supporting more fine grained control over the implementation. + */ +public interface CustomGLEventListener extends GLEventListener { + /** + * {@link #display(GLAutoDrawable, int) display flag}: Repeat last produced image. + * <p> + * While a repeated frame shall produce the same artifacts as the last <code>display</code> call, + * e.g. not change animated objects, it shall reflect the {@link #setProjectionModelview(GLAutoDrawable, float[], float[]) current matrix}. + * </p> + */ + public static final int DISPLAY_REPEAT = 1 << 0; + + /** + * {@link #display(GLAutoDrawable, int) display flag}: Do not clear any target buffer, e.g. color-, depth- or stencil-buffers. + */ + public static final int DISPLAY_DONTCLEAR = 1 << 1; + + /** + * Extended {@link #display(GLAutoDrawable) display} method, + * allowing to pass a display flag, e.g. {@link #DISPLAY_REPEAT} or {@link #DISPLAY_DONTCLEAR}. + * <p> + * Method is usually called by a custom rendering loop, + * e.g. for manual stereo rendering or the like. + * </p> + * @param drawable + * @param flags + */ + public void display(final GLAutoDrawable drawable, final int flags); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java index 23b0845ee..6b1485a6a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -34,33 +34,38 @@ package com.jogamp.opengl.util; import java.util.ArrayList; + import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { - public void display(ArrayList<GLAutoDrawable> drawables, - boolean ignoreExceptions, - boolean printExceptions) { + @Override + public void display(final ArrayList<GLAutoDrawable> drawables, + final boolean ignoreExceptions, + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { - GLAutoDrawable drawable = drawables.get(i); + final GLAutoDrawable drawable = drawables.get(i); try { drawable.display(); - } catch (RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } } - public boolean blockUntilDone(Thread thread) { - return (Thread.currentThread() != thread); + @Override + public boolean blockUntilDone(final Thread thread) { + return Thread.currentThread() != thread; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index f7fc58160..54d40f285 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,36 +29,49 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package com.jogamp.opengl.util; -import java.util.*; -import javax.media.opengl.*; +import java.util.Timer; +import java.util.TimerTask; -/** An Animator subclass which attempts to achieve a target -frames-per-second rate to avoid using all CPU time. The target FPS -is only an estimate and is not guaranteed. */ +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLException; + +/** + * An Animator subclass which attempts to achieve a target + * frames-per-second rate to avoid using all CPU time. The target FPS + * is only an estimate and is not guaranteed. + * <p> + * The Animator execution thread does not run as a daemon thread, + * so it is able to keep an application from terminating.<br> + * Call {@link #stop() } to terminate the animation and it's execution thread. + * </p> + */ public class FPSAnimator extends AnimatorBase { private Timer timer = null; - private TimerTask task = null; + private MainTask task = null; private int fps; - private boolean scheduleAtFixedRate; - private volatile boolean shouldRun; + private final boolean scheduleAtFixedRate; + private boolean isAnimating; // MainTask feedback + private volatile boolean pauseIssued; // MainTask trigger + private volatile boolean stopIssued; // MainTask trigger - protected String getBaseName(String prefix) { + @Override + protected String getBaseName(final String prefix) { return "FPS" + prefix + "Animator" ; } /** Creates an FPSAnimator with a given target frames-per-second value. Equivalent to <code>FPSAnimator(null, fps)</code>. */ - public FPSAnimator(int fps) { + public FPSAnimator(final int fps) { this(null, fps); } @@ -66,21 +79,22 @@ public class FPSAnimator extends AnimatorBase { value and a flag indicating whether to use fixed-rate scheduling. Equivalent to <code>FPSAnimator(null, fps, scheduleAtFixedRate)</code>. */ - public FPSAnimator(int fps, boolean scheduleAtFixedRate) { + public FPSAnimator(final int fps, final boolean scheduleAtFixedRate) { this(null, fps, scheduleAtFixedRate); } /** Creates an FPSAnimator with a given target frames-per-second value and an initial drawable to animate. Equivalent to <code>FPSAnimator(null, fps, false)</code>. */ - public FPSAnimator(GLAutoDrawable drawable, int fps) { + public FPSAnimator(final GLAutoDrawable drawable, final int fps) { this(drawable, fps, false); } /** Creates an FPSAnimator with a given target frames-per-second value, an initial drawable to animate, and a flag indicating whether to use fixed-rate scheduling. */ - public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) { + public FPSAnimator(final GLAutoDrawable drawable, final int fps, final boolean scheduleAtFixedRate) { + super(); this.fps = fps; if (drawable != null) { add(drawable); @@ -88,131 +102,314 @@ public class FPSAnimator extends AnimatorBase { this.scheduleAtFixedRate = scheduleAtFixedRate; } - public final boolean isStarted() { - stateSync.lock(); - try { - return (timer != null); - } finally { - stateSync.unlock(); + /** + * @param fps + * @throws GLException if the animator has already been started + */ + public final void setFPS(final int fps) throws GLException { + if ( isStarted() ) { + throw new GLException("Animator already started."); } + this.fps = fps; } + public final int getFPS() { return fps; } + + class MainTask extends TimerTask { + private boolean justStarted; + private boolean alreadyStopped; + private boolean alreadyPaused; - public final boolean isAnimating() { - stateSync.lock(); - try { - return (timer != null) && (task != null); - } finally { - stateSync.unlock(); + public MainTask() { + } + + public void start(final Timer timer) { + fpsCounter.resetFPSCounter(); + pauseIssued = false; + stopIssued = false; + isAnimating = false; + + justStarted = true; + alreadyStopped = false; + alreadyPaused = false; + + final long period = 0 < fps ? (long) (1000.0f / fps) : 1; // 0 -> 1: IllegalArgumentException: Non-positive period + if (scheduleAtFixedRate) { + timer.scheduleAtFixedRate(this, 0, period); + } else { + timer.schedule(this, 0, period); + } } - } - public final boolean isPaused() { - stateSync.lock(); - try { - return (timer != null) && (task == null); - } finally { - stateSync.unlock(); + public boolean isActive() { return !alreadyStopped && !alreadyPaused; } + + @Override + public final String toString() { + return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" pauseIssued "+pauseIssued+", stopIssued "+stopIssued+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; } - } - private void startTask() { - if(null != task) { - return; - } - long delay = (long) (1000.0f / (float) fps); - task = new TimerTask() { - public void run() { - if(FPSAnimator.this.shouldRun) { - FPSAnimator.this.animThread = Thread.currentThread(); - // display impl. uses synchronized block on the animator instance - display(); + @Override + public void run() { + UncaughtAnimatorException caughtException = null; + + if( justStarted ) { + justStarted = false; + synchronized (FPSAnimator.this) { + animThread = Thread.currentThread(); + if(DEBUG) { + System.err.println("FPSAnimator start/resume:" + Thread.currentThread() + ": " + toString()); + } + isAnimating = true; + if( drawablesEmpty ) { + pauseIssued = true; // isAnimating:=false @ pause below + } else { + pauseIssued = false; + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context + } + FPSAnimator.this.notifyAll(); // Wakes up 'waitForStartedCondition' sync -and resume from pause or drawablesEmpty + if(DEBUG) { + System.err.println("FPSAnimator P1:" + Thread.currentThread() + ": " + toString()); + } } } - }; + if( !pauseIssued && !stopIssued ) { // RUN + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } + } else if( pauseIssued && !stopIssued ) { // PAUSE + if(DEBUG) { + System.err.println("FPSAnimator pausing: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); + } + this.cancel(); - fpsCounter.resetFPSCounter(); - shouldRun = true; + if( !alreadyPaused ) { // PAUSE + alreadyPaused = true; + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } + } + if( null == caughtException ) { + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + } + isAnimating = false; + FPSAnimator.this.notifyAll(); + } + } + } + } + if( stopIssued ) { // STOP incl. immediate exception handling of 'displayCaught' + if(DEBUG) { + System.err.println("FPSAnimator stopping: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); + } + this.cancel(); - if (scheduleAtFixedRate) { - timer.scheduleAtFixedRate(task, 0, delay); - } else { - timer.schedule(task, 0, delay); + if( !alreadyStopped ) { + alreadyStopped = true; + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("FPSAnimator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); + } + } + } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); + } + } + isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; + FPSAnimator.this.notifyAll(); + } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } + } + } } } + private final boolean isAnimatingImpl() { + return animThread != null && isAnimating ; + } + @Override + public final synchronized boolean isAnimating() { + return animThread != null && isAnimating ; + } + + @Override + public final synchronized boolean isPaused() { + return animThread != null && pauseIssued; + } - public synchronized boolean start() { - if (timer != null) { + static int timerNo = 0; + + @Override + public final synchronized boolean start() { + if ( null != timer || null != task || isStarted() ) { return false; } - stateSync.lock(); - try { - timer = new Timer(); - startTask(); - } finally { - stateSync.unlock(); + timer = new Timer( getThreadName()+"-"+baseName+"-Timer"+(timerNo++) ); + task = new MainTask(); + if(DEBUG) { + System.err.println("FPSAnimator.start() START: "+task+", "+ Thread.currentThread() + ": " + toString()); + } + task.start(timer); + + final boolean res = finishLifecycleAction( drawablesEmpty ? waitForStartedEmptyCondition : waitForStartedAddedCondition, + POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); + if(DEBUG) { + System.err.println("FPSAnimator.start() END: "+task+", "+ Thread.currentThread() + ": " + toString()); } - return true; + if( drawablesEmpty ) { + task.cancel(); + task = null; + } + return res; } + private final Condition waitForStartedAddedCondition = new Condition() { + @Override + public boolean eval() { + return !isStarted() || !isAnimating ; + } }; + private final Condition waitForStartedEmptyCondition = new Condition() { + @Override + public boolean eval() { + return !isStarted() || isAnimating ; + } }; /** Stops this FPSAnimator. Due to the implementation of the FPSAnimator it is not guaranteed that the FPSAnimator will be completely stopped by the time this method returns. */ - public synchronized boolean stop() { - if (timer == null) { + @Override + public final synchronized boolean stop() { + if ( null == timer || !isStarted() ) { return false; } - stateSync.lock(); - try { - shouldRun = false; - if(null != task) { - task.cancel(); - task = null; - } - if(null != timer) { - timer.cancel(); - timer = null; - } - animThread = null; - try { - Thread.sleep(20); // ~ 1/60 hz wait, since we can't ctrl stopped threads - } catch (InterruptedException e) { } - } finally { - stateSync.unlock(); - } - return true; + if(DEBUG) { + System.err.println("FPSAnimator.stop() START: "+task+", "+ Thread.currentThread() + ": " + toString()); + } + final boolean res; + if( null == task ) { + // start/resume case w/ drawablesEmpty + res = true; + } else { + stopIssued = true; + res = finishLifecycleAction(waitForStoppedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); + } + + if(DEBUG) { + System.err.println("FPSAnimator.stop() END: "+task+", "+ Thread.currentThread() + ": " + toString()); + } + if(null != task) { + task.cancel(); + task = null; + } + if(null != timer) { + timer.cancel(); + timer = null; + } + animThread = null; + return res; } + private final Condition waitForStoppedCondition = new Condition() { + @Override + public boolean eval() { + return isStarted(); + } }; - public synchronized boolean pause() { - if (timer == null) { + @Override + public final synchronized boolean pause() { + if ( !isStarted() || pauseIssued ) { return false; } - stateSync.lock(); - try { - shouldRun = false; - if(null != task) { - task.cancel(); - task = null; - } - animThread = null; - try { - Thread.sleep(20); // ~ 1/60 hz wait, since we can't ctrl stopped threads - } catch (InterruptedException e) { } - } finally { - stateSync.unlock(); - } - return true; + if(DEBUG) { + System.err.println("FPSAnimator.pause() START: "+task+", "+ Thread.currentThread() + ": " + toString()); + } + final boolean res; + if( null == task ) { + // start/resume case w/ drawablesEmpty + res = true; + } else { + pauseIssued = true; + res = finishLifecycleAction(waitForPausedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); + } + + if(DEBUG) { + System.err.println("FPSAnimator.pause() END: "+task+", "+ Thread.currentThread() + ": " + toString()); + } + if(null != task) { + task.cancel(); + task = null; + } + return res; } + private final Condition waitForPausedCondition = new Condition() { + @Override + public boolean eval() { + // end waiting if stopped as well + return isStarted() && isAnimating; + } }; - public synchronized boolean resume() { - if (timer == null) { + @Override + public final synchronized boolean resume() { + if ( !isStarted() || !pauseIssued ) { return false; } - stateSync.lock(); - try { - startTask(); - } finally { - stateSync.unlock(); + if(DEBUG) { + System.err.println("FPSAnimator.resume() START: "+ Thread.currentThread() + ": " + toString()); + } + final boolean res; + if( drawablesEmpty ) { + res = true; + } else { + if( null != task ) { + if( DEBUG ) { + System.err.println("FPSAnimator.resume() Ops: !pauseIssued, but task != null: "+toString()); + Thread.dumpStack(); + } + task.cancel(); + task = null; + } + task = new MainTask(); + task.start(timer); + res = finishLifecycleAction(waitForResumeCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); + } + if(DEBUG) { + System.err.println("FPSAnimator.resume() END: "+task+", "+ Thread.currentThread() + ": " + toString()); } - return true; + return res; } + private final Condition waitForResumeCondition = new Condition() { + @Override + public boolean eval() { + // end waiting if stopped as well + return !drawablesEmpty && !isAnimating && isStarted(); + } }; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java index 134dd9677..1a9ec2782 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java @@ -1,6 +1,34 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package com.jogamp.opengl.util; +import java.lang.reflect.Constructor; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -26,13 +54,13 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * and starting with a new created Buffer object with initialElementCount size * * On profiles GL2 and ES1 the fixed function pipeline behavior is as expected. - * On profile ES2 the fixed function emulation will transform these calls to + * On profile ES2 the fixed function emulation will transform these calls to * EnableVertexAttribArray and VertexAttribPointer calls, * and a predefined vertex attribute variable name will be chosen. - * - * The default name mapping will be used, + * + * The default name mapping will be used, * see {@link GLPointerFuncUtil#getPredefinedArrayIndexName(int)}. - * + * * @param index The GL array index * @param comps The array component number * @param dataType The array index GL data type @@ -40,13 +68,13 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * @param initialElementCount * * @see javax.media.opengl.GLContext#getPredefinedArrayIndexName(int) - */ - public static GLArrayDataClient createFixed(int index, int comps, int dataType, boolean normalized, int initialElementCount) + */ + public static GLArrayDataClient createFixed(final int index, final int comps, final int dataType, final boolean normalized, final int initialElementCount) throws GLException { - GLArrayDataClient adc = new GLArrayDataClient(); - GLArrayHandler glArrayHandler = new GLFixedArrayHandler(adc); - adc.init(null, index, comps, dataType, normalized, 0, null, initialElementCount, false, glArrayHandler, 0, 0, 0, 0, false); + final GLArrayDataClient adc = new GLArrayDataClient(); + final GLArrayHandler glArrayHandler = new GLFixedArrayHandler(adc); + adc.init(null, index, comps, dataType, normalized, 0, null, initialElementCount, 0 /* mappedElementCount */, false, glArrayHandler, 0, 0, 0, 0, false); return adc; } @@ -55,13 +83,13 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * and starting with a given Buffer object incl it's stride * * On profiles GL2 and ES1 the fixed function pipeline behavior is as expected. - * On profile ES2 the fixed function emulation will transform these calls to + * On profile ES2 the fixed function emulation will transform these calls to * EnableVertexAttribArray and VertexAttribPointer calls, * and a predefined vertex attribute variable name will be chosen. - * - * The default name mapping will be used, + * + * The default name mapping will be used, * see {@link GLPointerFuncUtil#getPredefinedArrayIndexName(int)}. - * + * * @param index The GL array index * @param comps The array component number * @param dataType The array index GL data type @@ -70,115 +98,132 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * @param buffer the user define data * * @see javax.media.opengl.GLContext#getPredefinedArrayIndexName(int) - */ - public static GLArrayDataClient createFixed(int index, int comps, int dataType, boolean normalized, int stride, - Buffer buffer) + */ + public static GLArrayDataClient createFixed(final int index, final int comps, final int dataType, final boolean normalized, final int stride, + final Buffer buffer) throws GLException { - GLArrayDataClient adc = new GLArrayDataClient(); - GLArrayHandler glArrayHandler = new GLFixedArrayHandler(adc); - adc.init(null, index, comps, dataType, normalized, stride, buffer, comps*comps, false, glArrayHandler, 0, 0, 0, 0, false); + final GLArrayDataClient adc = new GLArrayDataClient(); + final GLArrayHandler glArrayHandler = new GLFixedArrayHandler(adc); + adc.init(null, index, comps, dataType, normalized, stride, buffer, comps*comps, 0 /* mappedElementCount */, false, glArrayHandler, 0, 0, 0, 0, false); return adc; } /** * Create a client side buffer object, using a custom GLSL array attribute name * and starting with a new created Buffer object with initialElementCount size - * @param name The custom name for the GL attribute. + * @param name The custom name for the GL attribute. * @param comps The array component number * @param dataType The array index GL data type * @param normalized Whether the data shall be normalized * @param initialElementCount */ - public static GLArrayDataClient createGLSL(String name, int comps, - int dataType, boolean normalized, int initialElementCount) + public static GLArrayDataClient createGLSL(final String name, final int comps, + final int dataType, final boolean normalized, final int initialElementCount) throws GLException { - GLArrayDataClient adc = new GLArrayDataClient(); - GLArrayHandler glArrayHandler = new GLSLArrayHandler(adc); - adc.init(name, -1, comps, dataType, normalized, 0, null, initialElementCount, true, glArrayHandler, 0, 0, 0, 0, true); + final GLArrayDataClient adc = new GLArrayDataClient(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandler(adc); + adc.init(name, -1, comps, dataType, normalized, 0, null, initialElementCount, 0 /* mappedElementCount */, true, glArrayHandler, 0, 0, 0, 0, true); return adc; } /** * Create a client side buffer object, using a custom GLSL array attribute name * and starting with a given Buffer object incl it's stride - * @param name The custom name for the GL attribute. + * @param name The custom name for the GL attribute. * @param comps The array component number * @param dataType The array index GL data type * @param normalized Whether the data shall be normalized * @param stride * @param buffer the user define data */ - public static GLArrayDataClient createGLSL(String name, int comps, - int dataType, boolean normalized, int stride, Buffer buffer) + public static GLArrayDataClient createGLSL(final String name, final int comps, + final int dataType, final boolean normalized, final int stride, final Buffer buffer) throws GLException { - GLArrayDataClient adc = new GLArrayDataClient(); - GLArrayHandler glArrayHandler = new GLSLArrayHandler(adc); - adc.init(name, -1, comps, dataType, normalized, stride, buffer, comps*comps, true, glArrayHandler, 0, 0, 0, 0, true); + final GLArrayDataClient adc = new GLArrayDataClient(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandler(adc); + adc.init(name, -1, comps, dataType, normalized, stride, buffer, comps*comps, 0 /* mappedElementCount */, true, glArrayHandler, 0, 0, 0, 0, true); return adc; } - // + @Override + public void associate(final Object obj, final boolean enable) { + if(obj instanceof ShaderState) { + if(enable) { + shaderState = (ShaderState)obj; + } else { + shaderState = null; + } + } + } + + // // Data read access // + @Override public final boolean isVBOWritten() { return bufferWritten; } + @Override public final boolean sealed() { return sealed; } - + + @Override public final boolean enabled() { return bufferEnabled; } // // Data and GL state modification .. // - public final void setVBOWritten(boolean written) { bufferWritten=written; } + @Override + public final void setVBOWritten(final boolean written) { + bufferWritten = ( 0 == mappedElementCount ) ? written : true; + } - public void destroy(GL gl) { + @Override + public void destroy(final GL gl) { reset(gl); super.destroy(gl); } - public void reset(GL gl) { + @Override + public void reset(final GL gl) { enableBuffer(gl, false); reset(); } - public void seal(GL gl, boolean seal) { + @Override + public void seal(final GL gl, final boolean seal) { seal(seal); enableBuffer(gl, seal); } - public void enableBuffer(GL gl, boolean enable) { - if( enableBufferAlways || bufferEnabled != enable ) { + @Override + public void enableBuffer(final GL gl, final boolean enable) { + if( enableBufferAlways || bufferEnabled != enable ) { if(enable) { checkSeal(true); // init/generate VBO name if not done yet init_vbo(gl); } - final Object ext; - if(usesGLSL) { - ext = ShaderState.getShaderState(gl); - if(null == ext) { - throw new GLException("A ShaderState must be bound to the GL context, use 'ShaderState.setShaderState(gl)'"); - } - } else { - ext = null; - } - if(enable) { - glArrayHandler.syncData(gl, true, ext); - glArrayHandler.enableState(gl, true, ext); - } else { - glArrayHandler.enableState(gl, false, ext); - glArrayHandler.syncData(gl, false, ext); - } + glArrayHandler.enableState(gl, enable, usesGLSL ? shaderState : null); bufferEnabled = enable; } } - public void setEnableAlways(boolean always) { + @Override + public boolean bindBuffer(final GL gl, final boolean bind) { + if(bind) { + checkSeal(true); + // init/generate VBO name if not done yet + init_vbo(gl); + } + return glArrayHandler.bindBuffer(gl, bind); + } + + @Override + public void setEnableAlways(final boolean always) { enableBufferAlways = always; } @@ -186,37 +231,41 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData // Data modification .. // + @Override public void reset() { - if(buffer!=null) { + if( buffer != null ) { buffer.clear(); } - this.sealed=false; - this.bufferEnabled=false; - this.bufferWritten=false; + sealed = false; + bufferEnabled = false; + bufferWritten = ( 0 == mappedElementCount ) ? false : true; } - public void seal(boolean seal) + @Override + public void seal(final boolean seal) { - if(sealed==seal) return; + if( sealed == seal ) return; sealed = seal; - bufferWritten=false; - if(seal) { - if (null!=buffer) { + bufferWritten = ( 0 == mappedElementCount ) ? false : true; + if( seal ) { + if ( null != buffer ) { buffer.flip(); } - } else if (null!=buffer) { + } else if ( null != buffer ) { buffer.position(buffer.limit()); buffer.limit(buffer.capacity()); } } + @Override public void rewind() { if(buffer!=null) { buffer.rewind(); } } + @Override public void padding(int doneInByteSize) { if ( buffer==null || sealed ) return; while(doneInByteSize<strideB) { @@ -231,7 +280,8 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * This class buffer Class must match the arguments buffer class. * The arguments remaining elements must be a multiple of this arrays element stride. */ - public void put(Buffer v) { + @Override + public void put(final Buffer v) { if ( sealed ) return; /** FIXME: isn't true for interleaved arrays ! if(0!=(v.remaining() % strideL)) { @@ -241,48 +291,57 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData Buffers.put(buffer, v); } - public void putb(byte v) { + @Override + public void putb(final byte v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.putb(buffer, v); } - public void puts(short v) { + @Override + public void puts(final short v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.puts(buffer, v); } - public void puti(int v) { + @Override + public void puti(final int v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.puti(buffer, v); } - public void putx(int v) { + @Override + public void putx(final int v) { puti(v); } - public void putf(float v) { + @Override + public void putf(final float v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.putf(buffer, v); } + @Override public String toString() { return "GLArrayDataClient["+name+ ", index "+index+ ", location "+location+ ", isVertexAttribute "+isVertexAttribute+ - ", dataType 0x"+Integer.toHexString(componentType)+ - ", bufferClazz "+componentClazz+ + ", usesGLSL "+usesGLSL+ + ", usesShaderState "+(null!=shaderState)+ + ", dataType 0x"+Integer.toHexString(componentType)+ + ", bufferClazz "+componentClazz+ ", elements "+getElementCount()+ - ", components "+components+ + ", components "+componentsPerElement+ ", stride "+strideB+"b "+strideL+"c"+ - ", initialElementCount "+initialElementCount+ - ", sealed "+sealed+ - ", bufferEnabled "+bufferEnabled+ - ", bufferWritten "+bufferWritten+ + ", mappedElementCount "+mappedElementCount+ + ", initialElementCount "+initialElementCount+ + ", sealed "+sealed+ + ", bufferEnabled "+bufferEnabled+ + ", bufferWritten "+bufferWritten+ ", buffer "+buffer+ ", alive "+alive+ "]"; @@ -290,48 +349,54 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData // non public matters - protected final boolean growBufferIfNecessary(int spare) { - if(buffer==null || buffer.remaining()<spare) { - growBuffer(Math.max(initialElementCount, spare)); + protected final boolean growBufferIfNecessary(final int spareComponents) { + if( buffer==null || buffer.remaining()<spareComponents ) { + if( 0 != mappedElementCount ) { + throw new GLException("Mapped buffer can't grow. Insufficient storage size: Needed "+spareComponents+" components, "+ + "mappedElementCount "+mappedElementCount+ + ", has mapped buffer "+buffer+"; "+this); + } + growBuffer(Math.max(initialElementCount, (spareComponents+componentsPerElement-1)/componentsPerElement)); return true; } return false; } - protected final void growBuffer(int additionalElements) { + protected final void growBuffer(int additionalElements) { if(!alive || sealed) { - throw new GLException("Invalid state: "+this); + throw new GLException("Invalid state: "+this); } // add the stride delta - additionalElements += (additionalElements/components)*(strideL-components); + additionalElements += (additionalElements/componentsPerElement)*(strideL-componentsPerElement); final int osize = (buffer!=null) ? buffer.capacity() : 0; - final int nsize = osize + ( additionalElements * components ); - + final int nsize = osize + ( additionalElements * componentsPerElement ); + final Buffer oldBuffer = buffer; + if(componentClazz==ByteBuffer.class) { - ByteBuffer newBBuffer = Buffers.newDirectByteBuffer( nsize ); + final ByteBuffer newBBuffer = Buffers.newDirectByteBuffer( nsize ); if(buffer!=null) { buffer.flip(); newBBuffer.put((ByteBuffer)buffer); } buffer = newBBuffer; } else if(componentClazz==ShortBuffer.class) { - ShortBuffer newSBuffer = Buffers.newDirectShortBuffer( nsize ); + final ShortBuffer newSBuffer = Buffers.newDirectShortBuffer( nsize ); if(buffer!=null) { buffer.flip(); newSBuffer.put((ShortBuffer)buffer); } buffer = newSBuffer; } else if(componentClazz==IntBuffer.class) { - IntBuffer newIBuffer = Buffers.newDirectIntBuffer( nsize ); + final IntBuffer newIBuffer = Buffers.newDirectIntBuffer( nsize ); if(buffer!=null) { buffer.flip(); newIBuffer.put((IntBuffer)buffer); } buffer = newIBuffer; } else if(componentClazz==FloatBuffer.class) { - FloatBuffer newFBuffer = Buffers.newDirectFloatBuffer( nsize ); + final FloatBuffer newFBuffer = Buffers.newDirectFloatBuffer( nsize ); if(buffer!=null) { buffer.flip(); newFBuffer.put((FloatBuffer)buffer); @@ -341,54 +406,91 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData throw new GLException("Given Buffer Class not supported: "+componentClazz+":\n\t"+this); } if(DEBUG) { - System.err.println("*** Grow: comps: "+components+", "+(osize/components)+"/"+osize+" -> "+(nsize/components)+"/"+nsize+", "+this); + System.err.println("*** Grow: comps: "+componentsPerElement+", "+(osize/componentsPerElement)+"/"+osize+" -> "+(nsize/componentsPerElement)+"/"+nsize+ + "; "+oldBuffer+" -> "+buffer+"; "+this); } } - protected final void checkSeal(boolean test) throws GLException { + protected final void checkSeal(final boolean test) throws GLException { if(!alive) { - throw new GLException("Invalid state: "+this); - } + throw new GLException("Invalid state: "+this); + } if(sealed!=test) { if(test) { - throw new GLException("Not Sealed yet, seal first:\n\t"+this); + throw new GLException("Not Sealed yet, seal first:\n\t"+this); } else { - throw new GLException("Already Sealed, can't modify VBO:\n\t"+this); + throw new GLException("Already Sealed, can't modify VBO:\n\t"+this); } } } - protected void init(String name, int index, int comps, int dataType, boolean normalized, int stride, Buffer data, - int initialElementCount, boolean isVertexAttribute, GLArrayHandler handler, - int vboName, long vboOffset, int vboUsage, int vboTarget, boolean usesGLSL) + protected void init(final String name, final int index, final int comps, final int dataType, final boolean normalized, final int stride, final Buffer data, + final int initialElementCount, final int mappedElementCount, final boolean isVertexAttribute, + final GLArrayHandler handler, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget, final boolean usesGLSL) throws GLException { - super.init(name, index, comps, dataType, normalized, stride, data, isVertexAttribute, - vboName, vboOffset, vboUsage, vboTarget); + super.init(name, index, comps, dataType, normalized, stride, data, mappedElementCount, + isVertexAttribute, vboName, vboOffset, vboUsage, vboTarget); + if( 0<mappedElementCount && 0<initialElementCount ) { // null!=buffer case validated in super.init(..) + throw new IllegalArgumentException("mappedElementCount:="+mappedElementCount+" specified, but passing non zero initialElementSize"); + } this.initialElementCount = initialElementCount; this.glArrayHandler = handler; this.usesGLSL = usesGLSL; this.sealed=false; this.bufferEnabled=false; this.enableBufferAlways=false; - this.bufferWritten=false; + this.bufferWritten = ( 0 == mappedElementCount ) ? false : true; + if(null==buffer && initialElementCount>0) { growBuffer(initialElementCount); } } private boolean isValidated = false; - - protected void init_vbo(GL gl) { + + protected void init_vbo(final GL gl) { if(!isValidated ) { isValidated = true; validate(gl.getGLProfile(), true); - } + } } protected GLArrayDataClient() { } + /** + * Copy Constructor + * <p> + * Buffer is {@link Buffers#slice(Buffer) sliced}, i.e. sharing content but using own state. + * </p> + * <p> + * All other values are simply copied. + * </p> + */ + public GLArrayDataClient(final GLArrayDataClient src) { + super(src); + this.isValidated = src.isValidated; + this.sealed = src.sealed; + this.bufferEnabled = src.bufferEnabled; + this.bufferWritten = src.bufferWritten; + this.enableBufferAlways = src.enableBufferAlways; + this.initialElementCount = src.initialElementCount; + if( null != src.glArrayHandler ) { + final Class<? extends GLArrayHandler> clazz = src.glArrayHandler.getClass(); + try { + final Constructor<? extends GLArrayHandler> ctor = clazz.getConstructor(GLArrayDataEditable.class); + this.glArrayHandler = ctor.newInstance(this); + } catch (final Exception e) { + throw new RuntimeException("Could not ctor "+clazz.getName()+"("+this.getClass().getName()+")", e); + } + } else { + this.glArrayHandler = null; + } + this.usesGLSL = src.usesGLSL; + this.shaderState = src.shaderState; + } + protected boolean sealed; protected boolean bufferEnabled; protected boolean bufferWritten; @@ -398,5 +500,7 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData protected GLArrayHandler glArrayHandler; protected boolean usesGLSL; + protected ShaderState shaderState; + } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataEditable.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataEditable.java index bb22a4b7e..9a0f1cb37 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataEditable.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataEditable.java @@ -14,7 +14,7 @@ import java.nio.*; public interface GLArrayDataEditable extends GLArrayData { public boolean sealed(); - + public boolean enabled(); /** @@ -31,6 +31,7 @@ public interface GLArrayDataEditable extends GLArrayData { // Data and GL state modification .. // + @Override public void destroy(GL gl); public void reset(GL gl); @@ -40,19 +41,24 @@ public interface GLArrayDataEditable extends GLArrayData { * * @see #seal(boolean) * @see #enableBuffer(GL, boolean) - * + * */ public void seal(GL gl, boolean seal); /** - * <p>Enables/disables the buffer, - * sets the client state, binds the VBO if used - * and transfers the data if necessary.</p> - * + * Enables the buffer if <code>enable</code> is <code>true</code>, + * and transfers the data if required. + * In case {@link #isVBO() VBO is used}, it is bound accordingly for the data transfer and association, + * i.e. it issued {@link #bindBuffer(GL, boolean)}. + * The VBO buffer is unbound when the method returns. + * <p> + * Disables the buffer if <code>enable</code> is <code>false</code>. + * </p> + * * <p>The action will only be executed, - * if the internal enable state differs, + * if the internal enable state differs, * or 'setEnableAlways' was called with 'true'.</b> - * + * * <p>It is up to the user to enable/disable the array properly, * ie in case of multiple data sets for the same vertex attribute (VA). * Meaning in such case usage of one set while expecting another one @@ -63,11 +69,31 @@ public interface GLArrayDataEditable extends GLArrayData { public void enableBuffer(GL gl, boolean enable); /** + * if <code>bind</code> is true and the data uses {@link #isVBO() VBO}, + * the latter will be bound and data written to the GPU if required. + * <p> + * If <code>bind</code> is false and the data uses {@link #isVBO() VBO}, + * the latter will be unbound. + * </p> + * <p> + * This method is exposed to allow data VBO arrays, i.e. {@link GL#GL_ELEMENT_ARRAY_BUFFER}, + * to be bounded and written while keeping the VBO bound. The latter is in contrast to {@link #enableBuffer(GL, boolean)}, + * which leaves the VBO unbound, since it's not required for vertex attributes or pointers. + * </p> + * + * @param gl current GL object + * @param bind true if VBO shall be bound and data written, + * otherwise clear VBO binding. + * @return true if data uses VBO and action was performed, otherwise false + */ + public boolean bindBuffer(GL gl, boolean bind); + + /** * Affects the behavior of 'enableBuffer'. * * The default is 'false' * - * This is useful when you mix up + * This is useful when you mix up * GLArrayData usage with conventional GL array calls * or in case of a buggy GL VBO implementation. * @@ -92,7 +118,7 @@ public interface GLArrayDataEditable extends GLArrayData { * ie position:=limit and limit:=capacity.</p> * * @see #seal(boolean) - */ + */ public void seal(boolean seal); public void rewind(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java index c9dd98751..51c55e326 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java @@ -1,14 +1,48 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package com.jogamp.opengl.util; import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLArrayData; +import javax.media.opengl.GLBufferStorage; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLPointerFuncUtil; +import com.jogamp.common.nio.Buffers; + import jogamp.opengl.util.GLArrayHandler; import jogamp.opengl.util.GLArrayHandlerInterleaved; import jogamp.opengl.util.GLDataArrayHandler; @@ -30,31 +64,31 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE * and starting with a given Buffer object incl it's stride * * On profiles GL2 and ES1 the fixed function pipeline behavior is as expected. - * On profile ES2 the fixed function emulation will transform these calls to + * On profile ES2 the fixed function emulation will transform these calls to * EnableVertexAttribArray and VertexAttribPointer calls, * and a predefined vertex attribute variable name will be chosen. - * - * The default name mapping will be used, + * + * The default name mapping will be used, * see {@link GLPointerFuncUtil#getPredefinedArrayIndexName(int)}. - * + * * @param index The GL array index - * @param comps The array component number - * @param dataType The array index GL data type + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized - * @param stride + * @param stride in bytes from one element to the other. If zero, compsPerElement * compSizeInBytes * @param buffer the user define data * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} * * @see javax.media.opengl.GLContext#getPredefinedArrayIndexName(int) */ - public static GLArrayDataServer createFixed(int index, int comps, int dataType, boolean normalized, int stride, - Buffer buffer, int vboUsage) + public static GLArrayDataServer createFixed(final int index, final int compsPerElement, final int dataType, final boolean normalized, final int stride, + final Buffer buffer, final int vboUsage) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLFixedArrayHandler(ads); - ads.init(null, index, comps, dataType, normalized, stride, buffer, buffer.limit(), false, glArrayHandler, - 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLFixedArrayHandler(ads); + ads.init(null, index, compsPerElement, dataType, normalized, stride, buffer, buffer.limit(), 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); return ads; } @@ -63,143 +97,234 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE * and starting with a new created Buffer object with initialElementCount size * * On profiles GL2 and ES1 the fixed function pipeline behavior is as expected. - * On profile ES2 the fixed function emulation will transform these calls to + * On profile ES2 the fixed function emulation will transform these calls to * EnableVertexAttribArray and VertexAttribPointer calls, * and a predefined vertex attribute variable name will be chosen. - * - * The default name mapping will be used, + * + * The default name mapping will be used, * see {@link GLPointerFuncUtil#getPredefinedArrayIndexName(int)}. - * + * * @param index The GL array index - * @param comps The array component number - * @param dataType The array index GL data type + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized * @param initialElementCount * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} * * @see javax.media.opengl.GLContext#getPredefinedArrayIndexName(int) */ - public static GLArrayDataServer createFixed(int index, int comps, int dataType, boolean normalized, int initialElementCount, - int vboUsage) + public static GLArrayDataServer createFixed(final int index, final int compsPerElement, final int dataType, final boolean normalized, final int initialElementCount, + final int vboUsage) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLFixedArrayHandler(ads); - ads.init(null, index, comps, dataType, normalized, 0, null, initialElementCount, false, glArrayHandler, - 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLFixedArrayHandler(ads); + ads.init(null, index, compsPerElement, dataType, normalized, 0, null, initialElementCount, 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); return ads; } /** * Create a VBO, using a custom GLSL array attribute name * and starting with a new created Buffer object with initialElementCount size - * @param name The custom name for the GL attribute - * @param comps The array component number - * @param dataType The array index GL data type + * @param name The custom name for the GL attribute + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized * @param initialElementCount * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} */ - public static GLArrayDataServer createGLSL(String name, int comps, - int dataType, boolean normalized, int initialElementCount, int vboUsage) - throws GLException + public static GLArrayDataServer createGLSL(final String name, final int compsPerElement, + final int dataType, final boolean normalized, final int initialElementCount, final int vboUsage) + throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLSLArrayHandler(ads); - ads.init(name, -1, comps, dataType, normalized, 0, null, initialElementCount, - true, glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandler(ads); + ads.init(name, -1, compsPerElement, dataType, normalized, 0, null, initialElementCount, + 0 /* mappedElementCount */, true, glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); return ads; - } - + } + + /** + * Create a VBO, using a custom GLSL array attribute name + * intended for GPU buffer storage mapping, see {@link GLBufferStorage}, via {@link #mapStorage(GL, int)} and {@link #mapStorage(GL, long, long, int)}. + * @param name The custom name for the GL attribute + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type + * @param normalized Whether the data shall be normalized + * @param mappedElementCount + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public static GLArrayDataServer createGLSLMapped(final String name, final int compsPerElement, + final int dataType, final boolean normalized, final int mappedElementCount, final int vboUsage) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandler(ads); + ads.init(name, -1, compsPerElement, dataType, normalized, 0, null, 0 /* initialElementCount */, + mappedElementCount, true, glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + ads.seal(true); + return ads; + } + /** * Create a VBO, using a custom GLSL array attribute name * and starting with a given Buffer object incl it's stride - * @param name The custom name for the GL attribute - * @param comps The array component number - * @param dataType The array index GL data type + * @param name The custom name for the GL attribute + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized - * @param stride + * @param stride in bytes from one element to the other. If zero, compsPerElement * compSizeInBytes * @param buffer the user define data * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} */ - public static GLArrayDataServer createGLSL(String name, int comps, - int dataType, boolean normalized, int stride, Buffer buffer, - int vboUsage) + public static GLArrayDataServer createGLSL(final String name, final int compsPerElement, + final int dataType, final boolean normalized, final int stride, final Buffer buffer, + final int vboUsage) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLSLArrayHandler(ads); - ads.init(name, -1, comps, dataType, normalized, stride, buffer, buffer.limit(), true, glArrayHandler, - 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandler(ads); + ads.init(name, -1, compsPerElement, dataType, normalized, stride, buffer, buffer.limit(), 0 /* mappedElementCount */, true, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); return ads; } - + /** * Create a VBO data object for any target w/o render pipeline association, ie {@link GL#GL_ELEMENT_ARRAY_BUFFER}. - * + * * Hence no index, name for a fixed function pipeline nor vertex attribute is given. - * - * @param comps The array component number - * @param dataType The array index GL data type - * @param stride + * + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type + * @param stride in bytes from one element to the other. If zero, compsPerElement * compSizeInBytes * @param buffer the user define data * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} * @param vboTarget {@link GL#GL_ELEMENT_ARRAY_BUFFER}, .. * {@link GL#glGenBuffers(int, int[], int) */ - public static GLArrayDataServer createData(int comps, int dataType, int stride, - Buffer buffer, int vboUsage, int vboTarget) + public static GLArrayDataServer createData(final int compsPerElement, final int dataType, final int stride, + final Buffer buffer, final int vboUsage, final int vboTarget) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLDataArrayHandler(ads); - ads.init(null, -1, comps, dataType, false, stride, buffer, buffer.limit(), false, glArrayHandler, - 0, 0, vboUsage, vboTarget, false); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLDataArrayHandler(ads); + ads.init(null, -1, compsPerElement, dataType, false, stride, buffer, buffer.limit(), 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, vboTarget, false); return ads; } /** * Create a VBO data object for any target w/o render pipeline association, ie {@link GL#GL_ELEMENT_ARRAY_BUFFER}. - * + * * Hence no index, name for a fixed function pipeline nor vertex attribute is given. - * - * @param comps The array component number - * @param dataType The array index GL data type + * + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type + * @param initialElementCount + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + * @param vboTarget {@link GL#GL_ELEMENT_ARRAY_BUFFER}, .. + */ + public static GLArrayDataServer createData(final int compsPerElement, final int dataType, final int initialElementCount, + final int vboUsage, final int vboTarget) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLDataArrayHandler(ads); + ads.init(null, -1, compsPerElement, dataType, false, 0, null, initialElementCount, 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, vboTarget, false); + return ads; + } + + /** + * Create a VBO data object for any target w/o render pipeline association, i.e. {@link GL#GL_ELEMENT_ARRAY_BUFFER}, + * intended for GPU buffer storage mapping, see {@link GLBufferStorage}, via {@link #mapStorage(GL, int)} and {@link #mapStorage(GL, long, long, int)}. + * <p> + * No index, name for a fixed function pipeline nor vertex attribute is given. + * </p> + * + * @param compsPerElement component count per element + * @param dataType The component's OpenGL data type * @param initialElementCount * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} * @param vboTarget {@link GL#GL_ELEMENT_ARRAY_BUFFER}, .. */ - public static GLArrayDataServer createData(int comps, int dataType, int initialElementCount, - int vboUsage, int vboTarget) + public static GLArrayDataServer createDataMapped(final int compsPerElement, final int dataType, final int mappedElementCount, + final int vboUsage, final int vboTarget) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLDataArrayHandler(ads); - ads.init(null, -1, comps, dataType, false, 0, null, initialElementCount, false, glArrayHandler, - 0, 0, vboUsage, vboTarget, false); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLDataArrayHandler(ads); + ads.init(null, -1, compsPerElement, dataType, false, 0, null, 0 /* initialElementCount */, mappedElementCount, false, + glArrayHandler, 0, 0, vboUsage, vboTarget, false); return ads; } - /** * Create a VBO for fixed function interleaved array data * starting with a new created Buffer object with initialElementCount size. * <p>User needs to <i>configure</i> the interleaved segments via {@link #addFixedSubArray(int, int, int)}.</p> - * - * @param comps The total number of all interleaved components. - * @param dataType The array index GL data type + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized - * @param initialElementCount + * @param initialElementCount The initial number of all interleaved elements * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} */ - public static GLArrayDataServer createFixedInterleaved(int comps, int dataType, boolean normalized, int initialElementCount, - int vboUsage) + public static GLArrayDataServer createFixedInterleaved(final int compsPerElement, final int dataType, final boolean normalized, final int initialElementCount, + final int vboUsage) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLArrayHandlerInterleaved(ads); - ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, comps, dataType, false, 0, null, initialElementCount, false, glArrayHandler, - 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, false, 0, null, initialElementCount, 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); + return ads; + } + + /** + * Create a VBO for fixed function interleaved array data + * intended for GPU buffer storage mapping, see {@link GLBufferStorage}, via {@link #mapStorage(GL, int)} and {@link #mapStorage(GL, long, long, int)}. + * <p>User needs to <i>configure</i> the interleaved segments via {@link #addFixedSubArray(int, int, int)}.</p> + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type + * @param normalized Whether the data shall be normalized + * @param mappedElementCount The total number of all interleaved elements + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public static GLArrayDataServer createFixedInterleavedMapped(final int compsPerElement, final int dataType, final boolean normalized, final int mappedElementCount, + final int vboUsage) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, false, 0, null, 0 /* initialElementCount */, mappedElementCount, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); + ads.seal(true); + return ads; + } + + /** + * Create a VBO for fixed function interleaved array data + * starting with a given Buffer object incl it's stride + * <p>User needs to <i>configure</i> the interleaved segments via {@link #addFixedSubArray(int, int, int)}.</p> + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type + * @param normalized Whether the data shall be normalized + * @param stride in bytes from one element of a sub-array to the other. If zero, compsPerElement * compSizeInBytes + * @param buffer The user define data of all interleaved elements + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public static GLArrayDataServer createFixedInterleaved(final int compsPerElement, final int dataType, final boolean normalized, final int stride, final Buffer buffer, + final int vboUsage) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, normalized, stride, buffer, buffer.limit(), 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, false); return ads; } @@ -207,91 +332,162 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE * Configure a segment of this fixed function interleaved array (see {@link #createFixedInterleaved(int, int, boolean, int, int)}). * <p> * This method may be called several times as long the sum of interleaved components does not - * exceed the total number of components of the created interleaved array.</p> + * exceed the total component count of the created interleaved array.</p> * <p> * The memory of the the interleaved array is being used.</p> * <p> * Must be called before using the array, eg: {@link #seal(boolean)}, {@link #putf(float)}, .. </p> - * + * * @param index The GL array index, maybe -1 if vboTarget is {@link GL#GL_ELEMENT_ARRAY_BUFFER} - * @param comps This interleaved array segment's component number + * @param comps This interleaved array segment's component count per element * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} */ - public GLArrayData addFixedSubArray(int index, int comps, int vboTarget) { + public GLArrayData addFixedSubArray(final int index, final int comps, final int vboTarget) { if(interleavedOffset >= getComponentCount() * getComponentSizeInBytes()) { final int iOffC = interleavedOffset / getComponentSizeInBytes(); throw new GLException("Interleaved offset > total components ("+iOffC+" > "+getComponentCount()+")"); } if(usesGLSL) { - throw new GLException("buffer uses GLSL"); + throw new GLException("buffer uses GLSL"); + } + final int subStrideB = ( 0 == getStride() ) ? getComponentCount() * getComponentSizeInBytes() : getStride(); + final GLArrayDataWrapper ad; + if( 0 < mappedElementCount ) { + ad = GLArrayDataWrapper.createFixed( + index, comps, getComponentType(), + getNormalized(), subStrideB, mappedElementCount, + getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); + } else { + ad = GLArrayDataWrapper.createFixed( + index, comps, getComponentType(), + getNormalized(), subStrideB, getBuffer(), + getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); } - final GLArrayDataWrapper ad = GLArrayDataWrapper.createFixed( - index, comps, getComponentType(), - getNormalized(), getStride(), getBuffer(), - getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); ad.setVBOEnabled(isVBO()); interleavedOffset += comps * getComponentSizeInBytes(); - if(GL.GL_ARRAY_BUFFER == vboTarget) { + if(GL.GL_ARRAY_BUFFER == vboTarget) { glArrayHandler.addSubHandler(new GLFixedArrayHandlerFlat(ad)); } return ad; } - + /** * Create a VBO for GLSL interleaved array data * starting with a new created Buffer object with initialElementCount size. * <p>User needs to <i>configure</i> the interleaved segments via {@link #addGLSLSubArray(int, int, int)}.</p> - * - * @param comps The total number of all interleaved components. - * @param dataType The array index GL data type + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type * @param normalized Whether the data shall be normalized - * @param initialElementCount + * @param initialElementCount The initial number of all interleaved elements * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} */ - public static GLArrayDataServer createGLSLInterleaved(int comps, int dataType, boolean normalized, int initialElementCount, - int vboUsage) + public static GLArrayDataServer createGLSLInterleaved(final int compsPerElement, final int dataType, final boolean normalized, final int initialElementCount, + final int vboUsage) throws GLException { - GLArrayDataServer ads = new GLArrayDataServer(); - GLArrayHandler glArrayHandler = new GLSLArrayHandlerInterleaved(ads); - ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, comps, dataType, false, 0, null, initialElementCount, false, glArrayHandler, - 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, normalized, 0, null, initialElementCount, 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); return ads; } - + + /** + * Create a VBO for GLSL interleaved array data + * intended for GPU buffer storage mapping, see {@link GLBufferStorage}, via {@link #mapStorage(GL, int)} and {@link #mapStorage(GL, long, long, int)}. + * <p>User needs to <i>configure</i> the interleaved segments via {@link #addGLSLSubArray(int, int, int)}.</p> + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type + * @param normalized Whether the data shall be normalized + * @param mappedElementCount The total number of all interleaved elements + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public static GLArrayDataServer createGLSLInterleavedMapped(final int compsPerElement, final int dataType, final boolean normalized, final int mappedElementCount, final int vboUsage) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, normalized, 0, null, 0 /* initialElementCount */, mappedElementCount, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + ads.seal(true); + return ads; + } + + /** + * Create a VBO for GLSL interleaved array data + * starting with a given Buffer object incl it's stride + * <p>User needs to <i>configure</i> the interleaved segments via {@link #addGLSLSubArray(int, int, int)}.</p> + * + * @param compsPerElement The total number of all interleaved components per element. + * @param dataType The component's OpenGL data type + * @param normalized Whether the data shall be normalized + * @param stride in bytes from one element of a sub-array to the other. If zero, compsPerElement * compSizeInBytes + * @param buffer The user define data of all interleaved elements + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public static GLArrayDataServer createGLSLInterleaved(final int compsPerElement, final int dataType, final boolean normalized, final int stride, final Buffer buffer, + final int vboUsage) + throws GLException + { + final GLArrayDataServer ads = new GLArrayDataServer(); + final GLArrayHandler glArrayHandler = new GLSLArrayHandlerInterleaved(ads); + ads.init(GLPointerFuncUtil.mgl_InterleaveArray, -1, compsPerElement, dataType, normalized, stride, buffer, buffer.limit(), 0 /* mappedElementCount */, false, + glArrayHandler, 0, 0, vboUsage, GL.GL_ARRAY_BUFFER, true); + return ads; + } + /** * Configure a segment of this GLSL interleaved array (see {@link #createGLSLInterleaved(int, int, boolean, int, int)}). * <p> * This method may be called several times as long the sum of interleaved components does not - * exceed the total number of components of the created interleaved array.</p> + * exceed the total component count of the created interleaved array.</p> * <p> * The memory of the the interleaved array is being used.</p> * <p> * Must be called before using the array, eg: {@link #seal(boolean)}, {@link #putf(float)}, .. </p> * @param name The custom name for the GL attribute, maybe null if vboTarget is {@link GL#GL_ELEMENT_ARRAY_BUFFER} - * @param comps This interleaved array segment's component number + * @param comps This interleaved array segment's component count per element * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} */ - public GLArrayData addGLSLSubArray(String name, int comps, int vboTarget) { + public GLArrayData addGLSLSubArray(final String name, final int comps, final int vboTarget) { if(interleavedOffset >= getComponentCount() * getComponentSizeInBytes()) { final int iOffC = interleavedOffset / getComponentSizeInBytes(); throw new GLException("Interleaved offset > total components ("+iOffC+" > "+getComponentCount()+")"); } if(!usesGLSL) { - throw new GLException("buffer uses fixed function"); + throw new GLException("buffer uses fixed function"); + } + final int subStrideB = ( 0 == getStride() ) ? getComponentCount() * getComponentSizeInBytes() : getStride(); + final GLArrayDataWrapper ad; + if( 0 < mappedElementCount ) { + ad = GLArrayDataWrapper.createGLSL( + name, comps, getComponentType(), + getNormalized(), subStrideB, mappedElementCount, + getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); + } else { + ad = GLArrayDataWrapper.createGLSL( + name, comps, getComponentType(), + getNormalized(), subStrideB, getBuffer(), + getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); } - final GLArrayDataWrapper ad = GLArrayDataWrapper.createGLSL( - name, comps, getComponentType(), - getNormalized(), getStride(), getBuffer(), - getVBOName(), interleavedOffset, getVBOUsage(), vboTarget); ad.setVBOEnabled(isVBO()); interleavedOffset += comps * getComponentSizeInBytes(); - if(GL.GL_ARRAY_BUFFER == vboTarget) { + if(GL.GL_ARRAY_BUFFER == vboTarget) { glArrayHandler.addSubHandler(new GLSLArrayHandlerFlat(ad)); } return ad; } - + + public final void setInterleavedOffset(final int interleavedOffset) { + this.interleavedOffset = interleavedOffset; + } + + public final int getInterleavedOffset() { + return interleavedOffset; + } + // // Data matters GLArrayData // @@ -300,11 +496,12 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE // Data and GL state modification .. // - public void destroy(GL gl) { + @Override + public void destroy(final GL gl) { // super.destroy(gl): // - GLArrayDataClient.destroy(gl): disables & clears client-side buffer // - GLArrayDataWrapper.destroy(gl) (clears all values 'vboName' ..) - int _vboName = vboName; + final int _vboName = vboName; super.destroy(gl); if(_vboName!=0) { final int[] tmp = new int[] { _vboName } ; @@ -314,40 +511,108 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE } // - // data matters + // data matters // /** - * Convenient way do disable the VBO behavior and + * Convenient way do disable the VBO behavior and * switch to client side data one * Only possible if buffer is defined. */ - public void setVBOEnabled(boolean vboUsage) { + @Override + public void setVBOEnabled(final boolean vboUsage) { checkSeal(false); super.setVBOEnabled(vboUsage); } + public GLBufferStorage mapStorage(final GL gl, final int access) { + if( null != this.getBuffer() ) { + throw new IllegalStateException("user buffer not null"); + } + if( null != mappedStorage ) { + throw new IllegalStateException("already mapped: "+mappedStorage); + } + checkSeal(true); + bindBuffer(gl, true); + gl.glBufferData(getVBOTarget(), getSizeInBytes(), null, getVBOUsage()); + final GLBufferStorage storage = gl.mapBuffer(getVBOTarget(), access); + setMappedBuffer(storage); + bindBuffer(gl, false); + seal(false); + rewind(); + return storage; + } + public GLBufferStorage mapStorage(final GL gl, final long offset, final long length, final int access) { + if( null != this.getBuffer() ) { + throw new IllegalStateException("user buffer not null"); + } + if( null != mappedStorage ) { + throw new IllegalStateException("already mapped: "+mappedStorage); + } + checkSeal(true); + bindBuffer(gl, true); + gl.glBufferData(getVBOTarget(), getSizeInBytes(), null, getVBOUsage()); + final GLBufferStorage storage = gl.mapBufferRange(getVBOTarget(), offset, length, access); + setMappedBuffer(storage); + bindBuffer(gl, false); + seal(false); + rewind(); + return storage; + } + private final void setMappedBuffer(final GLBufferStorage storage) { + mappedStorage = storage; + final ByteBuffer bb = storage.getMappedBuffer(); + if(componentClazz==ByteBuffer.class) { + buffer = bb; + } else if(componentClazz==ShortBuffer.class) { + buffer = bb.asShortBuffer(); + } else if(componentClazz==IntBuffer.class) { + buffer = bb.asIntBuffer(); + } else if(componentClazz==FloatBuffer.class) { + buffer = bb.asFloatBuffer(); + } else { + throw new GLException("Given Buffer Class not supported: "+componentClazz+":\n\t"+this); + } + } + + public void unmapStorage(final GL gl) { + if( null == mappedStorage ) { + throw new IllegalStateException("not mapped"); + } + mappedStorage = null; + buffer = null; + seal(true); + bindBuffer(gl, true); + gl.glUnmapBuffer(getVBOTarget()); + bindBuffer(gl, false); + } + + @Override public String toString() { return "GLArrayDataServer["+name+ ", index "+index+ ", location "+location+ ", isVertexAttribute "+isVertexAttribute+ - ", dataType 0x"+Integer.toHexString(componentType)+ - ", bufferClazz "+componentClazz+ + ", usesGLSL "+usesGLSL+ + ", usesShaderState "+(null!=shaderState)+ + ", dataType 0x"+Integer.toHexString(componentType)+ + ", bufferClazz "+componentClazz+ ", elements "+getElementCount()+ - ", components "+components+ + ", components "+componentsPerElement+ ", stride "+strideB+"b "+strideL+"c"+ ", initialElementCount "+initialElementCount+ - ", vboEnabled "+vboEnabled+ - ", vboName "+vboName+ - ", vboUsage 0x"+Integer.toHexString(vboUsage)+ - ", vboTarget 0x"+Integer.toHexString(vboTarget)+ - ", vboOffset "+vboOffset+ - ", sealed "+sealed+ - ", bufferEnabled "+bufferEnabled+ - ", bufferWritten "+bufferWritten+ - ", buffer "+buffer+ - ", alive "+alive+ + ", mappedElementCount "+mappedElementCount+ + ", mappedStorage "+mappedStorage+ + ", vboEnabled "+vboEnabled+ + ", vboName "+vboName+ + ", vboUsage 0x"+Integer.toHexString(vboUsage)+ + ", vboTarget 0x"+Integer.toHexString(vboTarget)+ + ", vboOffset "+vboOffset+ + ", sealed "+sealed+ + ", bufferEnabled "+bufferEnabled+ + ", bufferWritten "+bufferWritten+ + ", buffer "+buffer+ + ", alive "+alive+ "]"; } @@ -355,22 +620,24 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE // non public matters .. // - protected void init(String name, int index, int comps, int dataType, boolean normalized, - int stride, Buffer data, int initialElementCount, boolean isVertexAttribute, - GLArrayHandler glArrayHandler, - int vboName, long vboOffset, int vboUsage, int vboTarget, boolean usesGLSL) + @Override + protected void init(final String name, final int index, final int comps, final int dataType, final boolean normalized, + final int stride, final Buffer data, final int initialElementCount, final int mappedElementCount, + final boolean isVertexAttribute, + final GLArrayHandler glArrayHandler, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget, final boolean usesGLSL) throws GLException { - super.init(name, index, comps, dataType, normalized, stride, data, initialElementCount, isVertexAttribute, glArrayHandler, - vboName, vboOffset, vboUsage, vboTarget, usesGLSL); + super.init(name, index, comps, dataType, normalized, stride, data, initialElementCount, mappedElementCount, isVertexAttribute, + glArrayHandler, vboName, vboOffset, vboUsage, vboTarget, usesGLSL); vboEnabled=true; } - protected void init_vbo(GL gl) { + @Override + protected void init_vbo(final GL gl) { super.init_vbo(gl); if(vboEnabled && vboName==0) { - int[] tmp = new int[1]; + final int[] tmp = new int[1]; gl.glGenBuffers(1, tmp, 0); vboName = tmp[0]; if(0 < interleavedOffset) { @@ -378,7 +645,25 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE } } } - - private int interleavedOffset = 0; + + protected GLArrayDataServer() { } + + /** + * Copy Constructor + * <p> + * Buffer is {@link Buffers#slice(Buffer) sliced}, i.e. sharing content but using own state. + * </p> + * <p> + * All other values are simply copied. + * </p> + */ + public GLArrayDataServer(final GLArrayDataServer src) { + super(src); + this.interleavedOffset = src.interleavedOffset; + this.mappedStorage = src.mappedStorage; + } + + private int interleavedOffset = 0; + private GLBufferStorage mappedStorage = null; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataWrapper.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataWrapper.java index bade0a3ae..867bc00ac 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataWrapper.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataWrapper.java @@ -1,3 +1,30 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package com.jogamp.opengl.util; @@ -15,6 +42,8 @@ import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.fixedfunc.GLPointerFuncUtil; +import com.jogamp.common.nio.Buffers; + import jogamp.opengl.Debug; public class GLArrayDataWrapper implements GLArrayData { @@ -22,7 +51,7 @@ public class GLArrayDataWrapper implements GLArrayData { /** * Create a VBO, using a predefined fixed function array index, wrapping the given data. - * + * * @param index The GL array index * @param comps The array component number * @param dataType The array index GL data type @@ -34,23 +63,50 @@ public class GLArrayDataWrapper implements GLArrayData { * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} * @return the new create instance - * + * + * @throws GLException + */ + public static GLArrayDataWrapper createFixed(final int index, final int comps, final int dataType, final boolean normalized, final int stride, + final Buffer buffer, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget) + throws GLException + { + final GLArrayDataWrapper adc = new GLArrayDataWrapper(); + adc.init(null, index, comps, dataType, normalized, stride, buffer, 0 /* mappedElementCount */, + false, vboName, vboOffset, vboUsage, vboTarget); + return adc; + } + + /** + * Create a VBO, using a predefined fixed function array index, wrapping the mapped data characteristics. + * + * @param index The GL array index + * @param comps The array component number + * @param dataType The array index GL data type + * @param normalized Whether the data shall be normalized + * @param stride + * @param mappedElementCount + * @param vboName + * @param vboOffset + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} + * @return the new create instance + * * @throws GLException */ - public static GLArrayDataWrapper createFixed(int index, int comps, int dataType, boolean normalized, int stride, - Buffer buffer, int vboName, long vboOffset, int vboUsage, int vboTarget) + public static GLArrayDataWrapper createFixed(final int index, final int comps, final int dataType, final boolean normalized, final int stride, + final int mappedElementCount, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget) throws GLException { - GLArrayDataWrapper adc = new GLArrayDataWrapper(); - adc.init(null, index, comps, dataType, normalized, stride, buffer, false, - vboName, vboOffset, vboUsage, vboTarget); + final GLArrayDataWrapper adc = new GLArrayDataWrapper(); + adc.init(null, index, comps, dataType, normalized, stride, null, mappedElementCount, + false, vboName, vboOffset, vboUsage, vboTarget); return adc; } /** * Create a VBO, using a custom GLSL array attribute name, wrapping the given data. - * - * @param name The custom name for the GL attribute, maybe null if gpuBufferTarget is {@link GL#GL_ELEMENT_ARRAY_BUFFER} + * + * @param name The custom name for the GL attribute, maybe null if gpuBufferTarget is {@link GL#GL_ELEMENT_ARRAY_BUFFER} * @param comps The array component number * @param dataType The array index GL data type * @param normalized Whether the data shall be normalized @@ -63,30 +119,56 @@ public class GLArrayDataWrapper implements GLArrayData { * @return the new create instance * @throws GLException */ - public static GLArrayDataWrapper createGLSL(String name, int comps, int dataType, boolean normalized, int stride, - Buffer buffer, int vboName, long vboOffset, int vboUsage, int vboTarget) + public static GLArrayDataWrapper createGLSL(final String name, final int comps, final int dataType, final boolean normalized, final int stride, + final Buffer buffer, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget) throws GLException { - GLArrayDataWrapper adc = new GLArrayDataWrapper(); - adc.init(name, -1, comps, dataType, normalized, stride, buffer, true, - vboName, vboOffset, vboUsage, vboTarget); + final GLArrayDataWrapper adc = new GLArrayDataWrapper(); + adc.init(name, -1, comps, dataType, normalized, stride, buffer, 0 /* mappedElementCount */, + true, vboName, vboOffset, vboUsage, vboTarget); + return adc; + } + + /** + * Create a VBO, using a custom GLSL array attribute name, wrapping the mapped data characteristics. + * + * @param name The custom name for the GL attribute, maybe null if gpuBufferTarget is {@link GL#GL_ELEMENT_ARRAY_BUFFER} + * @param comps The array component number + * @param dataType The array index GL data type + * @param normalized Whether the data shall be normalized + * @param stride + * @param mappedElementCount + * @param vboName + * @param vboOffset + * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} + * @return the new create instance + * @throws GLException + */ + public static GLArrayDataWrapper createGLSL(final String name, final int comps, final int dataType, final boolean normalized, final int stride, + final int mappedElementCount, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget) + throws GLException + { + final GLArrayDataWrapper adc = new GLArrayDataWrapper(); + adc.init(name, -1, comps, dataType, normalized, stride, null, mappedElementCount, + true, vboName, vboOffset, vboUsage, vboTarget); return adc; } /** * Validates this instance's parameter. Called automatically by {@link GLArrayDataClient} and {@link GLArrayDataServer}. - * {@link GLArrayDataWrapper} does not validate it's instance by itself. - * + * {@link GLArrayDataWrapper} does not validate it's instance by itself. + * * @param glp the GLProfile to use * @param throwException whether to throw an exception if this instance has invalid parameter or not * @return true if this instance has invalid parameter, otherwise false */ - public final boolean validate(GLProfile glp, boolean throwException) { + public final boolean validate(final GLProfile glp, final boolean throwException) { if(!alive) { if(throwException) { throw new GLException("Instance !alive "+this); } - return false; + return false; } if(this.isVertexAttribute() && !glp.hasGLSL()) { if(throwException) { @@ -96,55 +178,104 @@ public class GLArrayDataWrapper implements GLArrayData { } return glp.isValidArrayDataType(getIndex(), getComponentCount(), getComponentType(), isVertexAttribute(), throwException); } - - // + + @Override + public void associate(final Object obj, final boolean enable) { + // nop + } + + // // Data read access // + @Override public final boolean isVertexAttribute() { return isVertexAttribute; } + @Override public final int getIndex() { return index; } + @Override public final int getLocation() { return location; } - public final void setLocation(int v) { location = v; } + @Override + public final int setLocation(final int v) { location = v; return location; } + + @Override + public final int setLocation(final GL2ES2 gl, final int program) { + location = gl.glGetAttribLocation(program, name); + return location; + } + + @Override + public final int setLocation(final GL2ES2 gl, final int program, final int location) { + this.location = location; + gl.glBindAttribLocation(program, location, name); + return location; + } + @Override public final String getName() { return name; } + @Override public final long getVBOOffset() { return vboEnabled?vboOffset:0; } + @Override public final int getVBOName() { return vboEnabled?vboName:0; } + @Override public final boolean isVBO() { return vboEnabled; } + @Override public final int getVBOUsage() { return vboEnabled?vboUsage:0; } - + + @Override public final int getVBOTarget() { return vboEnabled?vboTarget:0; } - - public final Buffer getBuffer() { return buffer; } - public final int getComponentCount() { return components; } + @Override + public Buffer getBuffer() { return buffer; } + + @Override + public final int getComponentCount() { return componentsPerElement; } + @Override public final int getComponentType() { return componentType; } + @Override public final int getComponentSizeInBytes() { return componentByteSize; } - + + @Override public final int getElementCount() { - if(null==buffer) return 0; - return ( buffer.position()==0 ) ? ( buffer.limit() / components ) : ( buffer.position() / components ) ; + if( 0 != mappedElementCount ) { + return mappedElementCount; + } else if( null != buffer ) { + final int remainingComponents = ( 0 == buffer.position() ) ? buffer.limit() : buffer.position(); + return ( remainingComponents * componentByteSize ) / strideB ; + } else { + return 0; + } } + + @Override public final int getSizeInBytes() { - if(null==buffer) return 0; - return ( buffer.position()==0 ) ? ( buffer.limit() * componentByteSize ) : ( buffer.position() * componentByteSize ) ; + if( 0 != mappedElementCount ) { + return mappedElementCount * componentsPerElement * componentByteSize ; + } else if( null != buffer ) { + return ( buffer.position()==0 ) ? ( buffer.limit() * componentByteSize ) : ( buffer.position() * componentByteSize ) ; + } else { + return 0; + } } - + + @Override public final boolean getNormalized() { return normalized; } + @Override public final int getStride() { return strideB; } - public final Class getBufferClass() { return componentClazz; } + public final Class<?> getBufferClass() { return componentClazz; } - public void destroy(GL gl) { + @Override + public void destroy(final GL gl) { buffer = null; vboName=0; vboEnabled=false; @@ -152,27 +283,29 @@ public class GLArrayDataWrapper implements GLArrayData { alive = false; } + @Override public String toString() { return "GLArrayDataWrapper["+name+ ", index "+index+ ", location "+location+ ", isVertexAttribute "+isVertexAttribute+ - ", dataType 0x"+Integer.toHexString(componentType)+ - ", bufferClazz "+componentClazz+ + ", dataType 0x"+Integer.toHexString(componentType)+ + ", bufferClazz "+componentClazz+ ", elements "+getElementCount()+ - ", components "+components+ + ", components "+componentsPerElement+ ", stride "+strideB+"b "+strideL+"c"+ - ", buffer "+buffer+ - ", vboEnabled "+vboEnabled+ - ", vboName "+vboName+ - ", vboUsage 0x"+Integer.toHexString(vboUsage)+ - ", vboTarget 0x"+Integer.toHexString(vboTarget)+ - ", vboOffset "+vboOffset+ - ", alive "+alive+ + ", mappedElementCount "+mappedElementCount+ + ", buffer "+buffer+ + ", vboEnabled "+vboEnabled+ + ", vboName "+vboName+ + ", vboUsage 0x"+Integer.toHexString(vboUsage)+ + ", vboTarget 0x"+Integer.toHexString(vboTarget)+ + ", vboOffset "+vboOffset+ + ", alive "+alive+ "]"; } - public static final Class getBufferClass(int dataType) { + public static final Class<?> getBufferClass(final int dataType) { switch(dataType) { case GL.GL_BYTE: case GL.GL_UNSIGNED_BYTE: @@ -180,16 +313,19 @@ public class GLArrayDataWrapper implements GLArrayData { case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: return ShortBuffer.class; - case GL2ES1.GL_FIXED: + case GL.GL_UNSIGNED_INT: + case GL.GL_FIXED: + case GL2ES2.GL_INT: return IntBuffer.class; case GL.GL_FLOAT: return FloatBuffer.class; - default: + default: throw new GLException("Given OpenGL data type not supported: "+dataType); } } - public void setName(String newName) { + @Override + public void setName(final String newName) { location = -1; name = newName; } @@ -198,94 +334,91 @@ public class GLArrayDataWrapper implements GLArrayData { * Enable or disable use of VBO. * Only possible if a VBO buffer name is defined. * @see #setVBOName(int) - */ - public void setVBOEnabled(boolean vboEnabled) { + */ + public void setVBOEnabled(final boolean vboEnabled) { this.vboEnabled=vboEnabled; } /** * Set the VBO buffer name, if valid (!= 0) enable use of VBO, * otherwise (==0) disable VBO usage. - * + * * @see #setVBOEnabled(boolean) - */ - public void setVBOName(int vboName) { + */ + public void setVBOName(final int vboName) { this.vboName=vboName; setVBOEnabled(0!=vboName); } - /** + /** * @param vboUsage {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} - */ - public void setVBOUsage(int vboUsage) { - this.vboUsage = vboUsage; + */ + public void setVBOUsage(final int vboUsage) { + this.vboUsage = vboUsage; } - - /** + + /** * @param vboTarget either {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} - */ - public void setVBOTarget(int vboTarget) { + */ + public void setVBOTarget(final int vboTarget) { this.vboTarget = vboTarget; - } + } - protected void init(String name, int index, int components, int componentType, - boolean normalized, int stride, Buffer data, - boolean isVertexAttribute, - int vboName, long vboOffset, int vboUsage, int vboTarget) + protected void init(final String name, final int index, final int componentsPerElement, final int componentType, + final boolean normalized, final int stride, final Buffer data, final int mappedElementCount, + final boolean isVertexAttribute, final int vboName, final long vboOffset, final int vboUsage, final int vboTarget) throws GLException { + if( 0<mappedElementCount && null != data ) { + throw new IllegalArgumentException("mappedElementCount:="+mappedElementCount+" specified, but passing non null buffer"); + } this.isVertexAttribute = isVertexAttribute; this.index = index; this.location = -1; // We can't have any dependence on the FixedFuncUtil class here for build bootstrapping reasons - + if( GL.GL_ELEMENT_ARRAY_BUFFER == vboTarget ) { - // ok .. - } else if( GL.GL_ARRAY_BUFFER == vboTarget ) { - // check name .. + // OK .. + } else if( ( 0 == vboUsage && 0 == vboTarget ) || GL.GL_ARRAY_BUFFER == vboTarget ) { + // Set/Check name .. - Required for GLSL case. Validation and debug-name for FFP. this.name = ( null == name ) ? GLPointerFuncUtil.getPredefinedArrayIndexName(index) : name ; if(null == this.name ) { throw new GLException("Not a valid array buffer index: "+index); - } + } } else if( 0 < vboTarget ) { throw new GLException("Invalid GPUBuffer target: 0x"+Integer.toHexString(vboTarget)); } - + this.componentType = componentType; componentClazz = getBufferClass(componentType); - switch(componentType) { - case GL.GL_BYTE: - case GL.GL_UNSIGNED_BYTE: - case GL.GL_SHORT: - case GL.GL_UNSIGNED_SHORT: - case GL.GL_FIXED: - this.normalized = normalized; - break; - default: - this.normalized = false; + if( GLBuffers.isGLTypeFixedPoint(componentType) ) { + this.normalized = normalized; + } else { + this.normalized = false; } componentByteSize = GLBuffers.sizeOfGLType(componentType); if(0 > componentByteSize) { - throw new GLException("Given componentType not supported: "+componentType+":\n\t"+this); + throw new GLException("Given componentType not supported: "+componentType+":\n\t"+this); } - if(0 >= components) { - throw new GLException("Invalid number of components: " + components); + if(0 >= componentsPerElement) { + throw new GLException("Invalid number of components: " + componentsPerElement); } - this.components = components; + this.componentsPerElement = componentsPerElement; - if(0<stride && stride<components*componentByteSize) { - throw new GLException("stride ("+stride+") lower than component bytes, "+components+" * "+componentByteSize); + if(0<stride && stride<componentsPerElement*componentByteSize) { + throw new GLException("stride ("+stride+") lower than component bytes, "+componentsPerElement+" * "+componentByteSize); } if(0<stride && stride%componentByteSize!=0) { throw new GLException("stride ("+stride+") not a multiple of bpc "+componentByteSize); } this.buffer = data; - this.strideB=(0==stride)?components*componentByteSize:stride; + this.mappedElementCount = mappedElementCount; + this.strideB=(0==stride)?componentsPerElement*componentByteSize:stride; this.strideL=strideB/componentByteSize; this.vboName= vboName; this.vboEnabled= 0 != vboName ; this.vboOffset=vboOffset; - + switch(vboUsage) { case 0: // nop case GL.GL_STATIC_DRAW: @@ -293,7 +426,7 @@ public class GLArrayDataWrapper implements GLArrayData { case GL2ES2.GL_STREAM_DRAW: break; default: - throw new GLException("invalid gpuBufferUsage: "+vboUsage+":\n\t"+this); + throw new GLException("invalid gpuBufferUsage: "+vboUsage+":\n\t"+this); } switch(vboTarget) { case 0: // nop @@ -304,30 +437,71 @@ public class GLArrayDataWrapper implements GLArrayData { throw new GLException("invalid gpuBufferTarget: "+vboTarget+":\n\t"+this); } this.vboUsage=vboUsage; - this.vboTarget=vboTarget; + this.vboTarget=vboTarget; this.alive=true; } protected GLArrayDataWrapper() { } + /** + * Copy Constructor + * <p> + * Buffer is {@link Buffers#slice(Buffer) sliced}, i.e. sharing content but using own state. + * </p> + * <p> + * All other values are simply copied. + * </p> + */ + public GLArrayDataWrapper(final GLArrayDataWrapper src) { + this.alive = src.alive; + this.index = src.index; + this.location = src.location; + this.name = src.name; + this.componentsPerElement = src.componentsPerElement; + this.componentType = src.componentType; + this.componentClazz = src.componentClazz; + this.componentByteSize = src.componentByteSize; + this.normalized = src.normalized; + this.strideB = src.strideB; + this.strideL = src.strideL; + if( null != src.buffer ) { + if( src.buffer.position() == 0 ) { + this.buffer = Buffers.slice(src.buffer); + } else { + this.buffer = Buffers.slice(src.buffer, 0, src.buffer.limit()); + } + } else { + this.buffer = null; + } + this.mappedElementCount = src.mappedElementCount; + this.isVertexAttribute = src.isVertexAttribute; + this.vboOffset = src.vboOffset; + this.vboName = src.vboName; + this.vboEnabled = src.vboEnabled; + this.vboUsage = src.vboUsage; + this.vboTarget = src.vboTarget; + } + protected boolean alive; protected int index; protected int location; protected String name; - protected int components; + protected int componentsPerElement; protected int componentType; - protected Class componentClazz; + protected Class<?> componentClazz; protected int componentByteSize; protected boolean normalized; - protected int strideB; // stride in bytes - protected int strideL; // stride in logical components + /** stride in bytes; strideB >= componentsPerElement * componentByteSize */ + protected int strideB; + /** stride in logical components */ + protected int strideL; protected Buffer buffer; + protected int mappedElementCount; protected boolean isVertexAttribute; - protected long vboOffset; protected int vboName; protected boolean vboEnabled; protected int vboUsage; - protected int vboTarget; + protected int vboTarget; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java index 331d6fa4e..d4ab4e4f4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,59 +28,104 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package com.jogamp.opengl.util; -import com.jogamp.common.nio.Buffers; +import java.nio.Buffer; +import java.nio.ByteBuffer; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLES2; import javax.media.opengl.GLException; -import java.nio.*; +import com.jogamp.common.nio.Buffers; /** * Utility routines for dealing with direct buffers. - * + * * @author Kenneth Russel, et.al. */ public class GLBuffers extends Buffers { /** - * @param glType shall be one of (29) <br/> + * @param glType GL primitive type + * @return false if one of GL primitive unsigned types, otherwise true + * GL_UNSIGNED_BYTE, <br/> + * GL_UNSIGNED_SHORT, <br/> + * GL_UNSIGNED_INT, <br/> + * GL_HILO16_NV <br/> + */ + public static final boolean isSignedGLType(final int glType) { + switch (glType) { // 29 + case GL.GL_UNSIGNED_BYTE: + case GL.GL_UNSIGNED_SHORT: + case GL.GL_UNSIGNED_INT: + case GL2.GL_HILO16_NV: + return false; + + } + return true; + } + + /** + * @param glType GL primitive type + * @return false if one of GL primitive floating point types, otherwise true + * GL_FLOAT, <br/> + * GL_HALF_FLOAT, <br/> + * GL_HALF_FLOAT_OES, <br/> + * GL_DOUBLE <br/> + */ + public static final boolean isGLTypeFixedPoint(final int glType) { + switch(glType) { + case GL.GL_FLOAT: + case GL.GL_HALF_FLOAT: + case GLES2.GL_HALF_FLOAT_OES: + case GL2GL3.GL_DOUBLE: + return false; + + default: + return true; + } + } + + /** + * @param glType shall be one of (31) <br/> * GL_BYTE, GL_UNSIGNED_BYTE, <br/> * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, <br/> * <br/> * GL_SHORT, GL_UNSIGNED_SHORT, <br/> - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> + * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, <br/> * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, <br/> + * GL_UNSIGNED_SHORT_8_8_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, <br/> * GL.GL_HALF_FLOAT, GLES2.GL_HALF_FLOAT_OES: <br/> * <br/> * GL_FIXED, GL_INT <br/> * GL_UNSIGNED_INT, GL_UNSIGNED_INT_8_8_8_8, <br/> - * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> + * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> * GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_24_8, <br/> - * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> + * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> * GL_HILO16_NV, GL_SIGNED_HILO16_NV <br/> * <br/> * GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV <br/> * <br/> - * GL_FLOAT, GL_DOUBLE <br/> - * - * @return -1 if glType is unhandled, otherwise the actual value > 0 + * GL_FLOAT, GL_DOUBLE <br/> + * + * @return -1 if glType is unhandled, otherwise the actual value > 0 */ - public static final int sizeOfGLType(int glType) { + public static final int sizeOfGLType(final int glType) { switch (glType) { // 29 // case GL2.GL_BITMAP: case GL.GL_BYTE: @@ -88,249 +133,272 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_BYTE_3_3_2: case GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV: return SIZEOF_BYTE; - + case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: case GL.GL_UNSIGNED_SHORT_5_6_5: case GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV: - case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4: + case GL.GL_UNSIGNED_SHORT_4_4_4_4: case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: - case GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1: + case GL.GL_UNSIGNED_SHORT_5_5_5_1: case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL2.GL_UNSIGNED_SHORT_8_8_APPLE: + case GL2.GL_UNSIGNED_SHORT_8_8_REV_APPLE: case GL.GL_HALF_FLOAT: case GLES2.GL_HALF_FLOAT_OES: return SIZEOF_SHORT; - + case GL.GL_FIXED: case GL2ES2.GL_INT: case GL.GL_UNSIGNED_INT: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV: - case GL2GL3.GL_UNSIGNED_INT_10_10_10_2: - case GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV: - case GL2GL3.GL_UNSIGNED_INT_24_8: - case GL2GL3.GL_UNSIGNED_INT_10F_11F_11F_REV: - case GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV: + case GL2ES2.GL_UNSIGNED_INT_10_10_10_2: + case GL2ES2.GL_UNSIGNED_INT_2_10_10_10_REV: + case GL.GL_UNSIGNED_INT_24_8: + case GL.GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL2ES3.GL_UNSIGNED_INT_5_9_9_9_REV: case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: return SIZEOF_INT; - - case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + + case GL2ES3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return SIZEOF_LONG; - + case GL.GL_FLOAT: return SIZEOF_FLOAT; - + case GL2GL3.GL_DOUBLE: return SIZEOF_DOUBLE; } return -1; } - + /** - * @param glType shall be one of (29) <br/> + * @param glType shall be one of (31) <br/> * GL_BYTE, GL_UNSIGNED_BYTE, <br/> * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, <br/> * <br/> * GL_SHORT, GL_UNSIGNED_SHORT, <br/> - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> + * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, <br/> * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, <br/> + * GL_UNSIGNED_SHORT_8_8_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, <br/> * GL_HALF_FLOAT, GL_HALF_FLOAT_OES <br/> * <br/> * GL_FIXED, GL_INT <br/> * GL_UNSIGNED_INT, GL_UNSIGNED_INT_8_8_8_8, <br/> - * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> + * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> * GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_24_8, <br/> - * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> + * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> * GL_HILO16_NV, GL_SIGNED_HILO16_NV <br/> * <br/> * GL_FLOAT_32_UNSIGNED_INT_24_8_REV <br/> * <br/> - * GL_FLOAT, GL_DOUBLE <br/> - * - * @return null if glType is unhandled, otherwise the new Buffer object + * GL_FLOAT, GL_DOUBLE <br/> + * + * @return null if glType is unhandled, otherwise the new Buffer object */ - public static final Buffer newDirectGLBuffer(int glType, int numElements) { + public static final Buffer newDirectGLBuffer(final int glType, final int numElements) { switch (glType) { // 29 case GL.GL_BYTE: case GL.GL_UNSIGNED_BYTE: case GL2GL3.GL_UNSIGNED_BYTE_3_3_2: case GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV: return newDirectByteBuffer(numElements); - + case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: case GL.GL_UNSIGNED_SHORT_5_6_5: case GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV: - case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4: + case GL.GL_UNSIGNED_SHORT_4_4_4_4: case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: - case GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1: + case GL.GL_UNSIGNED_SHORT_5_5_5_1: case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL2.GL_UNSIGNED_SHORT_8_8_APPLE: + case GL2.GL_UNSIGNED_SHORT_8_8_REV_APPLE: case GL.GL_HALF_FLOAT: case GLES2.GL_HALF_FLOAT_OES: return newDirectShortBuffer(numElements); - + case GL.GL_FIXED: case GL2ES2.GL_INT: case GL.GL_UNSIGNED_INT: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV: - case GL2GL3.GL_UNSIGNED_INT_10_10_10_2: - case GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV: - case GL2GL3.GL_UNSIGNED_INT_24_8: - case GL2GL3.GL_UNSIGNED_INT_10F_11F_11F_REV: - case GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV: + case GL2ES2.GL_UNSIGNED_INT_10_10_10_2: + case GL2ES2.GL_UNSIGNED_INT_2_10_10_10_REV: + case GL.GL_UNSIGNED_INT_24_8: + case GL.GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL2ES3.GL_UNSIGNED_INT_5_9_9_9_REV: case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: return newDirectIntBuffer(numElements); - - case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + + case GL2ES3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return newDirectLongBuffer(numElements); - + case GL.GL_FLOAT: return newDirectFloatBuffer(numElements); - - case GL2.GL_DOUBLE: + + case GL2GL3.GL_DOUBLE: return newDirectDoubleBuffer(numElements); } return null; } /** - * @param glType shall be one of (29) <br/> + * @param glType shall be one of (31) <br/> * GL_BYTE, GL_UNSIGNED_BYTE, <br/> * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, <br/> * <br/> * GL_SHORT, GL_UNSIGNED_SHORT, <br/> - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> + * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, <br/> * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, <br/> + * GL_UNSIGNED_SHORT_8_8_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, <br/> * GL_HALF_FLOAT, GL_HALF_FLOAT_OES <br/> * <br/> * GL_FIXED, GL_INT <br/> * GL_UNSIGNED_INT, GL_UNSIGNED_INT_8_8_8_8, <br/> - * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> + * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> * GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_24_8, <br/> - * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> + * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> * GL_HILO16_NV, GL_SIGNED_HILO16_NV <br/> * <br/> * GL_FLOAT_32_UNSIGNED_INT_24_8_REV <br/> * <br/> - * GL_FLOAT, GL_DOUBLE <br/> - * @return null if glType is unhandled or parent is null or bufLen is 0, otherwise the new Buffer object + * GL_FLOAT, GL_DOUBLE <br/> + * @return null if glType is unhandled or parent is null or bufLen is 0, otherwise the new Buffer object */ - public static final Buffer sliceGLBuffer(ByteBuffer parent, int bytePos, int byteLen, int glType) { + public static final Buffer sliceGLBuffer(final ByteBuffer parent, final int bytePos, final int byteLen, final int glType) { if (parent == null || byteLen == 0) { return null; } + final int parentPos = parent.position(); + final int parentLimit = parent.limit(); + parent.position(bytePos); parent.limit(bytePos + byteLen); + Buffer res = null; switch (glType) { // 29 case GL.GL_BYTE: case GL.GL_UNSIGNED_BYTE: case GL2GL3.GL_UNSIGNED_BYTE_3_3_2: case GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV: - return parent.slice(); - + res = parent.slice().order(parent.order()); // slice and duplicate may change byte order + break; + case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: case GL.GL_UNSIGNED_SHORT_5_6_5: case GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV: - case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4: + case GL.GL_UNSIGNED_SHORT_4_4_4_4: case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: - case GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1: + case GL.GL_UNSIGNED_SHORT_5_5_5_1: case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL2.GL_UNSIGNED_SHORT_8_8_APPLE: + case GL2.GL_UNSIGNED_SHORT_8_8_REV_APPLE: case GL.GL_HALF_FLOAT: case GLES2.GL_HALF_FLOAT_OES: - return parent.asShortBuffer(); - + res = parent.slice().order(parent.order()).asShortBuffer(); // slice and duplicate may change byte order + break; + case GL.GL_FIXED: - case GL2GL3.GL_INT: - case GL2ES2.GL_UNSIGNED_INT: + case GL2ES2.GL_INT: + case GL.GL_UNSIGNED_INT: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV: - case GL2GL3.GL_UNSIGNED_INT_10_10_10_2: - case GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV: - case GL2GL3.GL_UNSIGNED_INT_24_8: - case GL2GL3.GL_UNSIGNED_INT_10F_11F_11F_REV: - case GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV: + case GL2ES2.GL_UNSIGNED_INT_10_10_10_2: + case GL2ES2.GL_UNSIGNED_INT_2_10_10_10_REV: + case GL.GL_UNSIGNED_INT_24_8: + case GL.GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL2ES3.GL_UNSIGNED_INT_5_9_9_9_REV: case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: - return parent.asIntBuffer(); - - case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - return parent.asLongBuffer(); - + res = parent.slice().order(parent.order()).asIntBuffer(); // slice and duplicate may change byte order + break; + + case GL2ES3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + res = parent.slice().order(parent.order()).asLongBuffer(); // slice and duplicate may change byte order + break; + case GL.GL_FLOAT: - return parent.asFloatBuffer(); - - case GL2.GL_DOUBLE: - return parent.asDoubleBuffer(); + res = parent.slice().order(parent.order()).asFloatBuffer(); // slice and duplicate may change byte order + break; + + case GL2GL3.GL_DOUBLE: + res = parent.slice().order(parent.order()).asDoubleBuffer(); // slice and duplicate may change byte order + break; } - return null; + parent.position(parentPos).limit(parentLimit); + return res; } - private static final int glGetInteger(GL gl, int pname, int[] tmp) { + private static final int glGetInteger(final GL gl, final int pname, final int[] tmp) { gl.glGetIntegerv(pname, tmp, 0); return tmp[0]; } - - /** + + /** * Returns the number of bytes required to read/write a memory buffer via OpenGL * using the current GL pixel storage state and the given parameters. - * + * * <p>This method is security critical, hence it throws an exception (fail-fast) - * in case of an invalid alignment. In case we forgot to handle - * proper values, please contact the maintainer.</p> - * + * in case of an invalid alignment. In case we forgot to handle + * proper values, please contact the maintainer.</p> + * * @param gl the current GL object - * + * * @param tmp a pass through integer array of size >= 1 used to store temp data (performance) - * - * @param bytesPerElement bytes per element + * + * @param bytesPerPixel bytes per pixel, i.e. via {@link #bytesPerPixel(int, int)}. * @param width in pixels * @param height in pixels * @param depth in pixels - * @param pack true for read mode GPU -> CPU (pack), otherwise false for write mode CPU -> GPU (unpack) + * @param pack true for read mode GPU -> CPU (pack), otherwise false for write mode CPU -> GPU (unpack) * @return required minimum size of the buffer in bytes * @throws GLException if alignment is invalid. Please contact the maintainer if this is our bug. */ - public static final int sizeof(GL gl, int tmp[], - int bytesPerElement, int width, int height, int depth, - boolean pack) { + public static final int sizeof(final GL gl, final int tmp[], + final int bytesPerPixel, int width, int height, int depth, + final boolean pack) { int rowLength = 0; int skipRows = 0; int skipPixels = 0; int alignment = 1; int imageHeight = 0; int skipImages = 0; - - if (pack) { + + if (pack) { alignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT, tmp); - if(gl.isGL2GL3()) { - rowLength = glGetInteger(gl, GL2GL3.GL_PACK_ROW_LENGTH, tmp); - skipRows = glGetInteger(gl, GL2GL3.GL_PACK_SKIP_ROWS, tmp); - skipPixels = glGetInteger(gl, GL2GL3.GL_PACK_SKIP_PIXELS, tmp); - if (depth > 1) { - imageHeight = glGetInteger(gl, GL2GL3.GL_PACK_IMAGE_HEIGHT, tmp); + if( gl.isGL2ES3() ) { + rowLength = glGetInteger(gl, GL2ES3.GL_PACK_ROW_LENGTH, tmp); + skipRows = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_ROWS, tmp); + skipPixels = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_PIXELS, tmp); + if (depth > 1 && gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + imageHeight = glGetInteger(gl, GL2GL3.GL_PACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2GL3.GL_PACK_SKIP_IMAGES, tmp); } } - } else { + } else { alignment = glGetInteger(gl, GL.GL_UNPACK_ALIGNMENT, tmp); - if(gl.isGL2GL3 ()) { - rowLength = glGetInteger(gl, GL2GL3.GL_UNPACK_ROW_LENGTH, tmp); - skipRows = glGetInteger(gl, GL2GL3.GL_UNPACK_SKIP_ROWS, tmp); - skipPixels = glGetInteger(gl, GL2GL3.GL_UNPACK_SKIP_PIXELS, tmp); - if (depth > 1) { - imageHeight = glGetInteger(gl, GL2GL3.GL_UNPACK_IMAGE_HEIGHT, tmp); - skipImages = glGetInteger(gl, GL2GL3.GL_UNPACK_SKIP_IMAGES, tmp); - } + if( gl.isGL2ES3() ) { + rowLength = glGetInteger(gl, GL2ES2.GL_UNPACK_ROW_LENGTH, tmp); + skipRows = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_ROWS, tmp); + skipPixels = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_PIXELS, tmp); + if( depth > 1 && + ( gl.isGL3ES3() || + ( gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) + ) + ) { + imageHeight = glGetInteger(gl, GL2ES3.GL_UNPACK_IMAGE_HEIGHT, tmp); + skipImages = glGetInteger(gl, GL2ES3.GL_UNPACK_SKIP_IMAGES, tmp); + } } } - + // Try to deal somewhat correctly with potentially invalid values width = Math.max(0, width ); height = Math.max(1, height); // min 1D @@ -339,13 +407,13 @@ public class GLBuffers extends Buffers { skipPixels = Math.max(0, skipPixels); alignment = Math.max(1, alignment); skipImages = Math.max(0, skipImages); - + imageHeight = ( imageHeight > 0 ) ? imageHeight : height; rowLength = ( rowLength > 0 ) ? rowLength : width; - - int rowLengthInBytes = rowLength * bytesPerElement; - int skipBytes = skipPixels * bytesPerElement; - + + int rowLengthInBytes = rowLength * bytesPerPixel; + int skipBytes = skipPixels * bytesPerPixel; + switch(alignment) { case 1: break; @@ -361,208 +429,318 @@ public class GLBuffers extends Buffers { if (remainder > 0) { skipBytes += alignment - remainder; } - } + } break; default: - throw new GLException("Invalid alignment "+alignment+", must be 2**n (1,2,4,8). Pls notify the maintainer in case this is our bug."); + throw new GLException("Invalid alignment "+alignment+", must be 2**n (1,2,4,8). Pls notify the maintainer in case this is our bug."); } - + /** * skipImages, depth, skipPixels and skipRows are static offsets. * * skipImages and depth are in multiples of image size. * * skipBytes and rowLengthInBytes are aligned - * - * rowLengthInBytes is the aligned byte offset + * + * rowLengthInBytes is the aligned byte offset * from line n to line n+1 at the same x-axis position. */ return skipBytes + // aligned skipPixels * bpp - ( skipImages + depth - 1 ) * imageHeight * rowLengthInBytes + // aligned whole images + ( skipImages + depth - 1 ) * imageHeight * rowLengthInBytes + // aligned whole images ( skipRows + height - 1 ) * rowLengthInBytes + // aligned lines - width * bytesPerElement; // last line + width * bytesPerPixel; // last line } - - /** + + /** * Returns the number of bytes required to read/write a memory buffer via OpenGL * using the current GL pixel storage state and the given parameters. - * + * * <p>This method is security critical, hence it throws an exception (fail-fast) - * in case either the format, type or alignment is unhandled. In case we forgot to handle - * proper values, please contact the maintainer.</p> - * + * in case either the format, type or alignment is unhandled. In case we forgot to handle + * proper values, please contact the maintainer.</p> + * + * <p> See {@link #bytesPerPixel(int, int)}. </p> + * * @param gl the current GL object - * + * * @param tmp a pass through integer array of size >= 1 used to store temp data (performance) - * - * @param format must be one of (26) <br/> - * GL_COLOR_INDEX GL_STENCIL_INDEX <br/> - * GL_DEPTH_COMPONENT GL_DEPTH_STENCIL <br/> - * GL_RED GL_RED_INTEGER <br/> - * GL_GREEN GL_GREEN_INTEGER <br/> - * GL_BLUE GL_BLUE_INTEGER <br/> - * GL_ALPHA GL_LUMINANCE (12) <br/> - * <br/> + * + * @param format must be one of (27) <br/> + * GL_COLOR_INDEX GL_STENCIL_INDEX <br/> + * GL_DEPTH_COMPONENT GL_DEPTH_STENCIL <br/> + * GL_RED GL_RED_INTEGER <br/> + * GL_GREEN GL_GREEN_INTEGER <br/> + * GL_BLUE GL_BLUE_INTEGER <br/> + * GL_ALPHA GL_LUMINANCE (12) <br/> + * <br/> * GL_LUMINANCE_ALPHA GL_RG <br/> - * GL_RG_INTEGER GL_HILO_NV <br/> - * GL_SIGNED_HILO_NV (5) <br/> - * <br/> - * GL_RGB GL_RGB_INTEGER <br/> - * GL_BGR GL_BGR_INTEGER (4)<br/> - * <br/> - * GL_RGBA GL_RGBA_INTEGER <br/> + * GL_RG_INTEGER GL_HILO_NV <br/> + * GL_SIGNED_HILO_NV (5) <br/> + * <br/> + * GL_YCBCR_422_APPLE <br/> + * <br/> + * GL_RGB GL_RGB_INTEGER <br/> + * GL_BGR GL_BGR_INTEGER (4)<br/> + * <br/> + * GL_RGBA GL_RGBA_INTEGER <br/> * GL_BGRA GL_BGRA_INTEGER <br/> - * GL_ABGR_EXT (5)<br/> - * - * @param type must be one of (30) <br/> - * GL_BITMAP, <br/> + * GL_ABGR_EXT (5)<br/> + * + * @param type must be one of (32) <br/> + * GL_BITMAP, <br/> * GL_BYTE, GL_UNSIGNED_BYTE, <br/> * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, <br/> * <br/> * GL_SHORT, GL_UNSIGNED_SHORT, <br/> - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> + * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, <br/> * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, <br/> + * GL_UNSIGNED_SHORT_8_8_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, <br/> * GL_HALF_FLOAT, GL_HALF_FLOAT_OES <br/> * <br/> * GL_FIXED, GL_INT <br/> * GL_UNSIGNED_INT, GL_UNSIGNED_INT_8_8_8_8, <br/> - * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> + * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> * GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_24_8, <br/> - * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> + * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> * GL_HILO16_NV, GL_SIGNED_HILO16_NV <br/> * <br/> * GL_FLOAT_32_UNSIGNED_INT_24_8_REV <br/> * <br/> - * GL_FLOAT, GL_DOUBLE <br/> - * + * GL_FLOAT, GL_DOUBLE <br/> + * * @param width in pixels * @param height in pixels * @param depth in pixels - * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU * @return required minimum size of the buffer in bytes * @throws GLException if format, type or alignment is not handled. Please contact the maintainer if this is our bug. */ - public static final int sizeof(GL gl, int tmp[], - int format, int type, int width, int height, int depth, - boolean pack) throws GLException { - int elements = 0; - int esize = 0; - + public static final int sizeof(final GL gl, final int tmp[], + final int format, final int type, final int width, final int height, final int depth, + final boolean pack) throws GLException { if (width < 0) return 0; if (height < 0) return 0; if (depth < 0) return 0; - - switch (format) /* 26 */ { - case GL2.GL_COLOR_INDEX: - case GL2GL3.GL_STENCIL_INDEX: - case GL2GL3.GL_DEPTH_COMPONENT: - case GL2GL3.GL_DEPTH_STENCIL: - case GL2GL3.GL_RED: - case GL2GL3.GL_RED_INTEGER: - case GL2GL3.GL_GREEN: - case GL2GL3.GL_GREEN_INTEGER: - case GL2GL3.GL_BLUE: - case GL2GL3.GL_BLUE_INTEGER: - case GL.GL_ALPHA: - case GL.GL_LUMINANCE: - elements = 1; - break; - case GL.GL_LUMINANCE_ALPHA: - case GL2GL3.GL_RG: - case GL2GL3.GL_RG_INTEGER: - case GL2.GL_HILO_NV: - case GL2.GL_SIGNED_HILO_NV: - elements = 2; - break; - case GL.GL_RGB: - case GL2GL3.GL_RGB_INTEGER: - case GL2GL3.GL_BGR: - case GL2GL3.GL_BGR_INTEGER: - elements = 3; - break; - case GL.GL_RGBA: - case GL2GL3.GL_RGBA_INTEGER: - case GL.GL_BGRA: - case GL2GL3.GL_BGRA_INTEGER: - case GL2.GL_ABGR_EXT: - elements = 4; - break; - /* FIXME ?? - case GL.GL_HILO_NV: - elements = 2; - break; */ - default: - throw new GLException("format 0x"+Integer.toHexString(format)+" not supported [yet], pls notify the maintainer in case this is our bug."); - } - + + final int bytesPerPixel = bytesPerPixel(format, type); + return sizeof(gl, tmp, bytesPerPixel, width, height, depth, pack); + } + + /** + * Returns the number of bytes required for one pixel with the the given OpenGL format and type. + * + * <p>This method is security critical, hence it throws an exception (fail-fast) + * in case either the format, type or alignment is unhandled. In case we forgot to handle + * proper values, please contact the maintainer.</p> + * + * <p> See {@link #componentCount(int)}. </p> + * + * @param format must be one of (27) <br/> + * GL_COLOR_INDEX GL_STENCIL_INDEX <br/> + * GL_DEPTH_COMPONENT GL_DEPTH_STENCIL <br/> + * GL_RED GL_RED_INTEGER <br/> + * GL_GREEN GL_GREEN_INTEGER <br/> + * GL_BLUE GL_BLUE_INTEGER <br/> + * GL_ALPHA GL_LUMINANCE (12) <br/> + * <br/> + * GL_LUMINANCE_ALPHA GL_RG <br/> + * GL_RG_INTEGER GL_HILO_NV <br/> + * GL_SIGNED_HILO_NV (5) <br/> + * <br/> + * GL_YCBCR_422_APPLE <br/> + * <br/> + * GL_RGB GL_RGB_INTEGER <br/> + * GL_BGR GL_BGR_INTEGER (4)<br/> + * <br/> + * GL_RGBA GL_RGBA_INTEGER <br/> + * GL_BGRA GL_BGRA_INTEGER <br/> + * GL_ABGR_EXT (5)<br/> + * + * @param type must be one of (32) <br/> + * GL_BITMAP, <br/> + * GL_BYTE, GL_UNSIGNED_BYTE, <br/> + * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, <br/> + * <br/> + * GL_SHORT, GL_UNSIGNED_SHORT, <br/> + * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, <br/> + * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, <br/> + * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, <br/> + * GL_UNSIGNED_SHORT_8_8_APPLE, GL_UNSIGNED_SHORT_8_8_REV_APPLE, <br/> + * GL_HALF_FLOAT, GL_HALF_FLOAT_OES <br/> + * <br/> + * GL_FIXED, GL_INT <br/> + * GL_UNSIGNED_INT, GL_UNSIGNED_INT_8_8_8_8, <br/> + * GL_UNSIGNED_INT_8_8_8_8_REV, GL_UNSIGNED_INT_10_10_10_2, <br/> + * GL_UNSIGNED_INT_2_10_10_10_REV, GL_UNSIGNED_INT_24_8, <br/> + * GL_UNSIGNED_INT_10F_11F_11F_REV, GL_UNSIGNED_INT_5_9_9_9_REV <br/> + * GL_HILO16_NV, GL_SIGNED_HILO16_NV <br/> + * <br/> + * GL_FLOAT_32_UNSIGNED_INT_24_8_REV <br/> + * <br/> + * GL_FLOAT, GL_DOUBLE <br/> + * + * @return required size of one pixel in bytes + * @throws GLException if format or type alignment is not handled. Please contact the maintainer if this is our bug. + */ + public static final int bytesPerPixel(final int format, final int type) throws GLException { + int compSize = 0; + + int compCount = componentCount(format); + switch (type) /* 30 */ { case GL2.GL_BITMAP: - if (GL2.GL_COLOR_INDEX == format || GL2GL3.GL_STENCIL_INDEX == format) { - return (depth * (height * ((width+7)/8))); + if (GL2.GL_COLOR_INDEX == format || GL2ES2.GL_STENCIL_INDEX == format) { + compSize = 1; + } else { + throw new GLException("BITMAP type only supported for format COLOR_INDEX and STENCIL_INDEX, not 0x"+Integer.toHexString(format)); } + break; case GL.GL_BYTE: - case GL.GL_UNSIGNED_BYTE: - esize = 1; + case GL.GL_UNSIGNED_BYTE: + compSize = 1; break; case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: case GL.GL_HALF_FLOAT: case GLES2.GL_HALF_FLOAT_OES: - esize = 2; + compSize = 2; break; case GL.GL_FIXED: case GL2ES2.GL_INT: case GL.GL_UNSIGNED_INT: case GL.GL_FLOAT: - esize = 4; + compSize = 4; break; case GL2GL3.GL_DOUBLE: - esize = 8; + compSize = 8; break; - + case GL2GL3.GL_UNSIGNED_BYTE_3_3_2: case GL2GL3.GL_UNSIGNED_BYTE_2_3_3_REV: - esize = 1; - elements = 1; + compSize = 1; + compCount = 1; break; case GL.GL_UNSIGNED_SHORT_5_6_5: case GL2GL3.GL_UNSIGNED_SHORT_5_6_5_REV: - case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4: + case GL.GL_UNSIGNED_SHORT_4_4_4_4: case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: - case GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1: + case GL.GL_UNSIGNED_SHORT_5_5_5_1: case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: - esize = 2; - elements = 1; + case GL2.GL_UNSIGNED_SHORT_8_8_APPLE: + case GL2.GL_UNSIGNED_SHORT_8_8_REV_APPLE: + compSize = 2; + compCount = 1; break; case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: - esize = 2; - elements = 2; - break; + compSize = 2; + compCount = 2; + break; case GL2GL3.GL_UNSIGNED_INT_8_8_8_8: case GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV: - case GL2GL3.GL_UNSIGNED_INT_10_10_10_2: - case GL2GL3.GL_UNSIGNED_INT_2_10_10_10_REV: - case GL2GL3.GL_UNSIGNED_INT_24_8: - case GL2GL3.GL_UNSIGNED_INT_10F_11F_11F_REV: - case GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV: - esize = 4; - elements = 1; - break; - case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - esize = 8; - elements = 1; - break; - + case GL2ES2.GL_UNSIGNED_INT_10_10_10_2: + case GL2ES2.GL_UNSIGNED_INT_2_10_10_10_REV: + case GL.GL_UNSIGNED_INT_24_8: + case GL.GL_UNSIGNED_INT_10F_11F_11F_REV: + case GL2ES3.GL_UNSIGNED_INT_5_9_9_9_REV: + compSize = 4; + compCount = 1; + break; + case GL2ES3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + compSize = 8; + compCount = 1; + break; + default: throw new GLException("type 0x"+Integer.toHexString(type)+"/"+"format 0x"+Integer.toHexString(format)+" not supported [yet], pls notify the maintainer in case this is our bug."); } - - return sizeof(gl, tmp, elements * esize, width, height, depth, pack); + return compCount * compSize; + } + + /** + * Returns the number of components required for the given OpenGL format. + * + * <p>This method is security critical, hence it throws an exception (fail-fast) + * in case either the format, type or alignment is unhandled. In case we forgot to handle + * proper values, please contact the maintainer.</p> + * + * @param format must be one of (27) <br/> + * GL_COLOR_INDEX GL_STENCIL_INDEX <br/> + * GL_DEPTH_COMPONENT GL_DEPTH_STENCIL <br/> + * GL_RED GL_RED_INTEGER <br/> + * GL_GREEN GL_GREEN_INTEGER <br/> + * GL_BLUE GL_BLUE_INTEGER <br/> + * GL_ALPHA GL_LUMINANCE (12) <br/> + * <br/> + * GL_LUMINANCE_ALPHA GL_RG <br/> + * GL_RG_INTEGER GL_HILO_NV <br/> + * GL_SIGNED_HILO_NV (5) <br/> + * <br/> + * GL_YCBCR_422_APPLE <br/> + * <br/> + * GL_RGB GL_RGB_INTEGER <br/> + * GL_BGR GL_BGR_INTEGER (4)<br/> + * <br/> + * GL_RGBA GL_RGBA_INTEGER <br/> + * GL_BGRA GL_BGRA_INTEGER <br/> + * GL_ABGR_EXT (5)<br/> + * + * @return number of components required for the given OpenGL format + * @throws GLException if format is not handled. Please contact the maintainer if this is our bug. + */ + public static final int componentCount(final int format) throws GLException { + final int compCount; + + switch (format) /* 26 */ { + case GL2.GL_COLOR_INDEX: + case GL2ES2.GL_STENCIL_INDEX: + case GL2ES2.GL_DEPTH_COMPONENT: + case GL.GL_DEPTH_STENCIL: + case GL2ES2.GL_RED: + case GL2ES3.GL_RED_INTEGER: + case GL2ES3.GL_GREEN: + case GL2GL3.GL_GREEN_INTEGER: + case GL2ES3.GL_BLUE: + case GL2GL3.GL_BLUE_INTEGER: + case GL.GL_ALPHA: + case GL.GL_LUMINANCE: + compCount = 1; + break; + case GL.GL_LUMINANCE_ALPHA: + case GL2ES2.GL_RG: + case GL2ES3.GL_RG_INTEGER: + case GL2.GL_HILO_NV: + case GL2.GL_SIGNED_HILO_NV: + compCount = 2; + break; + case GL.GL_RGB: + case GL2ES3.GL_RGB_INTEGER: + case GL2GL3.GL_BGR: + case GL2GL3.GL_BGR_INTEGER: + compCount = 3; + break; + case GL2.GL_YCBCR_422_APPLE: + compCount = 3; + break; + case GL.GL_RGBA: + case GL2ES3.GL_RGBA_INTEGER: + case GL.GL_BGRA: + case GL2GL3.GL_BGRA_INTEGER: + case GL2.GL_ABGR_EXT: + compCount = 4; + break; + /* FIXME ?? + case GL.GL_HILO_NV: + elements = 2; + break; */ + default: + throw new GLException("format 0x"+Integer.toHexString(format)+" not supported [yet], pls notify the maintainer in case this is our bug."); + } + return compCount; } - + public static final int getNextPowerOf2(int number) { if (((number-1) & number) == 0) { //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0 @@ -574,14 +752,14 @@ public class GLBuffers extends Buffers { power++; } return (1<<power); - } - + } + //---------------------------------------------------------------------- // Conversion routines // - public final static float[] getFloatArray(double[] source) { + public final static float[] getFloatArray(final double[] source) { int i = source.length; - float[] dest = new float[i--]; + final float[] dest = new float[i--]; while (i >= 0) { dest[i] = (float) source[i]; i--; diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java new file mode 100644 index 000000000..634cfeaed --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -0,0 +1,346 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLBase; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable; +import javax.media.opengl.Threading; + +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.opengl.GLEventListenerState; + +import jogamp.opengl.Debug; + +/** + * Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}. + */ +public class GLDrawableUtil { + protected static final boolean DEBUG = Debug.debug("GLDrawable"); + + public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; + } + + public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; + } + + /** + * {@link GLRunnable} to issue {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * returning <code>true</code> on {@link GLRunnable#run(GLAutoDrawable)}. + */ + public static class ReshapeGLEventListener implements GLRunnable { + private final GLEventListener listener; + private final boolean displayAfterReshape; + /** + * + * @param listener + * @param displayAfterReshape <code>true</code> to issue {@link GLEventListener#display(GLAutoDrawable)} + * after {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * otherwise false. + */ + public ReshapeGLEventListener(final GLEventListener listener, final boolean displayAfterReshape) { + this.listener = listener; + this.displayAfterReshape = displayAfterReshape; + } + @Override + public boolean run(final GLAutoDrawable drawable) { + listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + if( displayAfterReshape ) { + listener.display(drawable); + } + return true; + } + } + + /** + * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) { + final boolean initialized = src.getGLEventListenerInitState(listener); + if( preserveInitState ) { + src.removeGLEventListener(listener); + dest.addGLEventListener(listener); + if( initialized ) { + dest.setGLEventListenerInitState(listener, true); + dest.invoke(false, new ReshapeGLEventListener(listener, true)); + } + } else { + src.disposeGLEventListener(listener, true); + dest.addGLEventListener(listener); + } + } + + /** + * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) { + for(int count = src.getGLEventListenerCount(); 0<count; count--) { + final GLEventListener listener = src.getGLEventListener(0); + moveGLEventListener(src, dest, listener, preserveInitState); + } + } + + /** + * Return a heuristic value whether switching the {@link GLContext} is safe between {@link GLAutoDrawable}s, + * i.e. via {@link #swapGLContext(GLAutoDrawable, GLAutoDrawable)} or {@link #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}. + * <p> + * Method currently returns <code>false</code> if: + * <ul> + * <li>Switching between on- and offscreen and one of the following is <code>true</code>: + * <ul> + * <li>{@link GLCapabilitiesImmutable#getSampleBuffers() MSAA is <i>used</i>} [1] in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getStereo() Stereo is <i>used</i>} in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getAccumAlphaBits() Accumulator Buffer is <i>requested</i>} [2] in <code>requestedCaps</code></li> + * </ul></li> + * </ul> + * Otherwise method returns <code>true</code> + * </p> + * <pre> + * [1] See Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX + * On NVidia GPUs w/ it's proprietary driver context swapping does not work if MSAA is involved + * and when swapping on- to offscreen. + * </pre> + * <pre> + * [2] On AMD GPUs w/ it's proprietary driver, requesting an accumulator buffer leads to receive an accumulator buffer configuration, + * for which context swapping does not work when swapping on- to offscreen and vice-versa, i.e. cannot make context current. + * With AMD and Mesa drivers we only receive an accumulator buffer if requested, + * where on NVidia drivers all configurations contain the accumulator buffer. + * On both drivers, NVidia and Mesa, context swapping with accumulator buffer works. + * </pre> + * @param requestedCaps requested {@link GLCapabilitiesImmutable} which are intended for usage by both {@link GLAutoDrawable}s A and B + * @param chosenCapsA chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} A, which {@link GLContext} is intended to be swapped + * @param chosenCapsB chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} B, which {@link GLContext} is intended to be swapped + * @see #swapGLContext(GLAutoDrawable, GLAutoDrawable) + * @see #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable) + */ + public static boolean isSwapGLContextSafe(final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesImmutable chosenCapsA, final GLCapabilitiesImmutable chosenCapsB) { + final boolean usingAccumulatorBuffer = requestedCaps.getAccumAlphaBits() > 0 || + requestedCaps.getAccumRedBits() > 0 || + requestedCaps.getAccumGreenBits() > 0 || + requestedCaps.getAccumBlueBits() > 0; + if( ( chosenCapsA.isOnscreen() && !chosenCapsB.isOnscreen() || !chosenCapsA.isOnscreen() && chosenCapsB.isOnscreen() ) && // switching between on- and offscreen + ( + ( chosenCapsA.getSampleBuffers() || chosenCapsB.getSampleBuffers() ) || // MSAA involved + ( chosenCapsA.getStereo() || chosenCapsB.getStereo() ) || // Stereo involved + usingAccumulatorBuffer // Using accumulator buffer + ) + ) + { + return false; + } else { + return true; + } + } + /** + * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>, + * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}. + * <p> + * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association + * is also swapped. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code> + * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a, true); + final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b, true); + final Runnable gllsAUnlockOp = gllsA.getUnlockSurfaceOp(); + final Runnable gllsBUnlockOp = gllsB.getUnlockSurfaceOp(); + try { + gllsA.moveTo(b, gllsBUnlockOp); + gllsB.moveTo(a, gllsAUnlockOp); + } finally { + // guarantee unlock in case of an exception + gllsBUnlockOp.run(); + gllsAUnlockOp.run(); + } + } + + /** + * Swaps the {@link GLContext} of given {@link GLAutoDrawable} + * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} + * each {@link GLEventListener} w/o removing it. + * <p> + * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @param a + * @param b + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContext(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLAnimatorControl aAnim = a.getAnimator(); + final GLAnimatorControl bAnim = b.getAnimator(); + final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); + final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); + + final RecursiveLock aUpstreamLock = a.getUpstreamLock(); + final RecursiveLock bUpstreamLock = b.getUpstreamLock(); + aUpstreamLock.lock(); + bUpstreamLock.lock(); + try { + final NativeSurface aSurface = a.getNativeSurface(); + final boolean aSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + if( a.isRealized() && !aSurfaceLocked ) { + throw new GLException("Could not lock realized a surface "+a); + } + final NativeSurface bSurface = b.getNativeSurface(); + final boolean bSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < bSurface.lockSurface(); + if( b.isRealized() && !bSurfaceLocked ) { + throw new GLException("Could not lock realized b surface "+b); + } + try { + for(int i = a.getGLEventListenerCount() - 1; 0 <= i; i--) { + a.disposeGLEventListener(a.getGLEventListener(i), false); + } + for(int i = b.getGLEventListenerCount() - 1; 0 <= i; i--) { + b.disposeGLEventListener(b.getGLEventListener(i), false); + } + b.setContext( a.setContext( b.getContext(), false ), false ); + + } finally { + if( bSurfaceLocked ) { + bSurface.unlockSurface(); + } + if( aSurfaceLocked ) { + aSurface.unlockSurface(); + } + } + } finally { + bUpstreamLock.unlock(); + aUpstreamLock.unlock(); + } + a.invoke(true, setViewport); + b.invoke(true, setViewport); + if(aIsPaused) { aAnim.resume(); } + if(bIsPaused) { bAnim.resume(); } + } + + private static final GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + return false; // issue re-display w/ new viewport! + } + }; + + /** + * Determines whether the chosen {@link GLCapabilitiesImmutable} + * requires a {@link GLDrawable#swapBuffers() swap-buffers} + * before reading pixels. + * <p> + * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer} + * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. + * </p> + * <p> + * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s + * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> + * the multisamples into the readable sampling sink. + * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. + * </p> + * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() + */ + public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) { + return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java new file mode 100644 index 000000000..a09321d75 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java @@ -0,0 +1,487 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import javax.media.nativewindow.util.PixelFormat; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.texture.TextureData; + +/** + * OpenGL pixel data buffer, allowing user to provide buffers via their {@link GLPixelBufferProvider} implementation. + * <p> + * {@link GLPixelBufferProvider} produces a {@link GLPixelBuffer}. + * </p> + * <p> + * You may use {@link #defaultProviderNoRowStride}. + * </p> + */ +public class GLPixelBuffer { + + /** Allows user to interface with another toolkit to define {@link GLPixelAttributes} and memory buffer to produce {@link TextureData}. */ + public static interface GLPixelBufferProvider { + /** Allow {@link GL2ES3#GL_PACK_ROW_LENGTH}, or {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. */ + boolean getAllowRowStride(); + + /** Called first to determine {@link GLPixelAttributes}. */ + GLPixelAttributes getAttributes(GL gl, int componentCount); + + /** + * Allocates a new {@link GLPixelBuffer} object. + * <p> + * Being called to gather the initial {@link GLPixelBuffer}, + * or a new replacement {@link GLPixelBuffer} if {@link GLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + * </p> + * <p> + * The minimum required {@link Buffer#remaining() remaining} byte size equals to <code>minByteSize</code>, if > 0, + * otherwise utilize {@link GLBuffers#sizeof(GL, int[], int, int, int, int, int, boolean)} + * to calculate it. + * </p> + * + * @param gl the corresponding current GL context object + * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param width in pixels + * @param height in pixels + * @param depth in pixels + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param minByteSize if > 0, the pre-calculated minimum byte-size for the resulting buffer, otherwise ignore. + */ + GLPixelBuffer allocate(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize); + } + + /** Single {@link GLPixelBuffer} provider. */ + public static interface SingletonGLPixelBufferProvider extends GLPixelBufferProvider { + /** Return the last {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated} {@link GLPixelBuffer} w/ {@link GLPixelAttributes#componentCount}. */ + GLPixelBuffer getSingleBuffer(GLPixelAttributes pixelAttributes); + /** + * Initializes the single {@link GLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * @return the newly initialized single {@link GLPixelBuffer}, or null if already allocated. + */ + GLPixelBuffer initSingleton(int componentCount, int width, int height, int depth, boolean pack); + } + + public static class DefaultGLPixelBufferProvider implements GLPixelBufferProvider { + private final boolean allowRowStride; + + /** + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. + * See {@link #getAllowRowStride()} and {@link GLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + */ + public DefaultGLPixelBufferProvider(final boolean allowRowStride) { + this.allowRowStride = allowRowStride; + } + + @Override + public boolean getAllowRowStride() { return allowRowStride; } + + @Override + public GLPixelAttributes getAttributes(final GL gl, final int componentCount) { + final GLContext ctx = gl.getContext(); + final int dFormat, dType; + + if( 1 == componentCount ) { + if( gl.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA is deprecated on core + dFormat = GL2ES2.GL_RED; + } else { + // ALPHA is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + dFormat = GL.GL_ALPHA; + } + dType = GL.GL_UNSIGNED_BYTE; + } else if( 3 == componentCount ) { + dFormat = GL.GL_RGB; + dType = GL.GL_UNSIGNED_BYTE; + } else if( 4 == componentCount ) { + final int _dFormat = ctx.getDefaultPixelDataFormat(); + final int dComps = GLBuffers.componentCount(_dFormat); + if( dComps == componentCount ) { + dFormat = _dFormat; + dType = ctx.getDefaultPixelDataType(); + } else { + dFormat = GL.GL_RGBA; + dType = GL.GL_UNSIGNED_BYTE; + } + } else { + throw new GLException("Unsupported componentCount "+componentCount+", contact maintainer to enhance"); + } + return new GLPixelAttributes(componentCount, dFormat, dType); + } + + /** + * {@inheritDoc} + * <p> + * Returns an NIO {@link ByteBuffer}. + * </p> + */ + @Override + public GLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { + if( minByteSize > 0 ) { + return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(minByteSize), getAllowRowStride()); + } else { + final int[] tmp = { 0 }; + final int byteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.bytesPerPixel, width, height, depth, pack); + return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(byteSize), getAllowRowStride()); + } + } + } + + /** + * Default {@link GLPixelBufferProvider} with {@link GLPixelBufferProvider#getAllowRowStride()} == <code>false</code>, + * utilizing best match for {@link GLPixelAttributes} + * and {@link GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocating} a {@link ByteBuffer}. + */ + public static final GLPixelBufferProvider defaultProviderNoRowStride = new DefaultGLPixelBufferProvider(false); + + /** + * Default {@link GLPixelBufferProvider} with {@link GLPixelBufferProvider#getAllowRowStride()} == <code>true</code>, + * utilizing best match for {@link GLPixelAttributes} + * and {@link GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocating} a {@link ByteBuffer}. + */ + public static final GLPixelBufferProvider defaultProviderWithRowStride = new DefaultGLPixelBufferProvider(true); + + /** Pixel attributes. */ + public static class GLPixelAttributes { + /** Undefined instance of {@link GLPixelAttributes}, having componentCount:=0, format:=0 and type:= 0. */ + public static final GLPixelAttributes UNDEF = new GLPixelAttributes(0, 0, 0, false); + + /** Pixel <i>source</i> component count, i.e. number of meaningful components. */ + public final int componentCount; + /** The OpenGL pixel data format */ + public final int format; + /** The OpenGL pixel data type */ + public final int type; + /** The OpenGL pixel size in bytes */ + public final int bytesPerPixel; + + /** + * Deriving {@link #componentCount} via GL <code>dataFormat</code>, i.e. {@link GLBuffers#componentCount(int)} if > 0. + * @param dataFormat GL data format + * @param dataType GL data type + */ + public GLPixelAttributes(final int dataFormat, final int dataType) { + this(0 < dataFormat ? GLBuffers.componentCount(dataFormat) : 0, dataFormat, dataType); + } + /** + * Using user specified source {@link #componentCount}. + * @param componentCount source component count + * @param dataFormat GL data format + * @param dataType GL data type + */ + public GLPixelAttributes(final int componentCount, final int dataFormat, final int dataType) { + this(componentCount, dataFormat, dataType, true); + } + + /** + * Returns the matching {@link GLPixelAttributes} for the given {@link PixelFormat} and {@link GLProfile} if exists, + * otherwise returns <code>null</code>. + */ + public static final GLPixelAttributes convert(final PixelFormat pixFmt, final GLProfile glp) { + int df = 0; // format + int dt = GL.GL_UNSIGNED_BYTE; // data type + switch(pixFmt) { + case LUMINANCE: + if( glp.isGL3ES3() ) { + // RED is supported on ES3 and >= GL3 [core]; ALPHA/LUMINANCE is deprecated on core + df = GL2ES2.GL_RED; + } else { + // ALPHA/LUMINANCE is supported on ES2 and GL2, i.e. <= GL3 [core] or compatibility + df = GL.GL_LUMINANCE; + } + break; + case BGR888: + if( glp.isGL2GL3() ) { + df = GL2GL3.GL_BGR; + } + break; + case RGB888: + df = GL.GL_RGB; + break; + case RGBA8888: + df = GL.GL_RGBA; + break; + case ABGR8888: + if( glp.isGL2GL3() ) { + df = GL.GL_RGBA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; + } + break; + case BGRA8888: + df = GL.GL_BGRA; + break; + case ARGB8888: + if( glp.isGL2GL3() ) { + df = GL.GL_BGRA; dt = GL2GL3.GL_UNSIGNED_INT_8_8_8_8; + } + break; + default: + break; + } + if( 0 != df ) { + return new GLPixelAttributes(pixFmt.componentCount, df, dt, true); + } + return null; + } + private GLPixelAttributes(final int componentCount, final int dataFormat, final int dataType, final boolean checkArgs) { + this.componentCount = componentCount; + this.format = dataFormat; + this.type = dataType; + this.bytesPerPixel = ( 0 < dataFormat && 0 < dataType ) ? GLBuffers.bytesPerPixel(dataFormat, dataType) : 0; + if( checkArgs ) { + if( 0 == componentCount || 0 == format || 0 == type ) { + throw new GLException("Zero components, format and/or type: "+this); + } + if( 0 == bytesPerPixel ) { + throw new GLException("Zero bytesPerPixel: "+this); + } + } + } + + /** + * Returns the matching {@link PixelFormat} of this {@link GLPixelAttributes} if exists, + * otherwise returns <code>null</code>. + */ + public final PixelFormat getPixelFormat() { + final PixelFormat pixFmt; + // FIXME: Take 'type' into consideration and complete mapping! + switch(format) { + case GL.GL_ALPHA: + case GL.GL_LUMINANCE: + case GL2ES2.GL_RED: + pixFmt = PixelFormat.LUMINANCE; + break; + case GL.GL_RGB: + pixFmt = PixelFormat.RGB888; + break; + case GL.GL_RGBA: + pixFmt = PixelFormat.RGBA8888; + break; + case GL2GL3.GL_BGR: + pixFmt = PixelFormat.BGR888; + break; + case GL.GL_BGRA: + pixFmt = PixelFormat.BGRA8888; + break; + default: + switch( bytesPerPixel ) { + case 1: + pixFmt = PixelFormat.LUMINANCE; + break; + case 3: + pixFmt = PixelFormat.RGB888; + break; + case 4: + pixFmt = PixelFormat.RGBA8888; + break; + default: + pixFmt = null; + break; + } + break; + } + return pixFmt; + } + + @Override + public String toString() { + return "PixelAttributes[comp "+componentCount+", fmt 0x"+Integer.toHexString(format)+", type 0x"+Integer.toHexString(type)+", bytesPerPixel "+bytesPerPixel+"]"; + } + } + + /** The {@link GLPixelAttributes}. */ + public final GLPixelAttributes pixelAttributes; + /** + * Width in pixels, representing {@link #buffer}'s {@link #byteSize}. + * <p> + * May not represent actual image width as user may re-use buffer for different dimensions, see {@link #requiresNewBuffer(GL, int, int, int)}. + * </p> + */ + public final int width; + /** + * Height in pixels, representing {@link #buffer}'s {@link #byteSize}. + * <p> + * May not represent actual image height as user may re-use buffer for different dimensions, see {@link #requiresNewBuffer(GL, int, int, int)}. + * </p> + */ + public final int height; + /** Depth in pixels. */ + public final int depth; + /** Data packing direction. If <code>true</code> for read mode GPU -> CPU, <code>false</code> for write mode CPU -> GPU. */ + public final boolean pack; + /** Byte size of the buffer. Actually the number of {@link Buffer#remaining()} bytes when passed in ctor. */ + public final int byteSize; + /** + * Buffer holding the pixel data. If {@link #rewind()}, it holds <code>byteSize</code> {@link Buffer#remaining()} bytes. + * <p> + * By default the {@link Buffer} is a {@link ByteBuffer}, due to {@link DefProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int)}. + * However, other {@link GLPixelBufferProvider} may utilize different {@link Buffer} types. + * </p> + */ + public final Buffer buffer; + /** Buffer element size in bytes. */ + public final int bufferElemSize; + + /** Allow {@link GL2ES3#GL_PACK_ROW_LENGTH}, or {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. See {@link #requiresNewBuffer(GL, int, int, int)}. */ + public final boolean allowRowStride; + + private boolean disposed = false; + + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append(pixelAttributes).append(", dim ").append(width).append("x").append(height).append("x").append(depth).append(", pack ").append(pack) + .append(", disposed ").append(disposed).append(", valid ").append(isValid()) + .append(", buffer[bytes ").append(byteSize).append(", elemSize ").append(bufferElemSize).append(", ").append(buffer).append("]"); + return sb; + } + @Override + public String toString() { + return "GLPixelBuffer["+toString(null).toString()+"]"; + } + + /** + * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param width in pixels + * @param height in pixels + * @param depth in pixels + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param buffer the backing array + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link #requiresNewBuffer(GL, int, int, int)}. + */ + public GLPixelBuffer(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final Buffer buffer, final boolean allowRowStride) { + this.pixelAttributes = pixelAttributes; + this.width = width; + this.height = height; + this.depth = depth; + this.pack = pack; + this.buffer = buffer; + this.byteSize = Buffers.remainingBytes(buffer); + this.bufferElemSize = Buffers.sizeOfBufferElem(buffer); + this.allowRowStride = allowRowStride; + } + + /** Allow {@link GL2ES3#GL_PACK_ROW_LENGTH}, or {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. */ + public final boolean getAllowRowStride() { return allowRowStride; } + + /** Is not {@link #dispose() disposed} and has {@link #byteSize} > 0. */ + public boolean isValid() { + return !disposed && 0 < byteSize; + } + + /** See {@link Buffer#rewind()}. */ + public Buffer rewind() { + return buffer.rewind(); + } + + /** Returns the byte position of the {@link #buffer}. */ + public int position() { + return buffer.position() * bufferElemSize; + } + + /** Sets the byte position of the {@link #buffer}. */ + public Buffer position(final int bytePos) { + return buffer.position( bytePos / bufferElemSize ); + } + + /** Returns the byte capacity of the {@link #buffer}. */ + public int capacity() { + return buffer.capacity() * bufferElemSize; + } + + /** Returns the byte limit of the {@link #buffer}. */ + public int limit() { + return buffer.limit() * bufferElemSize; + } + + /** See {@link Buffer#flip()}. */ + public Buffer flip() { + return buffer.flip(); + } + + /** See {@link Buffer#clear()}. */ + public Buffer clear() { + return buffer.clear(); + } + + /** + * Returns true, if {@link #isValid() invalid} or implementation requires a new buffer based on the new size + * due to pixel alignment or byte size, otherwise false. + * <p> + * It is assumed that <code>pixelAttributes</code>, <code>depth</code> and <code>pack</code> stays the same! + * </p> + * <p> + * The minimum required byte size equals to <code>minByteSize</code>, if > 0, + * otherwise {@link GLBuffers#sizeof(GL, int[], int, int, int, int, int, boolean) GLBuffers.sizeof(..)} + * is being used to calculate it. This value is referred to <i>newByteSize</i>. + * </p> + * <p> + * If <code>{@link #allowRowStride} = false</code>, + * method returns <code>true</code> if the <i>newByteSize</i> > <i>currentByteSize</i> + * or the <code>newWidth</code> != <code>currentWidth</code>. + * </p> + * <p> + * If <code>{@link #allowRowStride} = true</code>, see {@link GLPixelBufferProvider#getAllowRowStride()}, + * method returns <code>true</code> only if the <i>newByteSize</i> > <i>currentByteSize</i>. + * Assuming user utilizes the row-stride when dealing w/ the data, i.e. {@link GL2ES3#GL_PACK_ROW_LENGTH}. + * </p> + * @param gl the corresponding current GL context object + * @param newWidth new width in pixels + * @param newHeight new height in pixels + * @param newByteSize if > 0, the pre-calculated minimum byte-size for the resulting buffer, otherwise ignore. + * @see GLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) + */ + public boolean requiresNewBuffer(final GL gl, final int newWidth, final int newHeight, int newByteSize) { + if( !isValid() ) { + return true; + } + if( 0 >= newByteSize ) { + final int[] tmp = { 0 }; + newByteSize = GLBuffers.sizeof(gl, tmp, pixelAttributes.bytesPerPixel, newWidth, newHeight, 1, true); + } + if( allowRowStride ) { + return byteSize < newByteSize; + } + return byteSize < newByteSize || width != newWidth; + } + + /** Dispose resources. See {@link #isValid()}. */ + public void dispose() { + disposed = true; + buffer.clear(); + } +} + diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java index 05eb67269..290033e99 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java @@ -31,106 +31,290 @@ package com.jogamp.opengl.util; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; /** - * Utility to safely set and restore the pack and unpack pixel storage mode, + * Utility to safely set and restore the PACK and UNPACK pixel storage mode, * regardless of the GLProfile. + * <p> + * PACK for GPU to CPU transfers, e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) ReadPixels}, etc. + * </p> + * <p> + * UNPACK for CPU o GPU transfers, e.g. {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long) TexImage2D}, etc + * </p> */ public class GLPixelStorageModes { - private int[] savedGL2GL3Modes = new int[8]; - private int[] savedAlignment = new int[2]; - private boolean saved = false; + private final int[] cachePack = new int[8]; + private final int[] cacheUnpack = new int[8]; + private boolean savedPack = false; + private boolean savedUnpack = false; + + /** Create instance w/o {@link #saveAll(GL)} */ + public GLPixelStorageModes() {} + + /** Create instance w/ {@link #saveAll(GL)} */ + public GLPixelStorageModes(final GL gl) { saveAll(gl); } /** - * Sets the {@link GL2ES2.GL_PACK_ALIGNMENT}. Saves the pixel storage modes if not saved yet. + * Sets the {@link GL#GL_PACK_ALIGNMENT}. + * <p> + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. + * </p> */ - public final void setPackAlignment(GL gl, int packAlignment) { - if(!saved) { save(gl); } - gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, packAlignment); + public final void setPackAlignment(final GL gl, final int packAlignment) { + savePack(gl); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment); } /** - * Sets the {@link GL2ES2.GL_UNPACK_ALIGNMENT}. Saves the pixel storage modes if not saved yet. + * Sets the {@link GL#GL_UNPACK_ALIGNMENT}. + * <p> + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. + * </p> */ - public final void setUnpackAlignment(GL gl, int unpackAlignment) { - if(!saved) { save(gl); } - gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, unpackAlignment); + public final void setUnpackAlignment(final GL gl, final int unpackAlignment) { + saveUnpack(gl); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, unpackAlignment); } - + /** - * Sets the {@link GL2ES2.GL_PACK_ALIGNMENT} and {@link GL2ES2.GL_UNPACK_ALIGNMENT}. - * Saves the pixel storage modes if not saved yet. + * Sets the {@link GL#GL_PACK_ALIGNMENT} and {@link GL#GL_UNPACK_ALIGNMENT}. + * <p> + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. + * </p> */ - public final void setAlignment(GL gl, int packAlignment, int unpackAlignment) { + public final void setAlignment(final GL gl, final int packAlignment, final int unpackAlignment) { setPackAlignment(gl, packAlignment); setUnpackAlignment(gl, unpackAlignment); } - - private final void save(GL gl) { - if(gl.isGL2GL3()) { - if(gl.isGL2()) { - gl.getGL2().glPushClientAttrib(GL2.GL_CLIENT_PIXEL_STORE_BIT); - } else { - gl.glGetIntegerv(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment, 0); - gl.glGetIntegerv(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment, 1); - gl.glGetIntegerv(GL2GL3.GL_PACK_ROW_LENGTH, savedGL2GL3Modes, 0); - gl.glGetIntegerv(GL2GL3.GL_PACK_SKIP_ROWS, savedGL2GL3Modes, 1); - gl.glGetIntegerv(GL2GL3.GL_PACK_SKIP_PIXELS, savedGL2GL3Modes, 2); - gl.glGetIntegerv(GL2GL3.GL_PACK_SWAP_BYTES, savedGL2GL3Modes, 3); - gl.glGetIntegerv(GL2GL3.GL_UNPACK_ROW_LENGTH, savedGL2GL3Modes, 4); - gl.glGetIntegerv(GL2GL3.GL_UNPACK_SKIP_ROWS, savedGL2GL3Modes, 5); - gl.glGetIntegerv(GL2GL3.GL_UNPACK_SKIP_PIXELS, savedGL2GL3Modes, 6); - gl.glGetIntegerv(GL2GL3.GL_UNPACK_SWAP_BYTES, savedGL2GL3Modes, 7); + + /** + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH}. + * <p> + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. + * </p> + */ + public final void setPackRowLength(final GL2ES3 gl, final int packRowLength) { + savePack(gl); + gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, packRowLength); + } + + /** + * Sets the {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. + * <p> + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. + * </p> + */ + public final void setUnpackRowLength(final GL2ES3 gl, final int unpackRowLength) { + saveUnpack(gl); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, unpackRowLength); + } + + /** + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH} if {@link GL#isGL2ES3()}. + * <p> + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. + * </p> + */ + public final void setRowLength(final GL2ES3 gl, final int packRowLength, final int unpackRowLength) { + setPackRowLength(gl, packRowLength); + setUnpackRowLength(gl, unpackRowLength); + } + + /** + * Saves PACK and UNPACK pixel storage modes and {@link #resetAll(GL) resets} them, + * i.e. issues {@link #savePack(GL)} and {@link #saveUnpack(GL)}. + * <p> + * Operation is skipped, if the modes were already saved. + * </p> + * <p> + * Restore via {@link #restore(GL)} + * </p> + */ + public final void saveAll(final GL gl) { + savePack(gl); + saveUnpack(gl); + } + + /** + * Resets PACK and UNPACK pixel storage modes to their default value, + * i.e. issues {@link #resetPack(GL)} and {@link #resetUnpack(GL)}. + */ + public final void resetAll(final GL gl) { + resetPack(gl); + resetUnpack(gl); + } + + /** + * Restores PACK and UNPACK pixel storage mode previously saved w/ {@link #saveAll(GL)} + * or {@link #savePack(GL)} and {@link #saveUnpack(GL)}. + * @throws GLException if neither PACK nor UNPACK modes were saved. + */ + public final void restore(final GL gl) throws GLException { + if(!savedPack && !savedUnpack) { + throw new GLException("Neither PACK nor UNPACK pixel storage modes were saved"); + } + if( savedPack ) { + restorePack(gl); + savedPack = false; + } + if( savedUnpack ) { + restoreUnpack(gl); + savedUnpack = false; + } + } + + /** + * Resets PACK pixel storage modes to their default value. + */ + public final void resetPack(final GL gl) { + // Compared w/ ES2, ES3 and GL3-core spec + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 4); // es2, es3, gl3 + if( gl.isGL2ES3() ) { + gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); // es3, gl3 + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); // es3, gl3 + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, 0); // es3, gl3 + if( gl.isGL2GL3() ) { + gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, GL.GL_FALSE); // gl3 + gl.glPixelStorei(GL2GL3.GL_PACK_LSB_FIRST, GL.GL_FALSE); // gl3 + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); // gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, 0); // gl3, GL_VERSION_1_2 + } } - gl.glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, 0); - gl.glPixelStorei(GL2GL3.GL_UNPACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, 0); + } + } + /** + * Saves PACK pixel storage modes and {@link #resetPack(GL) resets} them. + * <p> + * Operation is skipped, if the modes were already saved. + * </p> + * <p> + * Restore via {@link #restore(GL)} + * </p> + */ + public final void savePack(final GL gl) { + if(savedPack) { + return; + } + if( gl.isGL2() ) { + // See GLStateTracker.pushAttrib(GL2.GL_CLIENT_PIXEL_STORE_BIT) + gl.getGL2().glPushClientAttrib(GL2.GL_CLIENT_PIXEL_STORE_BIT); } else { - // embedded deals with pack/unpack alignment only - gl.glGetIntegerv(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment, 0); - gl.glGetIntegerv(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment, 1); - } - saved = true; + // ES1 or ES2 deals with pack/unpack alignment only + gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, cachePack, 0); + if( gl.isGL2ES3() ) { + gl.glGetIntegerv(GL2ES3.GL_PACK_ROW_LENGTH, cachePack, 1); + gl.glGetIntegerv(GL2ES3.GL_PACK_SKIP_ROWS, cachePack, 2); + gl.glGetIntegerv(GL2ES3.GL_PACK_SKIP_PIXELS, cachePack, 3); + if( gl.isGL2GL3() ) { + gl.glGetIntegerv(GL2GL3.GL_PACK_SWAP_BYTES, cachePack, 4); + gl.glGetIntegerv(GL2GL3.GL_PACK_LSB_FIRST, cachePack, 5); + gl.glGetIntegerv(GL2GL3.GL_PACK_IMAGE_HEIGHT, cachePack, 6); + gl.glGetIntegerv(GL2GL3.GL_PACK_SKIP_IMAGES, cachePack, 7); + } + } + } + savedPack = true; + resetPack(gl); + } + private final void restorePack(final GL gl) { + if( gl.isGL2() ) { + gl.getGL2().glPopClientAttrib(); + } else { + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, cachePack[0]); + if( gl.isGL2ES3() ) { + gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, cachePack[1]); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, cachePack[2]); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, cachePack[3]); + if( gl.isGL2GL3() ) { + gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, cachePack[4]); + gl.glPixelStorei(GL2GL3.GL_PACK_LSB_FIRST, cachePack[5]); + gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, cachePack[6]); + gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, cachePack[7]); + } + } + } } /** - * Restores the pixel storage mode. - * @throws GLException if not saved via one of the set methods. + * Resets UNPACK pixel storage modes to their default value. */ - public final void restore(GL gl) throws GLException { - if(!saved) { - throw new GLException("pixel storage modes not saved"); - } - - if(gl.isGL2GL3()) { - if(gl.isGL2()) { - gl.getGL2().glPopClientAttrib(); + public final void resetUnpack(final GL gl) { + // Compared w/ ES2, ES3 and GL3-core spec + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4); // es2, es3, gl3 + if( gl.isGL2ES3() ) { + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); // es3, gl3 + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); // es3, gl3 + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); // es3, gl3 + if( gl.isGL2GL3() ) { + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 + } + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, GL.GL_FALSE); // gl3 + gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, GL.GL_FALSE); // gl3 } else { - gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment[0]); - gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment[1]); - gl.glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, savedGL2GL3Modes[0]); - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_ROWS, savedGL2GL3Modes[1]); - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_PIXELS, savedGL2GL3Modes[2]); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, savedGL2GL3Modes[3]); - gl.glPixelStorei(GL2GL3.GL_UNPACK_ROW_LENGTH, savedGL2GL3Modes[4]); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_ROWS, savedGL2GL3Modes[5]); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_PIXELS, savedGL2GL3Modes[6]); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, savedGL2GL3Modes[7]); + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 + } + } + } + /** + * Saves UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them. + * <p> + * Operation is skipped, if the modes were already saved. + * </p> + * <p> + * Restore via {@link #restore(GL)} + * </p> + */ + public final void saveUnpack(final GL gl) { + if(savedUnpack) { + return; + } + if( gl.isGL2() ) { + // See GLStateTracker.pushAttrib(GL2.GL_CLIENT_PIXEL_STORE_BIT) + gl.getGL2().glPushClientAttrib(GL2.GL_CLIENT_PIXEL_STORE_BIT); + } else { + // ES1 or ES2 deals with pack/unpack alignment only + gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, cacheUnpack, 0); + if( gl.isGL2ES3() ) { + gl.glGetIntegerv(GL2ES2.GL_UNPACK_ROW_LENGTH, cacheUnpack, 1); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_ROWS, cacheUnpack, 2); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_PIXELS, cacheUnpack, 3); + gl.glGetIntegerv(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, cacheUnpack, 4); + gl.glGetIntegerv(GL2ES3.GL_UNPACK_SKIP_IMAGES, cacheUnpack, 5); + if( gl.isGL2GL3() ) { + gl.glGetIntegerv(GL2GL3.GL_UNPACK_SWAP_BYTES, cacheUnpack, 6); + gl.glGetIntegerv(GL2GL3.GL_UNPACK_LSB_FIRST, cacheUnpack, 7); + } } + } + savedUnpack = true; + resetUnpack(gl); + } + private final void restoreUnpack(final GL gl) { + if( gl.isGL2() ) { + gl.getGL2().glPopClientAttrib(); } else { - // embedded deals with pack/unpack alignment only - gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment[0]); - gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment[1]); - } - saved = false; - } + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, cacheUnpack[0]); + if( gl.isGL2ES3() ) { + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, cacheUnpack[1]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, cacheUnpack[2]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, cacheUnpack[3]); + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, cacheUnpack[4]); + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, cacheUnpack[5]); + if( gl.isGL2GL3() ) { + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, cacheUnpack[6]); + gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, cacheUnpack[7]); + } + } + } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index 34cc0eb07..e84a1d874 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,24 +20,29 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - -package com.jogamp.opengl.util; -import com.jogamp.common.nio.Buffers; +package com.jogamp.opengl.util; import java.io.File; import java.io.IOException; -import java.nio.*; -import javax.media.opengl.*; -import com.jogamp.opengl.GLExtensions; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLException; + +import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureData; +import com.jogamp.opengl.util.GLPixelBuffer; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelBufferProvider; import com.jogamp.opengl.util.texture.TextureIO; /** @@ -45,38 +50,46 @@ import com.jogamp.opengl.util.texture.TextureIO; * <p>May be used directly to write the TextureData to file (screenshot).</p> */ public class GLReadBufferUtil { - protected final int components, alignment; + protected final GLPixelBufferProvider pixelBufferProvider; + protected final int componentCount, alignment; protected final Texture readTexture; protected final GLPixelStorageModes psm; - - protected int readPixelSizeLast = 0; - protected ByteBuffer readPixelBuffer = null; + + protected GLPixelBuffer readPixelBuffer = null; protected TextureData readTextureData = null; /** - * @param alpha true for RGBA readPixels, otherwise RGB readPixels. Disclaimer: Alpha maybe forced on ES platforms! + * @param alpha true for RGBA readPixels, otherwise RGB readPixels. Disclaimer: Alpha maybe forced on ES platforms! * @param write2Texture true if readPixel's TextureData shall be written to a 2d Texture */ - public GLReadBufferUtil(boolean alpha, boolean write2Texture) { - components = alpha ? 4 : 3 ; - alignment = alpha ? 4 : 1 ; - readTexture = write2Texture ? new Texture(GL.GL_TEXTURE_2D) : null ; - psm = new GLPixelStorageModes(); + public GLReadBufferUtil(final boolean alpha, final boolean write2Texture) { + this(GLPixelBuffer.defaultProviderNoRowStride, alpha, write2Texture); } - + + public GLReadBufferUtil(final GLPixelBufferProvider pixelBufferProvider, final boolean alpha, final boolean write2Texture) { + this.pixelBufferProvider = pixelBufferProvider; + this.componentCount = alpha ? 4 : 3 ; + this.alignment = alpha ? 4 : 1 ; + this.readTexture = write2Texture ? new Texture(GL.GL_TEXTURE_2D) : null ; + this.psm = new GLPixelStorageModes(); + } + + /** Returns the {@link GLPixelBufferProvider} used by this instance. */ + public GLPixelBufferProvider getPixelBufferProvider() { return pixelBufferProvider; } + public boolean isValid() { - return null!=readTextureData && null!=readPixelBuffer ; + return null!=readTextureData && null!=readPixelBuffer && readPixelBuffer.isValid(); } - - public boolean hasAlpha() { return 4 == components ? true : false ; } - + + public boolean hasAlpha() { return 4 == componentCount ? true : false ; } + public GLPixelStorageModes getGLPixelStorageModes() { return psm; } - + /** - * @return the raw pixel ByteBuffer, filled by {@link #readPixels(GLAutoDrawable, boolean)} + * Returns the {@link GLPixelBuffer}, created and filled by {@link #readPixels(GLAutoDrawable, boolean)}. */ - public ByteBuffer getPixelBuffer() { return readPixelBuffer; } - + public GLPixelBuffer getPixelBuffer() { return readPixelBuffer; } + /** * rewind the raw pixel ByteBuffer */ @@ -86,7 +99,7 @@ public class GLReadBufferUtil { * @return the resulting TextureData, filled by {@link #readPixels(GLAutoDrawable, boolean)} */ public TextureData getTextureData() { return readTextureData; } - + /** * @return the Texture object filled by {@link #readPixels(GLAutoDrawable, boolean)}, * if this instance writes to a 2d Texture, otherwise null. @@ -97,108 +110,144 @@ public class GLReadBufferUtil { /** * Write the TextureData filled by {@link #readPixels(GLAutoDrawable, boolean)} to file */ - public void write(File dest) { + public void write(final File dest) { try { TextureIO.write(readTextureData, dest); rewindPixelBuffer(); - } catch (IOException ex) { + } catch (final IOException ex) { throw new RuntimeException("can not write to file: " + dest.getAbsolutePath(), ex); } } /** - * Read the drawable's pixels to TextureData and Texture, if requested at construction - * + * Read the drawable's pixels to TextureData and Texture, if requested at construction. + * * @param gl the current GL context object. It's read drawable is being used as the pixel source. - * @param drawable the drawable to read from - * @param flip weather to flip the data vertically or not - * + * @param mustFlipVertically indicates whether to flip the data vertically or not. + * The context's drawable {@link GLDrawable#isGLOriented()} state + * is taken into account. + * Vertical flipping is propagated to TextureData + * and handled in a efficient manner there (TextureCoordinates and TextureIO writer). + * * @see #GLReadBufferUtil(boolean, boolean) */ - public boolean readPixels(GL gl, boolean flip) { + public boolean readPixels(final GL gl, final boolean mustFlipVertically) { + return readPixels(gl, 0, 0, 0, 0, mustFlipVertically); + } + + /** + * Read the drawable's pixels to TextureData and Texture, if requested at construction. + * + * @param gl the current GL context object. It's read drawable is being used as the pixel source. + * @param inX readPixel x offset + * @param inY readPixel y offset + * @param inWidth optional readPixel width value, used if [1 .. drawable.width], otherwise using drawable.width + * @param inHeight optional readPixel height, used if [1 .. drawable.height], otherwise using drawable.height + * @param mustFlipVertically indicates whether to flip the data vertically or not. + * The context's drawable {@link GLDrawable#isGLOriented()} state + * is taken into account. + * Vertical flipping is propagated to TextureData + * and handled in a efficient manner there (TextureCoordinates and TextureIO writer). + * @see #GLReadBufferUtil(boolean, boolean) + */ + public boolean readPixels(final GL gl, final int inX, final int inY, final int inWidth, final int inHeight, final boolean mustFlipVertically) { + final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + final int width, height; + if( 0 >= inWidth || drawable.getSurfaceWidth() < inWidth ) { + width = drawable.getSurfaceWidth(); + } else { + width = inWidth; + } + if( 0 >= inHeight || drawable.getSurfaceHeight() < inHeight ) { + height = drawable.getSurfaceHeight(); + } else { + height= inHeight; + } + return readPixelsImpl(drawable, gl, inX, inY, width, height, mustFlipVertically); + } + + protected boolean readPixelsImpl(final GLDrawable drawable, final GL gl, + final int inX, final int inY, final int width, final int height, + final boolean mustFlipVertically) { final int glerr0 = gl.glGetError(); if(GL.GL_NO_ERROR != glerr0) { System.err.println("Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); } - final GLDrawable drawable = gl.getContext().getGLReadDrawable(); - final int textureInternalFormat, textureDataFormat, textureDataType; - final int[] glImplColorReadVals = new int[] { 0, 0 }; - - if(gl.isGL2GL3() && 3 == components) { - textureInternalFormat=GL.GL_RGB; - textureDataFormat=GL.GL_RGB; - textureDataType = GL.GL_UNSIGNED_BYTE; - } else if(gl.isGLES2Compatible() || gl.isExtensionAvailable(GLExtensions.OES_read_format)) { - gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); - gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); - textureInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; - textureDataFormat = glImplColorReadVals[0]; - textureDataType = glImplColorReadVals[1]; + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); + final int internalFormat; + if(gl.isGL2GL3() && 3 == componentCount) { + internalFormat = GL.GL_RGB; + } else { + internalFormat = (4 == componentCount) ? GL.GL_RGBA : GL.GL_RGB; + } + + final boolean flipVertically; + if( drawable.isGLOriented() ) { + flipVertically = mustFlipVertically; } else { - // RGBA read is safe for all GL profiles - textureInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; - textureDataFormat=GL.GL_RGBA; - textureDataType = GL.GL_UNSIGNED_BYTE; + flipVertically = !mustFlipVertically; } - + final int tmp[] = new int[1]; - final int readPixelSize = GLBuffers.sizeof(gl, tmp, textureDataFormat, textureDataType, - drawable.getWidth(), drawable.getHeight(), 1, true); - + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, width, height, 1, true); + boolean newData = false; - if(readPixelSize>readPixelSizeLast) { - readPixelBuffer = Buffers.newDirectByteBuffer(readPixelSize); - readPixelSizeLast = readPixelSize ; + if( null == readPixelBuffer || readPixelBuffer.requiresNewBuffer(gl, width, height, readPixelSize) ) { + readPixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, width, height, 1, true, readPixelSize); + Buffers.rangeCheckBytes(readPixelBuffer.buffer, readPixelSize); try { readTextureData = new TextureData( gl.getGLProfile(), - textureInternalFormat, - drawable.getWidth(), drawable.getHeight(), - 0, - textureDataFormat, - textureDataType, - false, false, - flip, - readPixelBuffer, + internalFormat, + width, height, + 0, + pixelAttribs, + false, false, + flipVertically, + readPixelBuffer.buffer, null /* Flusher */); newData = true; - } catch (Exception e) { + } catch (final Exception e) { readTextureData = null; readPixelBuffer = null; - readPixelSizeLast = 0; throw new RuntimeException("can not fetch offscreen texture", e); } } else { - readTextureData.setInternalFormat(textureInternalFormat); - readTextureData.setWidth(drawable.getWidth()); - readTextureData.setHeight(drawable.getHeight()); - readTextureData.setPixelFormat(textureDataFormat); - readTextureData.setPixelType(textureDataType); + readTextureData.setInternalFormat(internalFormat); + readTextureData.setWidth(width); + readTextureData.setHeight(height); + readTextureData.setPixelAttributes(pixelAttribs); } - boolean res = null!=readPixelBuffer; + boolean res = null!=readPixelBuffer && readPixelBuffer.isValid(); if(res) { - psm.setAlignment(gl, alignment, alignment); + psm.setPackAlignment(gl, alignment); + if(gl.isGL2ES3()) { + final GL2ES3 gl2es3 = gl.getGL2ES3(); + psm.setPackRowLength(gl2es3, width); + gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); + } readPixelBuffer.clear(); - gl.glReadPixels(0, 0, drawable.getWidth(), drawable.getHeight(), textureDataFormat, textureDataType, readPixelBuffer); - readPixelBuffer.position(readPixelSize); + try { + gl.glReadPixels(inX, inY, width, height, pixelAttribs.format, pixelAttribs.type, readPixelBuffer.buffer); + } catch(final GLException gle) { res = false; gle.printStackTrace(); } + readPixelBuffer.position( readPixelSize ); readPixelBuffer.flip(); final int glerr1 = gl.glGetError(); if(GL.GL_NO_ERROR != glerr1) { System.err.println("GLReadBufferUtil.readPixels: readPixels error 0x"+Integer.toHexString(glerr1)+ - " "+drawable.getWidth()+"x"+drawable.getHeight()+ - ", fmt 0x"+Integer.toHexString(textureDataFormat)+", type 0x"+Integer.toHexString(textureDataType)+ - ", impl-fmt 0x"+Integer.toHexString(glImplColorReadVals[0])+", impl-type 0x"+Integer.toHexString(glImplColorReadVals[1])+ + " "+width+"x"+height+ + ", "+pixelAttribs+ ", "+readPixelBuffer+", sz "+readPixelSize); - res = false; + res = false; } if(res && null != readTexture) { if(newData) { readTexture.updateImage(gl, readTextureData); } else { - readTexture.updateSubImage(gl, readTextureData, 0, + readTexture.updateSubImage(gl, readTextureData, 0, 0, 0, // src offset 0, 0, // dst offset - drawable.getWidth(), drawable.getHeight()); + width, height); } readPixelBuffer.rewind(); } @@ -207,16 +256,15 @@ public class GLReadBufferUtil { return res; } - public void dispose(GL gl) { + public void dispose(final GL gl) { if(null != readTexture) { readTexture.destroy(gl); readTextureData = null; } if(null != readPixelBuffer) { - readPixelBuffer.clear(); + readPixelBuffer.dispose(); readPixelBuffer = null; } - readPixelSizeLast = 0; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java index c649d1c6a..bab85e531 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,80 +28,90 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package com.jogamp.opengl.util; -import javax.media.opengl.*; -import jogamp.opengl.*; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; -/** Provides control over the primary display's gamma, brightness and - contrast controls via the hardware gamma ramp tables. Not - supported on all platforms or graphics hardware. <P> - - Thanks to the LWJGL project for illustrating how to access gamma - control on the various platforms. -*/ +import com.jogamp.common.util.locks.RecursiveLock; +/** + * Provides convenient wrapper for {@link GLDrawableFactory} control over + * individual display's gamma, brightness and contrast values + * via the hardware gamma ramp tables. + * <p> + * Not supported on all platforms or graphics hardware. + * </p> + * <p> + * Thanks to the LWJGL project for illustrating how to access gamma + * control on the various platforms. + * </p> + */ public class Gamma { private Gamma() {} /** - * Sets the gamma, brightness, and contrast of the current main - * display. This functionality is not available on all platforms and - * graphics hardware. Returns true if the settings were successfully - * changed, false if not. This method may return false for some - * values of the incoming arguments even on hardware which does - * support the underlying functionality. <P> - * - * If this method returns true, the display settings will - * automatically be reset to their original values upon JVM exit - * (assuming the JVM does not crash); if the user wishes to change - * the display settings back to normal ahead of time, use {@link - * #resetDisplayGamma resetDisplayGamma}(). It is recommended to - * call {@link #resetDisplayGamma resetDisplayGamma} before calling - * e.g. <code>System.exit()</code> from the application rather than - * rely on the shutdown hook functionality due to inevitable race - * conditions and unspecified behavior during JVM teardown. <P> - * - * This method may be called multiple times during the application's - * execution, but calling {@link #resetDisplayGamma - * resetDisplayGamma} will only reset the settings to the values - * before the first call to this method. <P> - * - * @param gamma The gamma value, typically > 1.0 (default values - * vary, but typically roughly 1.0) - * @param brightness The brightness value between -1.0 and 1.0, - * inclusive (default values vary, but typically 0) - * @param contrast The contrast, greater than 0.0 (default values - * vary, but typically 1) - * @return true if gamma settings were successfully changed, false - * if not - * @throws IllegalArgumentException if any of the parameters were - * out-of-bounds + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)}. + * <p> + * Use {@link #setDisplayGamma(GLAutoDrawable, float, float, float)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static boolean setDisplayGamma(final GLDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. + */ + public static boolean setDisplayGamma(final GLAutoDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } finally { + lock.unlock(); + } + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)}. + * <p> + * Use {@link #resetDisplayGamma(GLAutoDrawable)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static void resetDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. */ - public static boolean setDisplayGamma(GL gl, float gamma, float brightness, float contrast) throws IllegalArgumentException { - return GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).setDisplayGamma(gamma, brightness, contrast); + public static void resetDisplayGamma(final GLAutoDrawable drawable) { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } finally { + lock.unlock(); + } } /** - * Resets the gamma, brightness and contrast values for the primary - * display to their original values before {@link #setDisplayGamma - * setDisplayGamma} was called the first time. {@link - * #setDisplayGamma setDisplayGamma} must be called before calling - * this method or an unspecified exception will be thrown. While it - * is not explicitly required that this method be called before - * exiting, calling it is recommended because of the inevitable - * unspecified behavior during JVM teardown. + * Convenient wrapper for {@link GLDrawableFactory#resetAllDisplayGamma()}. */ - public static void resetDisplayGamma(GL gl) { - GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).resetDisplayGamma(); + public static void resetAllDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetAllDisplayGamma(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java b/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java index cf0373044..150e92c2e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java +++ b/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java @@ -3,6 +3,7 @@ package com.jogamp.opengl.util; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.Iterator; @@ -13,53 +14,152 @@ import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLPointerFunc; -import com.jogamp.common.util.ReflectionUtil; +import jogamp.opengl.Debug; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.util.glsl.ShaderState; +/** + * <p> + * Immediate mode sink, implementing OpenGL fixed function subset of immediate mode operations, i.e. + * <pre> + * glBegin(); + * glVertex3f(1f, 1f, 1f); + * glColor4f(1f, 1f, 1f, 1f); + * ... + * glEnd(); + * </pre> + * Implementation buffers all vertex, colors, normal and texture-coord elements in their respective buffers + * to be either rendered directly via {@link #glEnd(GL)} or to be added to an internal display list + * via {@link #glEnd(GL, boolean) glEnd(gl, false)} for deferred rendering via {@link #draw(GL, boolean)}. + * </p> + * <a name="storageDetails"><h5>Buffer storage and it's creation via {@link #createFixed(int, int, int, int, int, int, int, int, int, int) createFixed(..)} + * and {@link #createGLSL(int, int, int, int, int, int, int, int, int, int, ShaderState) createGLSL(..)}</h5></a> + * <p> + * If unsure whether <i>colors</i>, <i>normals</i> and <i>textures</i> will be used, + * simply add them with an expected component count. + * This implementation will only render buffers which are being filled.<br/> + * The buffer growing implementation will only grow the exceeded buffers, unused buffers are not resized. + * </p> + * <p> + * Note: Optional types, i.e. color, must be either not used or used w/ the same element count as vertex, etc. + * This is a semantic constraint, same as in the original OpenGL spec. + * </p> + */ public class ImmModeSink { + protected static final boolean DEBUG_BEGIN_END; + protected static final boolean DEBUG_DRAW; + protected static final boolean DEBUG_BUFFER; + + static { + Debug.initSingleton(); + DEBUG_BEGIN_END = PropertyAccess.isPropertyDefined("jogl.debug.ImmModeSink.BeginEnd", true); + DEBUG_DRAW = PropertyAccess.isPropertyDefined("jogl.debug.ImmModeSink.Draw", true); + DEBUG_BUFFER = PropertyAccess.isPropertyDefined("jogl.debug.ImmModeSink.Buffer", true); + } - public static final boolean DEBUG_BEGIN_END = false; - public static final boolean DEBUG_DRAW = false; - - // public static final int GL_QUADS = 0x0007; // Needs data manipulation + public static final int GL_QUADS = 0x0007; // Needs data manipulation on ES1/ES2 public static final int GL_QUAD_STRIP = 0x0008; public static final int GL_POLYGON = 0x0009; /** * Uses a GL2ES1, or ES2 fixed function emulation immediate mode sink + * <p> + * See <a href="#storageDetails"> buffer storage details</a>. + * </p> + * + * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. + * @param vComps mandatory vertex component count, should be 2, 3 or 4. + * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} + * @param cComps optional color component count, may be 0, 3 or 4 + * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} + * @param nComps optional normal component count, may be 0, 3 or 4 + * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} + * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 + * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} + * @param glBufferUsage VBO <code>usage</code> parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, + * set to <code>0</code> for no VBO usage */ - public static ImmModeSink createFixed(GL gl, int glBufferUsage, int initialElementCount, - int vComps, int vDataType, - int cComps, int cDataType, - int nComps, int nDataType, - int tComps, int tDataType) { - return new ImmModeSink(gl, glBufferUsage, initialElementCount, - vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, false); + public static ImmModeSink createFixed(final int initialElementCount, + final int vComps, final int vDataType, + final int cComps, final int cDataType, + final int nComps, final int nDataType, + final int tComps, final int tDataType, + final int glBufferUsage) { + return new ImmModeSink(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + false, glBufferUsage, null, 0); } /** - * Uses a GL2ES2 GLSL shader immediate mode sink. - * To issue the draw() command, - * a ShaderState must be current, using ShaderState.glUseProgram(). + * Uses a GL2ES2 GLSL shader immediate mode sink, utilizing the given ShaderState. + * <p> + * See <a href="#storageDetails"> buffer storage details</a>. + * </p> * + * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. + * @param vComps mandatory vertex component count, should be 2, 3 or 4. + * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} + * @param cComps optional color component count, may be 0, 3 or 4 + * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} + * @param nComps optional normal component count, may be 0, 3 or 4 + * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} + * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 + * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} + * @param glBufferUsage VBO <code>usage</code> parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, + * set to <code>0</code> for no VBO usage + * @param st ShaderState to locate the vertex attributes * @see #draw(GL, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ - public static ImmModeSink createGLSL(GL gl, int glBufferUsage, int initialElementCount, - int vComps, int vDataType, - int cComps, int cDataType, - int nComps, int nDataType, - int tComps, int tDataType) { - return new ImmModeSink(gl, glBufferUsage, initialElementCount, - vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, true); + public static ImmModeSink createGLSL(final int initialElementCount, + final int vComps, final int vDataType, + final int cComps, final int cDataType, + final int nComps, final int nDataType, + final int tComps, final int tDataType, + final int glBufferUsage, final ShaderState st) { + return new ImmModeSink(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + true, glBufferUsage, st, 0); } - public static boolean usesVBO() { return vboUsage; } - - public static void setVBOUsage(boolean v) { vboUsage = v; } + /** + * Uses a GL2ES2 GLSL shader immediate mode sink, utilizing the given shader-program. + * <p> + * See <a href="#storageDetails"> buffer storage details</a>. + * </p> + * + * @param initialElementCount initial buffer size, if subsequent mutable operations are about to exceed the buffer size, the buffer will grow about the initial size. + * @param vComps mandatory vertex component count, should be 2, 3 or 4. + * @param vDataType mandatory vertex data type, e.g. {@link GL#GL_FLOAT} + * @param cComps optional color component count, may be 0, 3 or 4 + * @param cDataType optional color data type, e.g. {@link GL#GL_FLOAT} + * @param nComps optional normal component count, may be 0, 3 or 4 + * @param nDataType optional normal data type, e.g. {@link GL#GL_FLOAT} + * @param tComps optional texture-coordinate component count, may be 0, 2 or 3 + * @param tDataType optional texture-coordinate data type, e.g. {@link GL#GL_FLOAT} + * @param glBufferUsage VBO <code>usage</code> parameter for {@link GL#glBufferData(int, long, Buffer, int)}, e.g. {@link GL#GL_STATIC_DRAW}, + * set to <code>0</code> for no VBO usage + * @param shaderProgram shader-program name to locate the vertex attributes + * @see #draw(GL, boolean) + * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) + * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() + */ + public static ImmModeSink createGLSL(final int initialElementCount, + final int vComps, final int vDataType, + final int cComps, final int cDataType, + final int nComps, final int nDataType, + final int tComps, final int tDataType, + final int glBufferUsage, final int shaderProgram) { + return new ImmModeSink(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + true, glBufferUsage, null, shaderProgram); + } - public void destroy(GL gl) { + public void destroy(final GL gl) { destroyList(gl); vboSet.destroy(gl); @@ -69,15 +169,16 @@ public class ImmModeSink { reset(null); } - public void reset(GL gl) { + public void reset(final GL gl) { destroyList(gl); vboSet.reset(gl); } + @Override public String toString() { - StringBuilder sb = new StringBuilder("ImmModeSink["); + final StringBuilder sb = new StringBuilder("ImmModeSink["); sb.append(",\n\tVBO list: "+vboSetList.size()+" ["); - for(Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; ) { + for(final Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; ) { sb.append("\n\t"); sb.append( i.next() ); } @@ -91,66 +192,58 @@ public class ImmModeSink { return sb.toString(); } - public void draw(GL gl, boolean disableBufferAfterDraw) { + public void draw(final GL gl, final boolean disableBufferAfterDraw) { if(DEBUG_DRAW) { - Exception e = new Exception("Info: ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); - e.printStackTrace(); + System.err.println("ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); } int n=0; - for(Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; n++) { - i.next().draw(gl, null, disableBufferAfterDraw, n); + for(int i=0; i<vboSetList.size(); i++, n++) { + vboSetList.get(i).draw(gl, null, disableBufferAfterDraw, n); } } - public void draw(GL gl, Buffer indices, boolean disableBufferAfterDraw) { + public void draw(final GL gl, final Buffer indices, final boolean disableBufferAfterDraw) { if(DEBUG_DRAW) { - Exception e = new Exception("Info: ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); - e.printStackTrace(); + System.err.println("ImmModeSink.draw(disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); } int n=0; - for(Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; n++) { - i.next().draw(gl, indices, disableBufferAfterDraw, n); + for(int i=0; i<vboSetList.size(); i++, n++) { + vboSetList.get(i).draw(gl, indices, disableBufferAfterDraw, n); } } public void glBegin(int mode) { - if(DEBUG_BEGIN_END) { - Exception e = new Exception("Info: ImmModeSink.glBegin("+vboSet.mode+"):\n\t"+this); - e.printStackTrace(); - } vboSet.modeOrig = mode; switch(mode) { - // Needs data manipulation .. - //case GL_QUADS: - // mode=GL.GL_LINES; - // break; case GL_QUAD_STRIP: mode=GL.GL_TRIANGLE_STRIP; break; case GL_POLYGON: - mode=GL.GL_LINES; + mode=GL.GL_TRIANGLE_FAN; break; } vboSet.mode = mode; + if(DEBUG_BEGIN_END) { + System.err.println("ImmModeSink.glBegin("+vboSet.modeOrig+" -> "+vboSet.mode+")"); + } vboSet.checkSeal(false); } - public final void glEnd(GL gl) { + public final void glEnd(final GL gl) { glEnd(gl, null, true); } - public void glEnd(GL gl, boolean immediateDraw) { + public void glEnd(final GL gl, final boolean immediateDraw) { glEnd(gl, null, immediateDraw); } - public final void glEnd(GL gl, Buffer indices) { + public final void glEnd(final GL gl, final Buffer indices) { glEnd(gl, indices, true); } - private void glEnd(GL gl, Buffer indices, boolean immediateDraw) { + private void glEnd(final GL gl, final Buffer indices, final boolean immediateDraw) { if(DEBUG_BEGIN_END) { - Exception e = new Exception("Info: ImmModeSink START glEnd(immediate: "+immediateDraw+"):\n\t"+this); - e.printStackTrace(); + System.err.println("ImmModeSink START glEnd(immediate: "+immediateDraw+")"); } if(immediateDraw) { vboSet.seal(gl, true); @@ -160,151 +253,206 @@ public class ImmModeSink { vboSet.seal(gl, true); vboSet.enableBuffer(gl, false); vboSetList.add(vboSet); - vboSet = vboSet.regenerate(); + vboSet = vboSet.regenerate(gl); + } + if(DEBUG_BEGIN_END) { + System.err.println("ImmModeSink END glEnd(immediate: "+immediateDraw+")"); } } - public void glVertexv(Buffer v) { + public void glVertexv(final Buffer v) { vboSet.glVertexv(v); } - public void glNormalv(Buffer v) { + public void glNormalv(final Buffer v) { vboSet.glNormalv(v); } - public void glColorv(Buffer v) { + public void glColorv(final Buffer v) { vboSet.glColorv(v); } - public void glTexCoordv(Buffer v) { + public void glTexCoordv(final Buffer v) { vboSet.glTexCoordv(v); } - public final void glVertex2f(float x, float y) { + public final void glVertex2f(final float x, final float y) { vboSet.glVertex2f(x,y); } - public final void glVertex3f(float x, float y, float z) { + public final void glVertex3f(final float x, final float y, final float z) { vboSet.glVertex3f(x,y,z); } - public final void glNormal3f(float x, float y, float z) { + public final void glNormal3f(final float x, final float y, final float z) { vboSet.glNormal3f(x,y,z); } - public final void glColor3f(float x, float y, float z) { + public final void glColor3f(final float x, final float y, final float z) { vboSet.glColor3f(x,y,z); } - public final void glColor4f(float x, float y, float z, float a) { + public final void glColor4f(final float x, final float y, final float z, final float a) { vboSet.glColor4f(x,y,z, a); } - public final void glTexCoord2f(float x, float y) { + public final void glTexCoord2f(final float x, final float y) { vboSet.glTexCoord2f(x,y); } - public final void glTexCoord3f(float x, float y, float z) { + public final void glTexCoord3f(final float x, final float y, final float z) { vboSet.glTexCoord3f(x,y,z); } - public final void glVertex2s(short x, short y) { + public final void glVertex2s(final short x, final short y) { vboSet.glVertex2s(x,y); } - public final void glVertex3s(short x, short y, short z) { + public final void glVertex3s(final short x, final short y, final short z) { vboSet.glVertex3s(x,y,z); } - public final void glNormal3s(short x, short y, short z) { + public final void glNormal3s(final short x, final short y, final short z) { vboSet.glNormal3s(x,y,z); } - public final void glColor3s(short x, short y, short z) { + public final void glColor3s(final short x, final short y, final short z) { vboSet.glColor3s(x,y,z); } - public final void glColor4s(short x, short y, short z, short a) { + public final void glColor4s(final short x, final short y, final short z, final short a) { vboSet.glColor4s(x,y,z,a); } - public final void glTexCoord2s(short x, short y) { + public final void glTexCoord2s(final short x, final short y) { vboSet.glTexCoord2s(x,y); } - public final void glTexCoord3s(short x, short y, short z) { + public final void glTexCoord3s(final short x, final short y, final short z) { vboSet.glTexCoord3s(x,y,z); } - public final void glVertex2b(byte x, byte y) { + public final void glVertex2b(final byte x, final byte y) { vboSet.glVertex2b(x,y); } - public final void glVertex3b(byte x, byte y, byte z) { + public final void glVertex3b(final byte x, final byte y, final byte z) { vboSet.glVertex3b(x,y,z); } - public final void glNormal3b(byte x, byte y, byte z) { + public final void glNormal3b(final byte x, final byte y, final byte z) { vboSet.glNormal3b(x,y,z); } - public final void glColor3b(byte x, byte y, byte z) { + public final void glColor3b(final byte x, final byte y, final byte z) { vboSet.glColor3b(x,y,z); } - public final void glColor4b(byte x, byte y, byte z, byte a) { + public final void glColor3ub(final byte x, final byte y, final byte z) { + vboSet.glColor3ub(x,y,z); + } + + public final void glColor4b(final byte x, final byte y, final byte z, final byte a) { vboSet.glColor4b(x,y,z,a); } - public final void glTexCoord2b(byte x, byte y) { + public final void glColor4ub(final byte x, final byte y, final byte z, final byte a) { + vboSet.glColor4ub(x,y,z,a); + } + + public final void glTexCoord2b(final byte x, final byte y) { vboSet.glTexCoord2b(x,y); } - public final void glTexCoord3b(byte x, byte y, byte z) { + public final void glTexCoord3b(final byte x, final byte y, final byte z) { vboSet.glTexCoord3b(x,y,z); } - protected ImmModeSink(GL gl, int glBufferUsage, int initialElementCount, - int vComps, int vDataType, - int cComps, int cDataType, - int nComps, int nDataType, - int tComps, int tDataType, boolean useGLSL) { - if(useGLSL && !gl.hasGLSL()) { - throw new GLException("ImmModeSink GLSL usage not supported: "+gl); - } - vboSet = new VBOSet(gl, glBufferUsage, initialElementCount, - vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, useGLSL); + protected ImmModeSink(final int initialElementCount, + final int vComps, final int vDataType, + final int cComps, final int cDataType, + final int nComps, final int nDataType, + final int tComps, final int tDataType, + final boolean useGLSL, final int glBufferUsage, final ShaderState st, final int shaderProgram) { + vboSet = new VBOSet(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + useGLSL, glBufferUsage, st, shaderProgram); this.vboSetList = new ArrayList<VBOSet>(); } - private void destroyList(GL gl) { - for(Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; ) { - i.next().destroy(gl); + public boolean getUseVBO() { return vboSet.getUseVBO(); } + + /** + * Returns the additional element count if buffer resize is required. + * @see #setResizeElementCount(int) + */ + public int getResizeElementCount() { return vboSet.getResizeElementCount(); } + + /** + * Sets the additional element count if buffer resize is required, + * defaults to <code>initialElementCount</code> of factory method. + * @see #createFixed(int, int, int, int, int, int, int, int, int, int) + * @see #createGLSL(int, int, int, int, int, int, int, int, int, int, ShaderState) + */ + public void setResizeElementCount(final int v) { vboSet.setResizeElementCount(v); } + + private void destroyList(final GL gl) { + for(int i=0; i<vboSetList.size(); i++) { + vboSetList.get(i).destroy(gl); } vboSetList.clear(); } private VBOSet vboSet; - private ArrayList<VBOSet> vboSetList; - private static boolean vboUsage = true; + private final ArrayList<VBOSet> vboSetList; protected static class VBOSet { - protected VBOSet (GL gl, int glBufferUsage, int initialElementCount, - int vComps, int vDataType, - int cComps, int cDataType, - int nComps, int nDataType, - int tComps, int tDataType, boolean useGLSL) { - this.gl=gl; + protected VBOSet (final int initialElementCount, + final int vComps, final int vDataType, + final int cComps, final int cDataType, + final int nComps, final int nDataType, + final int tComps, final int tDataType, + final boolean useGLSL, final int glBufferUsage, final ShaderState st, final int shaderProgram) { + // final .. this.glBufferUsage=glBufferUsage; this.initialElementCount=initialElementCount; + this.useVBO = 0 != glBufferUsage; + this.useGLSL=useGLSL; + this.shaderState = st; + this.shaderProgram = shaderProgram; + + if(useGLSL && null == shaderState && 0 == shaderProgram) { + throw new IllegalArgumentException("Using GLSL but neither a valid shader-program nor ShaderState has been passed!"); + } + // variable .. + this.resizeElementCount=initialElementCount; this.vDataType=vDataType; + this.vDataTypeSigned=GLBuffers.isSignedGLType(vDataType); this.vComps=vComps; + this.vCompsBytes=vComps * GLBuffers.sizeOfGLType(vDataType); this.cDataType=cDataType; + this.cDataTypeSigned=GLBuffers.isSignedGLType(cDataType); this.cComps=cComps; + this.cCompsBytes=cComps * GLBuffers.sizeOfGLType(cDataType); this.nDataType=nDataType; + this.nDataTypeSigned=GLBuffers.isSignedGLType(nDataType); this.nComps=nComps; + this.nCompsBytes=nComps * GLBuffers.sizeOfGLType(nDataType); this.tDataType=tDataType; + this.tDataTypeSigned=GLBuffers.isSignedGLType(tDataType); this.tComps=tComps; - this.useGLSL=useGLSL; + this.tCompsBytes=tComps * GLBuffers.sizeOfGLType(tDataType); + this.vboName = 0; - allocateBuffer(initialElementCount); + this.vCount=0; + this.cCount=0; + this.nCount=0; + this.tCount=0; + this.vElems=0; + this.cElems=0; + this.nElems=0; + this.tElems=0; + + this.pageSize = Platform.getMachineDescription().pageSizeInBytes(); + + reallocateBuffer(initialElementCount); rewind(); this.sealed=false; @@ -313,302 +461,387 @@ public class ImmModeSink { this.modeOrig = 0; this.bufferEnabled=false; this.bufferWritten=false; + this.bufferWrittenOnce=false; + this.glslLocationSet = false; } - protected final VBOSet regenerate() { - return new VBOSet(gl, glBufferUsage, initialElementCount, - vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, useGLSL); + protected int getResizeElementCount() { return resizeElementCount; } + protected void setResizeElementCount(final int v) { resizeElementCount=v; } + + protected boolean getUseVBO() { return useVBO; } + + protected final VBOSet regenerate(final GL gl) { + return new VBOSet(initialElementCount, vComps, + vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + useGLSL, glBufferUsage, shaderState, shaderProgram); } - protected void checkSeal(boolean test) throws GLException { + protected void checkSeal(final boolean test) throws GLException { if(0==mode) { - throw new GLException("No mode set yet, call glBegin(mode) first:\n\t"+this); + throw new GLException("No mode set yet, call glBegin(mode) first:\n\t"+this); } if(sealed!=test) { if(test) { - throw new GLException("Not Sealed yet, call glEnd() first:\n\t"+this); + throw new GLException("Not Sealed yet, call glEnd() first:\n\t"+this); } else { - throw new GLException("Already Sealed, can't modify VBO after glEnd():\n\t"+this); + throw new GLException("Already Sealed, can't modify VBO after glEnd():\n\t"+this); } } } - protected void draw(GL gl, Buffer indices, boolean disableBufferAfterDraw, int i) + private boolean usingShaderProgram = false; + + protected void useShaderProgram(final GL2ES2 gl, final boolean force) { + if( force || !usingShaderProgram ) { + if(null != shaderState) { + shaderState.useProgram(gl, true); + } else /* if( 0 != shaderProgram) */ { + gl.glUseProgram(shaderProgram); + } + usingShaderProgram = true; + } + } + + protected void draw(final GL gl, final Buffer indices, final boolean disableBufferAfterDraw, final int i) { + enableBuffer(gl, true); + + if(null != shaderState || 0 != shaderProgram) { + useShaderProgram(gl.getGL2ES2(), false); + } + if(DEBUG_DRAW) { - Exception e = new Exception("Info: ImmModeSink.draw["+i+"](disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); - e.printStackTrace(); + System.err.println("ImmModeSink.draw["+i+"].0 (disableBufferAfterDraw: "+disableBufferAfterDraw+"):\n\t"+this); } - enableBuffer(gl, true); if (buffer!=null) { if(null==indices) { - gl.glDrawArrays(mode, 0, count); + if ( GL_QUADS == mode && !gl.isGL2() ) { + for (int j = 0; j < vElems - 3; j += 4) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, j, 4); + } + } else { + gl.glDrawArrays(mode, 0, vElems); + } } else { - Class<?> clazz = indices.getClass(); - int type=-1; - if(ReflectionUtil.instanceOf(clazz, ByteBuffer.class.getName())) { + // FIXME: Impl. VBO usage .. or unroll. + if( !gl.getContext().isCPUDataSourcingAvail() ) { + throw new GLException("CPU data sourcing n/a w/ "+gl.getContext()); + } + final int type; + if(indices instanceof ByteBuffer) { type = GL.GL_UNSIGNED_BYTE; - } else if(ReflectionUtil.instanceOf(clazz, ShortBuffer.class.getName())) { + } else if(indices instanceof ShortBuffer) { type = GL.GL_UNSIGNED_SHORT; + } else if(indices instanceof IntBuffer) { + type = GL.GL_UNSIGNED_INT; + } else { + throw new GLException("Given Buffer Class not supported: "+indices.getClass()+", should be ubyte, ushort or uint:\n\t"+this); } - if(0>type) { - throw new GLException("Given Buffer Class not supported: "+clazz+", should be ubyte or ushort:\n\t"+this); + final int idxLen = indices.remaining(); + final int idx0 = indices.position(); + + if ( GL_QUADS == mode && !gl.isGL2() ) { + if( GL.GL_UNSIGNED_BYTE == type ) { + final ByteBuffer b = (ByteBuffer) indices; + for (int j = 0; j < idxLen; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0x000000ff & b.get(idx0+j), 4); + } + } else if( GL.GL_UNSIGNED_SHORT == type ){ + final ShortBuffer b = (ShortBuffer) indices; + for (int j = 0; j < idxLen; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0x0000ffff & b.get(idx0+j), 4); + } + } else { + final IntBuffer b = (IntBuffer) indices; + for (int j = 0; j < idxLen; j++) { + gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 0xffffffff & b.get(idx0+j), 4); + } + } + } else { + ((GL2ES1)gl).glDrawElements(mode, idxLen, type, indices); + // GL2: gl.glDrawRangeElements(mode, 0, idxLen-1, idxLen, type, indices); } - gl.glDrawElements(mode, indices.remaining(), type, indices); - // GL2: gl.glDrawRangeElements(mode, 0, indices.remaining()-1, indices.remaining(), type, indices); } } if(disableBufferAfterDraw) { enableBuffer(gl, false); } + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.draw["+i+"].X (disableBufferAfterDraw: "+disableBufferAfterDraw+")"); + } } - public void glVertexv(Buffer v) { + public void glVertexv(final Buffer v) { checkSeal(false); - GLBuffers.put(vertexArray, v); + Buffers.put(vertexArray, v); } - public void glNormalv(Buffer v) { + public void glNormalv(final Buffer v) { checkSeal(false); - GLBuffers.put(normalArray, v); + Buffers.put(normalArray, v); } - public void glColorv(Buffer v) { + public void glColorv(final Buffer v) { checkSeal(false); - GLBuffers.put(colorArray, v); + Buffers.put(colorArray, v); } - public void glTexCoordv(Buffer v) { + public void glTexCoordv(final Buffer v) { checkSeal(false); - GLBuffers.put(textCoordArray, v); + Buffers.put(textCoordArray, v); } - public void glVertex2b(byte x, byte y) { + public void glVertex2b(final byte x, final byte y) { checkSeal(false); - growBufferIfNecessary(VERTEX, 2); - if(vComps>0) - GLBuffers.putb(vertexArray, x); - if(vComps>1) - GLBuffers.putb(vertexArray, y); - padding(VERTEX, vComps-2); - } - public void glVertex3b(byte x, byte y, byte z) { + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNb(vertexArray, vDataTypeSigned, x, true); + if(vComps>1) + Buffers.putNb(vertexArray, vDataTypeSigned, y, true); + countAndPadding(VERTEX, vComps-2); + } + public void glVertex3b(final byte x, final byte y, final byte z) { checkSeal(false); - growBufferIfNecessary(VERTEX, 3); - if(vComps>0) - GLBuffers.putb(vertexArray, x); - if(vComps>1) - GLBuffers.putb(vertexArray, y); - if(vComps>2) - GLBuffers.putb(vertexArray, z); - padding(VERTEX, vComps-3); - } - public void glVertex2s(short x, short y) { + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNb(vertexArray, vDataTypeSigned, x, true); + if(vComps>1) + Buffers.putNb(vertexArray, vDataTypeSigned, y, true); + if(vComps>2) + Buffers.putNb(vertexArray, vDataTypeSigned, z, true); + countAndPadding(VERTEX, vComps-3); + } + public void glVertex2s(final short x, final short y) { checkSeal(false); - growBufferIfNecessary(VERTEX, 2); - if(vComps>0) - GLBuffers.puts(vertexArray, x); - if(vComps>1) - GLBuffers.puts(vertexArray, y); - padding(VERTEX, vComps-2); - } - public void glVertex3s(short x, short y, short z) { + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNs(vertexArray, vDataTypeSigned, x, true); + if(vComps>1) + Buffers.putNs(vertexArray, vDataTypeSigned, y, true); + countAndPadding(VERTEX, vComps-2); + } + public void glVertex3s(final short x, final short y, final short z) { checkSeal(false); - growBufferIfNecessary(VERTEX, 3); - if(vComps>0) - GLBuffers.puts(vertexArray, x); - if(vComps>1) - GLBuffers.puts(vertexArray, y); - if(vComps>2) - GLBuffers.puts(vertexArray, z); - padding(VERTEX, vComps-3); - } - public void glVertex2f(float x, float y) { + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNs(vertexArray, vDataTypeSigned, x, true); + if(vComps>1) + Buffers.putNs(vertexArray, vDataTypeSigned, y, true); + if(vComps>2) + Buffers.putNs(vertexArray, vDataTypeSigned, z, true); + countAndPadding(VERTEX, vComps-3); + } + public void glVertex2f(final float x, final float y) { checkSeal(false); - growBufferIfNecessary(VERTEX, 2); - if(vComps>0) - GLBuffers.putf(vertexArray, x); - if(vComps>1) - GLBuffers.putf(vertexArray, y); - padding(VERTEX, vComps-2); - } - public void glVertex3f(float x, float y, float z) { + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNf(vertexArray, vDataTypeSigned, x); + if(vComps>1) + Buffers.putNf(vertexArray, vDataTypeSigned, y); + countAndPadding(VERTEX, vComps-2); + } + public void glVertex3f(final float x, final float y, final float z) { checkSeal(false); - growBufferIfNecessary(VERTEX, 3); - if(vComps>0) - GLBuffers.putf(vertexArray, x); - if(vComps>1) - GLBuffers.putf(vertexArray, y); - if(vComps>2) - GLBuffers.putf(vertexArray, z); - padding(VERTEX, vComps-3); + growBuffer(VERTEX); + if(vComps>0) + Buffers.putNf(vertexArray, vDataTypeSigned, x); + if(vComps>1) + Buffers.putNf(vertexArray, vDataTypeSigned, y); + if(vComps>2) + Buffers.putNf(vertexArray, vDataTypeSigned, z); + countAndPadding(VERTEX, vComps-3); } - public void glNormal3b(byte x, byte y, byte z) { + public void glNormal3b(final byte x, final byte y, final byte z) { checkSeal(false); - growBufferIfNecessary(NORMAL, 3); - if(nComps>0) - GLBuffers.putb(normalArray, x); - if(nComps>1) - GLBuffers.putb(normalArray, y); - if(nComps>2) - GLBuffers.putb(normalArray, z); - padding(NORMAL, nComps-3); - } - public void glNormal3s(short x, short y, short z) { + growBuffer(NORMAL); + if(nComps>0) + Buffers.putNb(normalArray, nDataTypeSigned, x, true); + if(nComps>1) + Buffers.putNb(normalArray, nDataTypeSigned, y, true); + if(nComps>2) + Buffers.putNb(normalArray, nDataTypeSigned, z, true); + countAndPadding(NORMAL, nComps-3); + } + public void glNormal3s(final short x, final short y, final short z) { checkSeal(false); - growBufferIfNecessary(NORMAL, 3); - if(nComps>0) - GLBuffers.puts(normalArray, x); - if(nComps>1) - GLBuffers.puts(normalArray, y); - if(nComps>2) - GLBuffers.puts(normalArray, z); - padding(NORMAL, nComps-3); - } - public void glNormal3f(float x, float y, float z) { + growBuffer(NORMAL); + if(nComps>0) + Buffers.putNs(normalArray, nDataTypeSigned, x, true); + if(nComps>1) + Buffers.putNs(normalArray, nDataTypeSigned, y, true); + if(nComps>2) + Buffers.putNs(normalArray, nDataTypeSigned, z, true); + countAndPadding(NORMAL, nComps-3); + } + public void glNormal3f(final float x, final float y, final float z) { checkSeal(false); - growBufferIfNecessary(NORMAL, 3); - if(nComps>0) - GLBuffers.putf(normalArray, x); - if(nComps>1) - GLBuffers.putf(normalArray, y); - if(nComps>2) - GLBuffers.putf(normalArray, z); - padding(NORMAL, nComps-3); + growBuffer(NORMAL); + if(nComps>0) + Buffers.putNf(normalArray, nDataTypeSigned, x); + if(nComps>1) + Buffers.putNf(normalArray, nDataTypeSigned, y); + if(nComps>2) + Buffers.putNf(normalArray, nDataTypeSigned, z); + countAndPadding(NORMAL, nComps-3); } - public void glColor3b(byte r, byte g, byte b) { + public void glColor3b(final byte r, final byte g, final byte b) { checkSeal(false); - growBufferIfNecessary(COLOR, 3); - if(cComps>0) - GLBuffers.putb(colorArray, r); - if(cComps>1) - GLBuffers.putb(colorArray, g); - if(cComps>2) - GLBuffers.putb(colorArray, b); - padding(COLOR, cComps-3); - } - public void glColor4b(byte r, byte g, byte b, byte a) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNb(colorArray, cDataTypeSigned, r, true); + if(cComps>1) + Buffers.putNb(colorArray, cDataTypeSigned, g, true); + if(cComps>2) + Buffers.putNb(colorArray, cDataTypeSigned, b, true); + countAndPadding(COLOR, cComps-3); + } + public void glColor3ub(final byte r, final byte g, final byte b) { checkSeal(false); - growBufferIfNecessary(COLOR, 4); - if(cComps>0) - GLBuffers.putb(colorArray, r); - if(cComps>1) - GLBuffers.putb(colorArray, g); - if(cComps>2) - GLBuffers.putb(colorArray, b); - if(cComps>3) - GLBuffers.putb(colorArray, a); - padding(COLOR, cComps-4); - } - public void glColor3s(short r, short g, short b) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNb(colorArray, cDataTypeSigned, r, false); + if(cComps>1) + Buffers.putNb(colorArray, cDataTypeSigned, g, false); + if(cComps>2) + Buffers.putNb(colorArray, cDataTypeSigned, b, false); + countAndPadding(COLOR, cComps-3); + } + public void glColor4b(final byte r, final byte g, final byte b, final byte a) { checkSeal(false); - growBufferIfNecessary(COLOR, 3); - if(cComps>0) - GLBuffers.puts(colorArray, r); - if(cComps>1) - GLBuffers.puts(colorArray, g); - if(cComps>2) - GLBuffers.puts(colorArray, b); - padding(COLOR, cComps-3); - } - public void glColor4s(short r, short g, short b, short a) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNb(colorArray, cDataTypeSigned, r, true); + if(cComps>1) + Buffers.putNb(colorArray, cDataTypeSigned, g, true); + if(cComps>2) + Buffers.putNb(colorArray, cDataTypeSigned, b, true); + if(cComps>3) + Buffers.putNb(colorArray, cDataTypeSigned, a, true); + countAndPadding(COLOR, cComps-4); + } + public void glColor4ub(final byte r, final byte g, final byte b, final byte a) { checkSeal(false); - growBufferIfNecessary(COLOR, 4); - if(cComps>0) - GLBuffers.puts(colorArray, r); - if(cComps>1) - GLBuffers.puts(colorArray, g); - if(cComps>2) - GLBuffers.puts(colorArray, b); - if(cComps>3) - GLBuffers.puts(colorArray, a); - padding(COLOR, cComps-4); - } - public void glColor3f(float r, float g, float b) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNb(colorArray, cDataTypeSigned, r, false); + if(cComps>1) + Buffers.putNb(colorArray, cDataTypeSigned, g, false); + if(cComps>2) + Buffers.putNb(colorArray, cDataTypeSigned, b, false); + if(cComps>3) + Buffers.putNb(colorArray, cDataTypeSigned, a, false); + countAndPadding(COLOR, cComps-4); + } + public void glColor3s(final short r, final short g, final short b) { checkSeal(false); - growBufferIfNecessary(COLOR, 3); - if(cComps>0) - GLBuffers.putf(colorArray, r); - if(cComps>1) - GLBuffers.putf(colorArray, g); - if(cComps>2) - GLBuffers.putf(colorArray, b); - padding(COLOR, cComps-3); - } - public void glColor4f(float r, float g, float b, float a) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNs(colorArray, cDataTypeSigned, r, true); + if(cComps>1) + Buffers.putNs(colorArray, cDataTypeSigned, g, true); + if(cComps>2) + Buffers.putNs(colorArray, cDataTypeSigned, b, true); + countAndPadding(COLOR, cComps-3); + } + public void glColor4s(final short r, final short g, final short b, final short a) { checkSeal(false); - growBufferIfNecessary(COLOR, 4); - if(cComps>0) - GLBuffers.putf(colorArray, r); - if(cComps>1) - GLBuffers.putf(colorArray, g); - if(cComps>2) - GLBuffers.putf(colorArray, b); - if(cComps>3) - GLBuffers.putf(colorArray, a); - padding(COLOR, cComps-4); - } - - public void glTexCoord2b(byte x, byte y) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNs(colorArray, cDataTypeSigned, r, true); + if(cComps>1) + Buffers.putNs(colorArray, cDataTypeSigned, g, true); + if(cComps>2) + Buffers.putNs(colorArray, cDataTypeSigned, b, true); + if(cComps>3) + Buffers.putNs(colorArray, cDataTypeSigned, a, true); + countAndPadding(COLOR, cComps-4); + } + public void glColor3f(final float r, final float g, final float b) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.putb(textCoordArray, x); - if(tComps>1) - GLBuffers.putb(textCoordArray, y); - padding(TEXTCOORD, tComps-2); - } - public void glTexCoord3b(byte x, byte y, byte z) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNf(colorArray, cDataTypeSigned, r); + if(cComps>1) + Buffers.putNf(colorArray, cDataTypeSigned, g); + if(cComps>2) + Buffers.putNf(colorArray, cDataTypeSigned, b); + countAndPadding(COLOR, cComps-3); + } + public void glColor4f(final float r, final float g, final float b, final float a) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 3); - if(tComps>0) - GLBuffers.putb(textCoordArray, x); - if(tComps>1) - GLBuffers.putb(textCoordArray, y); - if(tComps>2) - GLBuffers.putb(textCoordArray, z); - padding(TEXTCOORD, tComps-3); - } - public void glTexCoord2s(short x, short y) { + growBuffer(COLOR); + if(cComps>0) + Buffers.putNf(colorArray, cDataTypeSigned, r); + if(cComps>1) + Buffers.putNf(colorArray, cDataTypeSigned, g); + if(cComps>2) + Buffers.putNf(colorArray, cDataTypeSigned, b); + if(cComps>3) + Buffers.putNf(colorArray, cDataTypeSigned, a); + countAndPadding(COLOR, cComps-4); + } + + public void glTexCoord2b(final byte x, final byte y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.puts(textCoordArray, x); - if(tComps>1) - GLBuffers.puts(textCoordArray, y); - padding(TEXTCOORD, tComps-2); - } - public void glTexCoord3s(short x, short y, short z) { + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNb(textCoordArray, tDataTypeSigned, x, true); + if(tComps>1) + Buffers.putNb(textCoordArray, tDataTypeSigned, y, true); + countAndPadding(TEXTCOORD, tComps-2); + } + public void glTexCoord3b(final byte x, final byte y, final byte z) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 3); - if(tComps>0) - GLBuffers.puts(textCoordArray, x); - if(tComps>1) - GLBuffers.puts(textCoordArray, y); - if(tComps>2) - GLBuffers.puts(textCoordArray, z); - padding(TEXTCOORD, tComps-3); - } - public void glTexCoord2f(float x, float y) { + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNb(textCoordArray, tDataTypeSigned, x, true); + if(tComps>1) + Buffers.putNb(textCoordArray, tDataTypeSigned, y, true); + if(tComps>2) + Buffers.putNb(textCoordArray, tDataTypeSigned, z, true); + countAndPadding(TEXTCOORD, tComps-3); + } + public void glTexCoord2s(final short x, final short y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.putf(textCoordArray, x); - if(tComps>1) - GLBuffers.putf(textCoordArray, y); - padding(TEXTCOORD, tComps-2); - } - public void glTexCoord3f(float x, float y, float z) { + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNs(textCoordArray, tDataTypeSigned, x, true); + if(tComps>1) + Buffers.putNs(textCoordArray, tDataTypeSigned, y, true); + countAndPadding(TEXTCOORD, tComps-2); + } + public void glTexCoord3s(final short x, final short y, final short z) { + checkSeal(false); + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNs(textCoordArray, tDataTypeSigned, x, true); + if(tComps>1) + Buffers.putNs(textCoordArray, tDataTypeSigned, y, true); + if(tComps>2) + Buffers.putNs(textCoordArray, tDataTypeSigned, z, true); + countAndPadding(TEXTCOORD, tComps-3); + } + public void glTexCoord2f(final float x, final float y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 3); - if(tComps>0) - GLBuffers.putf(textCoordArray, x); - if(tComps>1) - GLBuffers.putf(textCoordArray, y); - if(tComps>2) - GLBuffers.putf(textCoordArray, z); - padding(TEXTCOORD, tComps-3); + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNf(textCoordArray, tDataTypeSigned, x); + if(tComps>1) + Buffers.putNf(textCoordArray, tDataTypeSigned, y); + countAndPadding(TEXTCOORD, tComps-2); + } + public void glTexCoord3f(final float x, final float y, final float z) { + checkSeal(false); + growBuffer(TEXTCOORD); + if(tComps>0) + Buffers.putNf(textCoordArray, tDataTypeSigned, x); + if(tComps>1) + Buffers.putNf(textCoordArray, tDataTypeSigned, y); + if(tComps>2) + Buffers.putNf(textCoordArray, tDataTypeSigned, z); + countAndPadding(TEXTCOORD, tComps-3); } public void rewind() { @@ -626,16 +859,61 @@ public class ImmModeSink { } } - public void destroy(GL gl) { + public void setShaderProgram(final int program) { + if(null == shaderState && 0 == program) { + throw new IllegalArgumentException("Not allowed to zero shader program if no ShaderState is set"); + } + shaderProgram = program; + glslLocationSet = false; // enforce location reset! + } + + /** + * @param gl + * @return true if all locations for all used arrays are found (min 1 array), otherwise false. + * Also sets 'glslLocationSet' to the return value! + */ + private boolean resetGLSLArrayLocation(final GL2ES2 gl) { + int iA = 0; + int iL = 0; + + if(null != vArrayData) { + iA++; + if( vArrayData.setLocation(gl, shaderProgram) >= 0 ) { + iL++; + } + } + if(null != cArrayData) { + iA++; + if( cArrayData.setLocation(gl, shaderProgram) >= 0 ) { + iL++; + } + } + if(null != nArrayData) { + iA++; + if( nArrayData.setLocation(gl, shaderProgram) >= 0 ) { + iL++; + } + } + if(null != tArrayData) { + iA++; + if( tArrayData.setLocation(gl, shaderProgram) >= 0 ) { + iL++; + } + } + glslLocationSet = iA == iL; + return glslLocationSet; + } + + public void destroy(final GL gl) { reset(gl); + vCount=0; cCount=0; nCount=0; tCount=0; vertexArray=null; colorArray=null; normalArray=null; textCoordArray=null; vArrayData=null; cArrayData=null; nArrayData=null; tArrayData=null; buffer=null; - bSize=0; count=0; } - public void reset(GL gl) { + public void reset(final GL gl) { enableBuffer(gl, false); reset(); } @@ -649,162 +927,334 @@ public class ImmModeSink { this.mode = 0; this.modeOrig = 0; this.sealed=false; + this.sealedGL=false; this.bufferEnabled=false; this.bufferWritten=false; + this.vElems=0; + this.cElems=0; + this.nElems=0; + this.tElems=0; } - public void seal(GL glObj, boolean seal) + public void seal(final GL glObj, final boolean seal) { seal(seal); if(sealedGL==seal) return; sealedGL = seal; - GL gl = glObj.getGL(); + final GL gl = glObj.getGL(); if(seal) { - if(vboUsage && vboName==0) { - int[] tmp = new int[1]; - gl.glGenBuffers(1, tmp, 0); - vboName = tmp[0]; + if(useVBO) { + if(0 == vboName) { + final int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + vboName = tmp[0]; + } + if(null!=vArrayData) { + vArrayData.setVBOName(vboName); + } + if(null!=cArrayData) { + cArrayData.setVBOName(vboName); + } + if(null!=nArrayData) { + nArrayData.setVBOName(vboName); + } + if(null!=tArrayData) { + tArrayData.setVBOName(vboName); + } } - if(null!=vArrayData) - vArrayData.setVBOName(vboName); - if(null!=cArrayData) - cArrayData.setVBOName(vboName); - if(null!=nArrayData) - nArrayData.setVBOName(vboName); - if(null!=tArrayData) - tArrayData.setVBOName(vboName); enableBuffer(gl, true); } else { enableBuffer(gl, false); } } - public void seal(boolean seal) + public void seal(final boolean seal) { if(sealed==seal) return; sealed = seal; if(seal) { bufferWritten=false; + rewind(); } } - public void enableBuffer(GL gl, boolean enable) { - /* if(enableBufferAlways && enable) { - bufferEnabled = false; - } */ - if( bufferEnabled != enable && count>0 ) { + public void enableBuffer(final GL gl, final boolean enable) { + if( bufferEnabled != enable && vElems>0 ) { if(enable) { checkSeal(true); } - if(useGLSL) { - enableBufferGLSL(gl, enable); + bufferEnabled = enable; + if(useGLSL) { + useShaderProgram(gl.getGL2ES2(), true); + if(null != shaderState) { + enableBufferGLSLShaderState(gl, enable); + } else { + enableBufferGLSLSimple(gl, enable); + } } else { enableBufferFixed(gl, enable); } - bufferEnabled = enable; } } - public void enableBufferFixed(GL gl, boolean enable) { - GL2ES1 glf = gl.getGL2ES1(); + private final void writeBuffer(final GL gl) { + final int vBytes = vElems * vCompsBytes; + final int cBytes = cElems * cCompsBytes; + final int nBytes = nElems * nCompsBytes; + final int tBytes = tElems * tCompsBytes; + final int delta = buffer.limit() - (vBytes+cBytes+nBytes+tBytes); + if( bufferWrittenOnce && delta > pageSize ) { + if(0 < vBytes) { + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, vOffset, vBytes, vertexArray); + } + if(0 < cBytes) { + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, cOffset, cBytes, colorArray); + } + if(0 < nBytes) { + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, nOffset, nBytes, normalArray); + } + if(0 < tBytes) { + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, tOffset, tBytes, textCoordArray); + } + } else { + gl.glBufferData(GL.GL_ARRAY_BUFFER, buffer.limit(), buffer, glBufferUsage); + bufferWrittenOnce = true; + } + } + + private void enableBufferFixed(final GL gl, final boolean enable) { + final GL2ES1 glf = gl.getGL2ES1(); + + final boolean useV = vComps>0 && vElems>0 ; + final boolean useC = cComps>0 && cElems>0 ; + final boolean useN = nComps>0 && nElems>0 ; + final boolean useT = tComps>0 && tElems>0 ; + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableFixed.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); + } if(enable) { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); + if(useVBO) { + if(0 == vboName) { + throw new InternalError("Using VBO but no vboName"); + } + glf.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); - if(!bufferWritten) { - gl.glBufferData(GL.GL_ARRAY_BUFFER, buffer.limit(), buffer, GL.GL_STATIC_DRAW); - bufferWritten=true; + if(!bufferWritten) { + writeBuffer(gl); + } } + bufferWritten=true; + } - if(vComps>0) { + if(useV) { + if(enable) { glf.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY); glf.glVertexPointer(vArrayData); - } - if(cComps>0) { + } else { + glf.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); + } + } + if(useC) { + if(enable) { glf.glEnableClientState(GLPointerFunc.GL_COLOR_ARRAY); glf.glColorPointer(cArrayData); - } - if(nComps>0) { + } else { + glf.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); + } + } + if(useN) { + if(enable) { glf.glEnableClientState(GLPointerFunc.GL_NORMAL_ARRAY); glf.glNormalPointer(nArrayData); - } - if(tComps>0) { + } else { + glf.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY); + } + } + if(useT) { + if(enable) { glf.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); glf.glTexCoordPointer(tArrayData); - } + } else { + glf.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); + } + } + if(enable && useVBO) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); - } else { - if(vComps>0) { - glf.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); - } - if(cComps>0) { - glf.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); - } - if(nComps>0) { - glf.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY); - } - if(tComps>0) { - glf.glDisableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); - } + } + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableFixed.X "); } } - public void enableBufferGLSL(GL gl, boolean enable) { - ShaderState st = ShaderState.getShaderState(gl); - if(null==st) { - throw new GLException("No ShaderState in "+gl); - } - GL2ES2 glsl = gl.getGL2ES2(); - - if(enable) { - glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); + private void enableBufferGLSLShaderState(final GL gl, final boolean enable) { + final GL2ES2 glsl = gl.getGL2ES2(); - if(!bufferWritten) { - glsl.glBufferData(GL.GL_ARRAY_BUFFER, buffer.limit(), buffer, GL.GL_STATIC_DRAW); - bufferWritten=true; - } + final boolean useV = vComps>0 && vElems>0 ; + final boolean useC = cComps>0 && cElems>0 ; + final boolean useN = nComps>0 && nElems>0 ; + final boolean useT = tComps>0 && tElems>0 ; - if(vComps>0) { - st.enableVertexAttribArray(glsl, vArrayData); - st.vertexAttribPointer(glsl, vArrayData); - } - if(cComps>0) { - st.enableVertexAttribArray(glsl, cArrayData); - st.vertexAttribPointer(glsl, cArrayData); - } - if(nComps>0) { - st.enableVertexAttribArray(glsl, nArrayData); - st.vertexAttribPointer(glsl, nArrayData); - } - if(tComps>0) { - st.enableVertexAttribArray(glsl, tArrayData); - st.vertexAttribPointer(glsl, tArrayData); + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableGLSL.A.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); + } + + if(enable) { + if(useVBO) { + if(0 == vboName) { + throw new InternalError("Using VBO but no vboName"); + } + glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); + if(!bufferWritten) { + writeBuffer(gl); + } } + bufferWritten=true; + } + if(useV) { + if(enable) { + shaderState.enableVertexAttribArray(glsl, vArrayData); + shaderState.vertexAttribPointer(glsl, vArrayData); + } else { + shaderState.disableVertexAttribArray(glsl, vArrayData); + } + } + if(useC) { + if(enable) { + shaderState.enableVertexAttribArray(glsl, cArrayData); + shaderState.vertexAttribPointer(glsl, cArrayData); + } else { + shaderState.disableVertexAttribArray(glsl, cArrayData); + } + } + if(useN) { + if(enable) { + shaderState.enableVertexAttribArray(glsl, nArrayData); + shaderState.vertexAttribPointer(glsl, nArrayData); + } else { + shaderState.disableVertexAttribArray(glsl, nArrayData); + } + } + if(useT) { + if(enable) { + shaderState.enableVertexAttribArray(glsl, tArrayData); + shaderState.vertexAttribPointer(glsl, tArrayData); + } else { + shaderState.disableVertexAttribArray(glsl, tArrayData); + } + } + glslLocationSet = true; // ShaderState does set the location implicit + + if(enable && useVBO) { glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); - } else { - if(vComps>0) { - st.disableVertexAttribArray(glsl, vArrayData); - } - if(cComps>0) { - st.disableVertexAttribArray(glsl, cArrayData); - } - if(nComps>0) { - st.disableVertexAttribArray(glsl, nArrayData); + } + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableGLSL.A.X "); + } + } + + private void enableBufferGLSLSimple(final GL gl, final boolean enable) { + final GL2ES2 glsl = gl.getGL2ES2(); + + final boolean useV = vComps>0 && vElems>0 ; + final boolean useC = cComps>0 && cElems>0 ; + final boolean useN = nComps>0 && nElems>0 ; + final boolean useT = tComps>0 && tElems>0 ; + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableGLSL.B.0 "+enable+": use [ v "+useV+", c "+useC+", n "+useN+", t "+useT+"], "+getElemUseCountStr()+", "+buffer); + } + + if(!glslLocationSet) { + if( !resetGLSLArrayLocation(glsl) ) { + if(DEBUG_DRAW) { + final int vLoc = null != vArrayData ? vArrayData.getLocation() : -1; + final int cLoc = null != cArrayData ? cArrayData.getLocation() : -1; + final int nLoc = null != nArrayData ? nArrayData.getLocation() : -1; + final int tLoc = null != tArrayData ? tArrayData.getLocation() : -1; + System.err.println("ImmModeSink.enableGLSL.B.X attribute locations in shader program "+shaderProgram+", incomplete ["+vLoc+", "+cLoc+", "+nLoc+", "+tLoc+"] - glslLocationSet "+glslLocationSet); + } + return; } - if(tComps>0) { - st.disableVertexAttribArray(glsl, tArrayData); + } + + if(enable) { + if(useVBO) { + if(0 == vboName) { + throw new InternalError("Using VBO but no vboName"); + } + glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); + if(!bufferWritten) { + writeBuffer(gl); + } } + bufferWritten=true; + } + + if(useV) { + if(enable) { + glsl.glEnableVertexAttribArray(vArrayData.getLocation()); + glsl.glVertexAttribPointer(vArrayData); + } else { + glsl.glDisableVertexAttribArray(vArrayData.getLocation()); + } + } + if(useC) { + if(enable) { + glsl.glEnableVertexAttribArray(cArrayData.getLocation()); + glsl.glVertexAttribPointer(cArrayData); + } else { + glsl.glDisableVertexAttribArray(cArrayData.getLocation()); + } + } + if(useN) { + if(enable) { + glsl.glEnableVertexAttribArray(nArrayData.getLocation()); + glsl.glVertexAttribPointer(nArrayData); + } else { + glsl.glDisableVertexAttribArray(nArrayData.getLocation()); + } + } + if(useT) { + if(enable) { + glsl.glEnableVertexAttribArray(tArrayData.getLocation()); + glsl.glVertexAttribPointer(tArrayData); + } else { + glsl.glDisableVertexAttribArray(tArrayData.getLocation()); + } + } + + if(enable && useVBO) { + glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } + + if(DEBUG_DRAW) { + System.err.println("ImmModeSink.enableGLSL.B.X "); } } + @Override public String toString() { - return "VBOSet[mode "+mode+ - ", modeOrig "+modeOrig+ - ", sealed "+sealed+ - ", bufferEnabled "+bufferEnabled+ - ", bufferWritten "+bufferWritten+ + final String glslS = useGLSL ? + ", useShaderState "+(null!=shaderState)+ + ", shaderProgram "+shaderProgram+ + ", glslLocationSet "+glslLocationSet : ""; + + return "VBOSet[mode "+mode+ + ", modeOrig "+modeOrig+ + ", use/count "+getElemUseCountStr()+ + ", sealed "+sealed+ + ", sealedGL "+sealedGL+ + ", bufferEnabled "+bufferEnabled+ + ", bufferWritten "+bufferWritten+" (once "+bufferWrittenOnce+")"+ + ", useVBO "+useVBO+", vboName "+vboName+ + ", useGLSL "+useGLSL+ + glslS+ ",\n\t"+vArrayData+ ",\n\t"+cArrayData+ ",\n\t"+nArrayData+ @@ -814,165 +1264,239 @@ public class ImmModeSink { // non public matters - protected void allocateBuffer(int elementCount) { - int vWidth = vComps * GLBuffers.sizeOfGLType(vDataType); - int cWidth = cComps * GLBuffers.sizeOfGLType(cDataType); - int nWidth = nComps * GLBuffers.sizeOfGLType(nDataType); - int tWidth = tComps * GLBuffers.sizeOfGLType(tDataType); + protected String getElemUseCountStr() { + return "[v "+vElems+"/"+vCount+", c "+cElems+"/"+cCount+", n "+nElems+"/"+nCount+", t "+tElems+"/"+tCount+"]"; + } + + protected boolean fitElementInBuffer(final int type) { + final int addElems = 1; + switch (type) { + case VERTEX: + return ( vCount - vElems ) >= addElems ; + case COLOR: + return ( cCount - cElems ) >= addElems ; + case NORMAL: + return ( nCount - nElems ) >= addElems ; + case TEXTCOORD: + return ( tCount - tElems ) >= addElems ; + default: + throw new InternalError("XXX"); + } + } - count = elementCount; - bSize = count * ( vWidth + cWidth + nWidth + tWidth ) ; + protected boolean reallocateBuffer(final int addElems) { + final int vAdd = addElems - ( vCount - vElems ); + final int cAdd = addElems - ( cCount - cElems ); + final int nAdd = addElems - ( nCount - nElems ); + final int tAdd = addElems - ( tCount - tElems ); - buffer = GLBuffers.newDirectByteBuffer(bSize); + if( 0>=vAdd && 0>=cAdd && 0>=nAdd && 0>=tAdd) { + if(DEBUG_BUFFER) { + System.err.println("ImmModeSink.realloc: "+getElemUseCountStr()+" + "+addElems+" -> NOP"); + } + return false; + } + + if(DEBUG_BUFFER) { + System.err.println("ImmModeSink.realloc: "+getElemUseCountStr()+" + "+addElems); + } + vCount += vAdd; + cCount += cAdd; + nCount += nAdd; + tCount += tAdd; + + final int vBytes = vCount * vCompsBytes; + final int cBytes = cCount * cCompsBytes; + final int nBytes = nCount * nCompsBytes; + final int tBytes = tCount * tCompsBytes; - int pos = 0; - int size= count * vWidth ; - if(size>0) { - vertexArray = GLBuffers.sliceGLBuffer(buffer, pos, size, vDataType); + buffer = Buffers.newDirectByteBuffer( vBytes + cBytes + nBytes + tBytes ); + vOffset = 0; + + if(vBytes>0) { + vertexArray = GLBuffers.sliceGLBuffer(buffer, vOffset, vBytes, vDataType); } else { vertexArray = null; } - vOffset = pos; - pos+=size; + cOffset=vOffset+vBytes; - size= count * cWidth ; - if(size>0) { - colorArray = GLBuffers.sliceGLBuffer(buffer, pos, size, cDataType); + if(cBytes>0) { + colorArray = GLBuffers.sliceGLBuffer(buffer, cOffset, cBytes, cDataType); } else { colorArray = null; } - cOffset = pos; - pos+=size; + nOffset=cOffset+cBytes; - size= count * nWidth ; - if(size>0) { - normalArray = GLBuffers.sliceGLBuffer(buffer, pos, size, nDataType); + if(nBytes>0) { + normalArray = GLBuffers.sliceGLBuffer(buffer, nOffset, nBytes, nDataType); } else { normalArray = null; } - nOffset = pos; - pos+=size; + tOffset=nOffset+nBytes; - size= count * tWidth ; - if(size>0) { - textCoordArray = GLBuffers.sliceGLBuffer(buffer, pos, size, tDataType); + if(tBytes>0) { + textCoordArray = GLBuffers.sliceGLBuffer(buffer, tOffset, tBytes, tDataType); } else { textCoordArray = null; } - tOffset = pos; - pos+=size; - buffer.position(pos); + buffer.position(tOffset+tBytes); buffer.flip(); if(vComps>0) { - vArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_VERTEX_ARRAY, vComps, vDataType, false, 0, + vArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_VERTEX_ARRAY, vComps, + vDataType, GLBuffers.isGLTypeFixedPoint(vDataType), 0, vertexArray, 0, vOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { vArrayData = null; } if(cComps>0) { - cArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_COLOR_ARRAY, cComps, cDataType, false, 0, + cArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_COLOR_ARRAY, cComps, + cDataType, GLBuffers.isGLTypeFixedPoint(cDataType), 0, colorArray, 0, cOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { cArrayData = null; } if(nComps>0) { - nArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_NORMAL_ARRAY, nComps, nDataType, false, 0, + nArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_NORMAL_ARRAY, nComps, + nDataType, GLBuffers.isGLTypeFixedPoint(nDataType), 0, normalArray, 0, nOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { nArrayData = null; } if(tComps>0) { - tArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_TEXTURE_COORD_ARRAY, tComps, tDataType, false, 0, + tArrayData = GLArrayDataWrapper.createFixed(GLPointerFunc.GL_TEXTURE_COORD_ARRAY, tComps, + tDataType, GLBuffers.isGLTypeFixedPoint(tDataType), 0, textCoordArray, 0, tOffset, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER); } else { tArrayData = null; } - } + bufferWrittenOnce = false; // new buffer data storage size! - protected final boolean growBufferIfNecessary(int type, int spare) { - if(buffer==null || count < spare) { - growBuffer(type, initialElementCount); - return true; + if(DEBUG_BUFFER) { + System.err.println("ImmModeSink.realloc.X: "+this.toString()); + Thread.dumpStack(); } - return false; + return true; } - protected final void growBuffer(int type, int additional) { - if(sealed || 0==additional) return; - - // save olde values .. - Buffer _vertexArray=vertexArray, _colorArray=colorArray, _normalArray=normalArray, _textCoordArray=textCoordArray; - - allocateBuffer(count+additional); - - if(null!=_vertexArray) { - _vertexArray.flip(); - GLBuffers.put(vertexArray, _vertexArray); - } - if(null!=_colorArray) { - _colorArray.flip(); - GLBuffers.put(colorArray, _colorArray); - } - if(null!=_normalArray) { - _normalArray.flip(); - GLBuffers.put(normalArray, _normalArray); - } - if(null!=_textCoordArray) { - _textCoordArray.flip(); - GLBuffers.put(textCoordArray, _textCoordArray); + /** grow buffer by initialElementCount if there is no space for one more element in the designated buffer */ + protected final boolean growBuffer(final int type) { + if( null !=buffer && !sealed ) { + if( !fitElementInBuffer(type) ) { + // save olde values .. + final Buffer _vertexArray=vertexArray, _colorArray=colorArray, _normalArray=normalArray, _textCoordArray=textCoordArray; + + if ( reallocateBuffer(resizeElementCount) ) { + if(null!=_vertexArray) { + _vertexArray.flip(); + Buffers.put(vertexArray, _vertexArray); + } + if(null!=_colorArray) { + _colorArray.flip(); + Buffers.put(colorArray, _colorArray); + } + if(null!=_normalArray) { + _normalArray.flip(); + Buffers.put(normalArray, _normalArray); + } + if(null!=_textCoordArray) { + _textCoordArray.flip(); + Buffers.put(textCoordArray, _textCoordArray); + } + return true; + } + } } + return false; } - protected void padding(int type, int fill) { + /** + * Fourth element default value for color (alpha), vertex (w) is '1', + * as specified w/ VertexAttributes (ES2/GL3). + * <p> + * vec4 v = vec4(0, 0, 0, 1); + * vec4 c = vec4(0, 0, 0, 1); + * </p> + * + * @param type + * @param fill + */ + private void countAndPadding(final int type, int fill) { if ( sealed ) return; - Buffer dest = null; + final Buffer dest; + final boolean dSigned; + final int e; // either 0 or 1 switch (type) { case VERTEX: dest = vertexArray; + dSigned = vDataTypeSigned; + e = 4 == vComps ? 1 : 0; + vElems++; break; case COLOR: dest = colorArray; + dSigned = cDataTypeSigned; + e = 4 == cComps ? 1 : 0; + cElems++; break; case NORMAL: dest = normalArray; + dSigned = nDataTypeSigned; + e = 0; + nElems++; break; case TEXTCOORD: dest = textCoordArray; + dSigned = tDataTypeSigned; + e = 0; + tElems++; break; + default: throw new InternalError("Invalid type "+type); } if ( null==dest ) return; - while((fill--)>0) { - GLBuffers.putb(dest, (byte)0); + while( fill > e ) { + fill--; + Buffers.putNf(dest, dSigned, 0f); + } + if( fill > 0 ) { // e == 1, add missing '1f end component' + Buffers.putNf(dest, dSigned, 1f); } } - protected int mode, modeOrig; - protected int glBufferUsage, initialElementCount; - - protected ByteBuffer buffer; - protected int bSize, count, vboName; - - public static final int VERTEX = 0; - public static final int COLOR = 1; - public static final int NORMAL = 2; - public static final int TEXTCOORD = 3; - - protected int vOffset, cOffset, nOffset, tOffset; - protected int vComps, cComps, nComps, tComps; - protected int vDataType, cDataType, nDataType, tDataType; - protected Buffer vertexArray, colorArray, normalArray, textCoordArray; - protected GLArrayDataWrapper vArrayData, cArrayData, nArrayData, tArrayData; - - protected boolean sealed, sealedGL, useGLSL; - protected boolean bufferEnabled, bufferWritten; - protected GL gl; + final private int glBufferUsage, initialElementCount; + final private boolean useVBO, useGLSL; + final private ShaderState shaderState; + private int shaderProgram; + private int mode, modeOrig, resizeElementCount; + + private ByteBuffer buffer; + private int vboName; + + private static final int VERTEX = 0; + private static final int COLOR = 1; + private static final int NORMAL = 2; + private static final int TEXTCOORD = 3; + + private int vCount, cCount, nCount, tCount; // number of elements fit in each buffer + private int vOffset, cOffset, nOffset, tOffset; // offset of specific array in common buffer + private int vElems, cElems, nElems, tElems; // number of used elements in each buffer + private final int vComps, cComps, nComps, tComps; // number of components for each elements [2, 3, 4] + private final int vCompsBytes, cCompsBytes, nCompsBytes, tCompsBytes; // byte size of all components + private final int vDataType, cDataType, nDataType, tDataType; + private final boolean vDataTypeSigned, cDataTypeSigned, nDataTypeSigned, tDataTypeSigned; + private final int pageSize; + private Buffer vertexArray, colorArray, normalArray, textCoordArray; + private GLArrayDataWrapper vArrayData, cArrayData, nArrayData, tArrayData; + + private boolean sealed, sealedGL; + private boolean bufferEnabled, bufferWritten, bufferWrittenOnce; + private boolean glslLocationSet; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java b/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java index fc6ef3ede..11acb0c58 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java +++ b/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2011 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,147 +29,87 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ package com.jogamp.opengl.util; -import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.List; import javax.media.opengl.GL; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLMatrixFunc; -import jogamp.opengl.ProjectFloat; +import jogamp.common.os.PlatformPropsImpl; -import com.jogamp.opengl.FloatUtil; import com.jogamp.common.nio.Buffers; - -public class PMVMatrix implements GLMatrixFunc { - - protected final float[] matrixBufferArray; +import com.jogamp.common.util.FloatStack; +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.Ray; +import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.math.geom.Frustum; + +/** + * PMVMatrix implements a subset of the fixed function pipeline + * regarding the projection (P), modelview (Mv) matrix operation + * which is specified in {@link GLMatrixFunc}. + * <p> + * Further more, PMVMatrix provides the {@link #glGetMviMatrixf() inverse modelview matrix (Mvi)} and + * {@link #glGetMvitMatrixf() inverse transposed modelview matrix (Mvit)}. + * {@link Frustum} is also provided by {@link #glGetFrustum()}. + * To keep these derived values synchronized after mutable Mv operations like {@link #glRotatef(float, float, float, float) glRotatef(..)} + * in {@link #glMatrixMode(int) glMatrixMode}({@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}), + * users have to call {@link #update()} before using Mvi and Mvit. + * </p> + * <p> + * All matrices are provided in column-major order, + * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile. + * See {@link FloatUtil}. + * </p> + * <p> + * PMVMatrix can supplement {@link GL2ES2} applications w/ the + * lack of the described matrix functionality. + * </p> + * <a name="storageDetails"><h5>Matrix storage details</h5></a> + * <p> + * All matrices are backed up by a common primitive float-array for performance considerations + * and are a {@link Buffers#slice2Float(float[], int, int) sliced} representation of it. + * </p> + * <p> + * <b>Note:</b> + * <ul> + * <li>The matrix is a {@link Buffers#slice2Float(float[], int, int) sliced part } of a host matrix and it's start position has been {@link FloatBuffer#mark() marked}.</li> + * <li>Use {@link FloatBuffer#reset() reset()} to rewind it to it's start position after relative operations, like {@link FloatBuffer#get() get()}.</li> + * <li>If using absolute operations like {@link FloatBuffer#get(int) get(int)}, use it's {@link FloatBuffer#reset() reset} {@link FloatBuffer#position() position} as it's offset.</li> + * </ul> + * </p> + */ +public final class PMVMatrix implements GLMatrixFunc { + + /** Bit value stating a modified {@link #glGetPMatrixf() projection matrix (P)}, since last {@link #update()} call. */ + public static final int MODIFIED_PROJECTION = 1 << 0; + /** Bit value stating a modified {@link #glGetMvMatrixf() modelview matrix (Mv)}, since last {@link #update()} call. */ + public static final int MODIFIED_MODELVIEW = 1 << 1; + /** Bit value stating a modified {@link #glGetTMatrixf() texture matrix (T)}, since last {@link #update()} call. */ + public static final int MODIFIED_TEXTURE = 1 << 2; + /** Bit value stating all is modified */ + public static final int MODIFIED_ALL = MODIFIED_PROJECTION | MODIFIED_MODELVIEW | MODIFIED_TEXTURE ; + + /** Bit value stating a dirty {@link #glGetMviMatrixf() inverse modelview matrix (Mvi)}. */ + public static final int DIRTY_INVERSE_MODELVIEW = 1 << 0; + /** Bit value stating a dirty {@link #glGetMvitMatrixf() inverse transposed modelview matrix (Mvit)}. */ + public static final int DIRTY_INVERSE_TRANSPOSED_MODELVIEW = 1 << 1; + /** Bit value stating a dirty {@link #glGetFrustum() frustum}. */ + public static final int DIRTY_FRUSTUM = 1 << 2; + /** Bit value stating all is dirty */ + public static final int DIRTY_ALL = DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM; /** - * Creates an instance of PMVMatrix {@link #PMVMatrix(boolean) PMVMatrix(boolean useBackingArray)}, - * with <code>useBackingArray = true</code>. + * @param matrixModeName One of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} + * @return true if the given matrix-mode name is valid, otherwise false. */ - public PMVMatrix() { - this(true); - } - - /** - * Creates an instance of PMVMatrix. - * - * @param useBackingArray <code>true</code> for non direct NIO Buffers with guaranteed backing array, - * which allows faster access in Java computation. - * <p><code>false</code> for direct NIO buffers w/o a guaranteed backing array. - * In most Java implementations, direct NIO buffers have no backing array - * and hence the Java computation will be throttled down by direct IO get/put - * operations.</p> - * <p>Depending on the application, ie. whether the Java computation or - * JNI invocation and hence native data transfer part is heavier, - * this flag shall be set to <code>true</code> or <code>false</code></p>. - */ - public PMVMatrix(boolean useBackingArray) { - this.usesBackingArray = useBackingArray; - - // I Identity - // T Texture - // P Projection - // Mv ModelView - // Mvi Modelview-Inverse - // Mvit Modelview-Inverse-Transpose - if(useBackingArray) { - matrixBufferArray = new float[ 6*16 + ProjectFloat.getRequiredFloatBufferSize() ]; - matrixBuffer = null; - } else { - matrixBufferArray = null; - matrixBuffer = Buffers.newDirectByteBuffer( ( 6*16 + ProjectFloat.getRequiredFloatBufferSize() ) * Buffers.SIZEOF_FLOAT ); - matrixBuffer.mark(); - } - - matrixIdent = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 0*16, 1*16); // I - matrixTex = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 1*16, 1*16); // T - matrixPMvMvit = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 2*16, 4*16); // P + Mv + Mvi + Mvit - matrixPMvMvi = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 2*16, 3*16); // P + Mv + Mvi - matrixPMv = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 2*16, 2*16); // P + Mv - matrixP = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 2*16, 1*16); // P - matrixMv = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 3*16, 1*16); // Mv - matrixMvi = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 4*16, 1*16); // Mvi - matrixMvit = Buffers.slice2Float(matrixBuffer, matrixBufferArray, 5*16, 1*16); // Mvit - - projectFloat = new ProjectFloat(matrixBuffer, matrixBufferArray, 6*16); - - if(null != matrixBuffer) { - matrixBuffer.reset(); - } - FloatUtil.makeIdentityf(matrixIdent); - - vec3f = new float[3]; - matrixMult = new float[16]; - matrixTrans = new float[16]; - matrixRot = new float[16]; - matrixScale = new float[16]; - matrixOrtho = new float[16]; - matrixFrustum = new float[16]; - FloatUtil.makeIdentityf(matrixTrans, 0); - FloatUtil.makeIdentityf(matrixRot, 0); - FloatUtil.makeIdentityf(matrixScale, 0); - FloatUtil.makeIdentityf(matrixOrtho, 0); - FloatUtil.makeZero(matrixFrustum, 0); - - matrixPStack = new ArrayList<float[]>(); - matrixMvStack= new ArrayList<float[]>(); - - // default values and mode - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glMatrixMode(GL.GL_TEXTURE); - glLoadIdentity(); - setDirty(); - update(); - } - - public final boolean usesBackingArray() { return usesBackingArray; } - - public void destroy() { - if(null!=projectFloat) { - projectFloat.destroy(); projectFloat=null; - } - - matrixBuffer=null; - matrixBuffer=null; matrixPMvMvit=null; matrixPMvMvi=null; matrixPMv=null; - matrixP=null; matrixTex=null; matrixMv=null; matrixMvi=null; matrixMvit=null; - - vec3f = null; - matrixMult = null; - matrixTrans = null; - matrixRot = null; - matrixScale = null; - matrixOrtho = null; - matrixFrustum = null; - - if(null!=matrixPStack) { - matrixPStack.clear(); matrixPStack=null; - } - if(null!=matrixMvStack) { - matrixMvStack.clear(); matrixMvStack=null; - } - if(null!=matrixPStack) { - matrixPStack.clear(); matrixPStack=null; - } - if(null!=matrixTStack) { - matrixTStack.clear(); matrixTStack=null; - } - } - - public static final boolean isMatrixModeName(final int matrixModeName) { switch(matrixModeName) { case GL_MODELVIEW_MATRIX: @@ -180,6 +120,10 @@ public class PMVMatrix implements GLMatrixFunc { return false; } + /** + * @param matrixModeName One of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} + * @return The corresponding matrix-get name, one of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX}, {@link GLMatrixFunc#GL_PROJECTION_MATRIX GL_PROJECTION_MATRIX} or {@link GLMatrixFunc#GL_TEXTURE_MATRIX GL_TEXTURE_MATRIX} + */ public static final int matrixModeName2MatrixGetName(final int matrixModeName) { switch(matrixModeName) { case GL_MODELVIEW: @@ -193,6 +137,10 @@ public class PMVMatrix implements GLMatrixFunc { } } + /** + * @param matrixGetName One of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX}, {@link GLMatrixFunc#GL_PROJECTION_MATRIX GL_PROJECTION_MATRIX} or {@link GLMatrixFunc#GL_TEXTURE_MATRIX GL_TEXTURE_MATRIX} + * @return true if the given matrix-get name is valid, otherwise false. + */ public static final boolean isMatrixGetName(final int matrixGetName) { switch(matrixGetName) { case GL_MATRIX_MODE: @@ -204,6 +152,10 @@ public class PMVMatrix implements GLMatrixFunc { return false; } + /** + * @param matrixGetName One of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX}, {@link GLMatrixFunc#GL_PROJECTION_MATRIX GL_PROJECTION_MATRIX} or {@link GLMatrixFunc#GL_TEXTURE_MATRIX GL_TEXTURE_MATRIX} + * @return The corresponding matrix-mode name, one of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} + */ public static final int matrixGetName2MatrixModeName(final int matrixGetName) { switch(matrixGetName) { case GL_MODELVIEW_MATRIX: @@ -217,122 +169,275 @@ public class PMVMatrix implements GLMatrixFunc { } } - public void setDirty() { - modified = DIRTY_MODELVIEW | DIRTY_PROJECTION | DIRTY_TEXTURE ; - matrixMode = GL_MODELVIEW; - } - - public int getDirtyBits() { - return modified; + /** + * @param sb optional passed StringBuilder instance to be used + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a 4x4 matrix in column major order (OpenGL) + * @return matrix string representation + */ + @SuppressWarnings("deprecation") + public static StringBuilder matrixToString(final StringBuilder sb, final String f, final FloatBuffer a) { + return FloatUtil.matrixToString(sb, null, f, a, 0, 4, 4, false); } - public boolean isDirty(final int matrixName) { - boolean res; - switch(matrixName) { - case GL_MODELVIEW: - res = (modified&DIRTY_MODELVIEW)!=0 ; - break; - case GL_PROJECTION: - res = (modified&DIRTY_PROJECTION)!=0 ; - break; - case GL.GL_TEXTURE: - res = (modified&DIRTY_TEXTURE)!=0 ; - break; - default: - throw new GLException("unsupported matrixName: "+matrixName); - } - return res; + /** + * @param sb optional passed StringBuilder instance to be used + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @param a 4x4 matrix in column major order (OpenGL) + * @param b 4x4 matrix in column major order (OpenGL) + * @return side by side representation + */ + @SuppressWarnings("deprecation") + public static StringBuilder matrixToString(final StringBuilder sb, final String f, final FloatBuffer a, final FloatBuffer b) { + return FloatUtil.matrixToString(sb, null, f, a, 0, b, 0, 4, 4, false); } - public boolean isDirty() { - return modified!=0; + /** + * Creates an instance of PMVMatrix. + * <p> + * Implementation uses non-direct non-NIO Buffers with guaranteed backing array, + * which allows faster access in Java computation. + * </p> + */ + public PMVMatrix() { + // I Identity + // T Texture + // P Projection + // Mv ModelView + // Mvi Modelview-Inverse + // Mvit Modelview-Inverse-Transpose + matrixArray = new float[5*16]; + + mP_offset = 0*16; + mMv_offset = 1*16; + mTex_offset = 4*16; + + matrixPMvMvit = Buffers.slice2Float(matrixArray, 0*16, 4*16); // P + Mv + Mvi + Mvit + matrixPMvMvi = Buffers.slice2Float(matrixArray, 0*16, 3*16); // P + Mv + Mvi + matrixPMv = Buffers.slice2Float(matrixArray, 0*16, 2*16); // P + Mv + matrixP = Buffers.slice2Float(matrixArray, 0*16, 1*16); // P + matrixMv = Buffers.slice2Float(matrixArray, 1*16, 1*16); // Mv + matrixMvi = Buffers.slice2Float(matrixArray, 2*16, 1*16); // Mvi + matrixMvit = Buffers.slice2Float(matrixArray, 3*16, 1*16); // Mvit + matrixTex = Buffers.slice2Float(matrixArray, 4*16, 1*16); // T + + mat4Tmp1 = new float[16]; + mat4Tmp2 = new float[16]; + mat4Tmp3 = new float[16]; + matrixTxSx = new float[16]; + FloatUtil.makeIdentity(matrixTxSx); + + // Start w/ zero size to save memory + matrixTStack = new FloatStack( 0, 2*16); // growSize: GL-min size (2) + matrixPStack = new FloatStack( 0, 2*16); // growSize: GL-min size (2) + matrixMvStack= new FloatStack( 0, 16*16); // growSize: half GL-min size (32) + + reset(); + + frustum = null; } /** - * Update the derived Mvi, Mvit and Pmv matrices - * in case Mv or P has changed. - * - * @return + * Issues {@link #glLoadIdentity()} on all matrices, + * i.e. {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} + * and resets all internal states. */ - public boolean update() { - if(0==modified) return false; - - final int res = modified; - if( (res&DIRTY_MODELVIEW)!=0 ) { - setMviMvit(); - } - modified=0; - return res!=0; + public final void reset() { + FloatUtil.makeIdentity(matrixArray, mMv_offset); + FloatUtil.makeIdentity(matrixArray, mP_offset); + FloatUtil.makeIdentity(matrixArray, mTex_offset); + + modifiedBits = MODIFIED_ALL; + dirtyBits = DIRTY_ALL; + requestMask = 0; + matrixMode = GL_MODELVIEW; } + /** Returns the current matrix-mode, one of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE}. */ public final int glGetMatrixMode() { return matrixMode; } + /** + * Returns the {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). + * <p> + * See <a href="#storageDetails"> matrix storage details</a>. + * </p> + */ public final FloatBuffer glGetTMatrixf() { return matrixTex; } + /** + * Returns the {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). + * <p> + * See <a href="#storageDetails"> matrix storage details</a>. + * </p> + */ public final FloatBuffer glGetPMatrixf() { return matrixP; } + /** + * Returns the {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). + * <p> + * See <a href="#storageDetails"> matrix storage details</a>. + * </p> + */ public final FloatBuffer glGetMvMatrixf() { return matrixMv; } - public final FloatBuffer glGetPMvMviMatrixf() { - usesMviMvit |= 1; - return matrixPMvMvi; + /** + * Returns the inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi). + * <p> + * Method enables the Mvi matrix update, and performs it's update w/o clearing the modified bits. + * </p> + * <p> + * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * </p> + * @see #update() + * @see #clearAllUpdateRequests() + */ + public final FloatBuffer glGetMviMatrixf() { + requestMask |= DIRTY_INVERSE_MODELVIEW ; + updateImpl(false); + return matrixMvi; } + /** + * Returns the inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit). + * <p> + * Method enables the Mvit matrix update, and performs it's update w/o clearing the modified bits. + * </p> + * <p> + * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * </p> + * @see #update() + * @see #clearAllUpdateRequests() + */ + public final FloatBuffer glGetMvitMatrixf() { + requestMask |= DIRTY_INVERSE_TRANSPOSED_MODELVIEW ; + updateImpl(false); + return matrixMvit; + } + + /** + * Returns 2 matrices within one FloatBuffer: {@link #glGetPMatrixf() P} and {@link #glGetMvMatrixf() Mv}. + * <p> + * See <a href="#storageDetails"> matrix storage details</a>. + * </p> + */ public final FloatBuffer glGetPMvMatrixf() { return matrixPMv; } - public final FloatBuffer glGetMviMatrixf() { - usesMviMvit |= 1; - return matrixMvi; + /** + * Returns 3 matrices within one FloatBuffer: {@link #glGetPMatrixf() P}, {@link #glGetMvMatrixf() Mv} and {@link #glGetMviMatrixf() Mvi}. + * <p> + * Method enables the Mvi matrix update, and performs it's update w/o clearing the modified bits. + * </p> + * <p> + * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * </p> + * @see #update() + * @see #clearAllUpdateRequests() + */ + public final FloatBuffer glGetPMvMviMatrixf() { + requestMask |= DIRTY_INVERSE_MODELVIEW ; + updateImpl(false); + return matrixPMvMvi; } + /** + * Returns 4 matrices within one FloatBuffer: {@link #glGetPMatrixf() P}, {@link #glGetMvMatrixf() Mv}, {@link #glGetMviMatrixf() Mvi} and {@link #glGetMvitMatrixf() Mvit}. + * <p> + * Method enables the Mvi and Mvit matrix update, and performs it's update w/o clearing the modified bits. + * </p> + * <p> + * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * </p> + * @see #update() + * @see #clearAllUpdateRequests() + */ public final FloatBuffer glGetPMvMvitMatrixf() { - usesMviMvit |= 1 | 2; + requestMask |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW ; + updateImpl(false); return matrixPMvMvit; } - - public final FloatBuffer glGetMvitMatrixf() { - usesMviMvit |= 1 | 2; - return matrixMvit; + + /** Returns the frustum, derived from projection * modelview */ + public final Frustum glGetFrustum() { + requestMask |= DIRTY_FRUSTUM; + updateImpl(false); + return frustum; } - - /* - * @return the current matrix - */ + + /* + * @return the matrix of the current matrix-mode + */ public final FloatBuffer glGetMatrixf() { return glGetMatrixf(matrixMode); } - /** - * @param matrixName GL_MODELVIEW, GL_PROJECTION or GL.GL_TEXTURE - * @return the given matrix - */ + /** + * @param matrixName Either a matrix-get-name, i.e. + * {@link GLMatrixFunc#GL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX}, {@link GLMatrixFunc#GL_PROJECTION_MATRIX GL_PROJECTION_MATRIX} or {@link GLMatrixFunc#GL_TEXTURE_MATRIX GL_TEXTURE_MATRIX}, + * or a matrix-mode-name, i.e. + * {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} + * @return the named matrix, not a copy! + */ public final FloatBuffer glGetMatrixf(final int matrixName) { - if(matrixName==GL_MODELVIEW) { - return matrixMv; - } else if(matrixName==GL_PROJECTION) { - return matrixP; - } else if(matrixName==GL.GL_TEXTURE) { - return matrixTex; - } else { - throw new GLException("unsupported matrixName: "+matrixName); + switch(matrixName) { + case GL_MODELVIEW_MATRIX: + case GL_MODELVIEW: + return matrixMv; + case GL_PROJECTION_MATRIX: + case GL_PROJECTION: + return matrixP; + case GL_TEXTURE_MATRIX: + case GL.GL_TEXTURE: + return matrixTex; + default: + throw new GLException("unsupported matrixName: "+matrixName); } } - // - // MatrixIf + + /** + * Multiplies the {@link #glGetPMatrixf() P} and {@link #glGetMvMatrixf() Mv} matrix, i.e. + * <pre> + * mat4PMv = P x Mv + * </pre> + * @param mat4PMv 4x4 matrix storage for result + * @param mat4PMv_offset + * @return given matrix for chaining + */ + public final float[] multPMvMatrixf(final float[/*16*/] mat4PMv, final int mat4PMv_offset) { + FloatUtil.multMatrix(matrixArray, mP_offset, matrixArray, mMv_offset, mat4PMv, mat4PMv_offset); + return mat4PMv; + } + + /** + * Multiplies the {@link #glGetMvMatrixf() Mv} and {@link #glGetPMatrixf() P} matrix, i.e. + * <pre> + * mat4MvP = Mv x P + * </pre> + * @param mat4MvP 4x4 matrix storage for result + * @param mat4MvP_offset + * @return given matrix for chaining + */ + public final float[] multMvPMatrixf(final float[/*16*/] mat4MvP, final int mat4MvP_offset) { + FloatUtil.multMatrix(matrixArray, mMv_offset, matrixArray, mP_offset, mat4MvP, mat4MvP_offset); + return mat4MvP; + } + + // + // GLMatrixFunc implementation // - public void glMatrixMode(final int matrixName) { + @Override + public final void glMatrixMode(final int matrixName) { switch(matrixName) { case GL_MODELVIEW: case GL_PROJECTION: @@ -344,28 +449,33 @@ public class PMVMatrix implements GLMatrixFunc { matrixMode = matrixName; } - public void glGetFloatv(int matrixGetName, FloatBuffer params) { - int pos = params.position(); + @Override + public final void glGetFloatv(final int matrixGetName, final FloatBuffer params) { + final int pos = params.position(); if(matrixGetName==GL_MATRIX_MODE) { - params.put((float)matrixMode); + params.put(matrixMode); } else { - FloatBuffer matrix = glGetMatrixf(matrixGetName2MatrixModeName(matrixGetName)); + final FloatBuffer matrix = glGetMatrixf(matrixGetName); params.put(matrix); // matrix -> params matrix.reset(); } params.position(pos); } - public void glGetFloatv(int matrixGetName, float[] params, int params_offset) { + + @Override + public final void glGetFloatv(final int matrixGetName, final float[] params, final int params_offset) { if(matrixGetName==GL_MATRIX_MODE) { - params[params_offset]=(float)matrixMode; + params[params_offset]=matrixMode; } else { - FloatBuffer matrix = glGetMatrixf(matrixGetName2MatrixModeName(matrixGetName)); + final FloatBuffer matrix = glGetMatrixf(matrixGetName); matrix.get(params, params_offset, 16); // matrix -> params matrix.reset(); } } - public void glGetIntegerv(int pname, IntBuffer params) { - int pos = params.position(); + + @Override + public final void glGetIntegerv(final int pname, final IntBuffer params) { + final int pos = params.position(); if(pname==GL_MATRIX_MODE) { params.put(matrixMode); } else { @@ -373,7 +483,9 @@ public class PMVMatrix implements GLMatrixFunc { } params.position(pos); } - public void glGetIntegerv(int pname, int[] params, int params_offset) { + + @Override + public final void glGetIntegerv(final int pname, final int[] params, final int params_offset) { if(pname==GL_MATRIX_MODE) { params[params_offset]=matrixMode; } else { @@ -381,374 +493,581 @@ public class PMVMatrix implements GLMatrixFunc { } } + @Override public final void glLoadMatrixf(final float[] values, final int offset) { - int len = values.length-offset; if(matrixMode==GL_MODELVIEW) { - matrixMv.put(values, offset, len); + matrixMv.put(values, offset, 16); matrixMv.reset(); - modified |= DIRTY_MODELVIEW ; + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - matrixP.put(values, offset, len); + matrixP.put(values, offset, 16); matrixP.reset(); - modified |= DIRTY_PROJECTION ; + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - matrixTex.put(values, offset, len); + matrixTex.put(values, offset, 16); matrixTex.reset(); - modified |= DIRTY_TEXTURE ; - } + modifiedBits |= MODIFIED_TEXTURE; + } } - public final void glLoadMatrixf(java.nio.FloatBuffer m) { - int spos = m.position(); + @Override + public final void glLoadMatrixf(final java.nio.FloatBuffer m) { + final int spos = m.position(); if(matrixMode==GL_MODELVIEW) { matrixMv.put(m); matrixMv.reset(); - modified |= DIRTY_MODELVIEW ; + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matrixP.put(m); matrixP.reset(); - modified |= DIRTY_PROJECTION ; + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matrixTex.put(m); matrixTex.reset(); - modified |= DIRTY_TEXTURE ; - } + modifiedBits |= MODIFIED_TEXTURE; + } m.position(spos); } + /** + * Load the current matrix with the values of the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. + */ + public final void glLoadMatrix(final Quaternion quat) { + if(matrixMode==GL_MODELVIEW) { + quat.toMatrix(matrixArray, mMv_offset); + matrixMv.reset(); + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; + } else if(matrixMode==GL_PROJECTION) { + quat.toMatrix(matrixArray, mP_offset); + matrixP.reset(); + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; + } else if(matrixMode==GL.GL_TEXTURE) { + quat.toMatrix(matrixArray, mTex_offset); + matrixTex.reset(); + modifiedBits |= MODIFIED_TEXTURE; + } + } + + @Override public final void glPopMatrix() { - float[] stackEntry=null; + final FloatStack stack; if(matrixMode==GL_MODELVIEW) { - stackEntry = matrixMvStack.remove(0); + stack = matrixMvStack; } else if(matrixMode==GL_PROJECTION) { - stackEntry = matrixPStack.remove(0); + stack = matrixPStack; } else if(matrixMode==GL.GL_TEXTURE) { - stackEntry = matrixTStack.remove(0); - } - glLoadMatrixf(stackEntry, 0); + stack = matrixTStack; + } else { + throw new InternalError("XXX: mode "+matrixMode); + } + stack.position(stack.position() - 16); + glLoadMatrixf(stack.buffer(), stack.position()); } + @Override public final void glPushMatrix() { - float[] stackEntry = new float[1*16]; if(matrixMode==GL_MODELVIEW) { - matrixMv.get(stackEntry); + matrixMvStack.putOnTop(matrixMv, 16); matrixMv.reset(); - matrixMvStack.add(0, stackEntry); } else if(matrixMode==GL_PROJECTION) { - matrixP.get(stackEntry); + matrixPStack.putOnTop(matrixP, 16); matrixP.reset(); - matrixPStack.add(0, stackEntry); } else if(matrixMode==GL.GL_TEXTURE) { - matrixTex.get(stackEntry); + matrixTStack.putOnTop(matrixTex, 16); matrixTex.reset(); - matrixTStack.add(0, stackEntry); } } + @Override public final void glLoadIdentity() { if(matrixMode==GL_MODELVIEW) { - matrixMv.put(matrixIdent); - matrixMv.reset(); - modified |= DIRTY_MODELVIEW ; + FloatUtil.makeIdentity(matrixArray, mMv_offset); + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - matrixP.put(matrixIdent); - matrixP.reset(); - modified |= DIRTY_PROJECTION ; + FloatUtil.makeIdentity(matrixArray, mP_offset); + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - matrixTex.put(matrixIdent); - matrixTex.reset(); - modified |= DIRTY_TEXTURE ; - } - matrixIdent.reset(); + FloatUtil.makeIdentity(matrixArray, mTex_offset); + modifiedBits |= MODIFIED_TEXTURE; + } } + @SuppressWarnings("deprecation") + @Override public final void glMultMatrixf(final FloatBuffer m) { if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrixf(matrixMv, m, matrixMv); - modified |= DIRTY_MODELVIEW ; + FloatUtil.multMatrix(matrixMv, m); + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - FloatUtil.multMatrixf(matrixP, m, matrixP); - modified |= DIRTY_PROJECTION ; + FloatUtil.multMatrix(matrixP, m); + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.multMatrixf(matrixTex, m, matrixTex); - modified |= DIRTY_TEXTURE ; - } + FloatUtil.multMatrix(matrixTex, m); + modifiedBits |= MODIFIED_TEXTURE; + } } - public void glMultMatrixf(float[] m, int m_offset) { + @Override + public final void glMultMatrixf(final float[] m, final int m_offset) { if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrixf(matrixMv, m, m_offset, matrixMv); - modified |= DIRTY_MODELVIEW ; + FloatUtil.multMatrix(matrixArray, mMv_offset, m, m_offset); + dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - FloatUtil.multMatrixf(matrixP, m, m_offset, matrixP); - modified |= DIRTY_PROJECTION ; + FloatUtil.multMatrix(matrixArray, mP_offset, m, m_offset); + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.multMatrixf(matrixTex, m, m_offset, matrixTex); - modified |= DIRTY_TEXTURE ; - } + FloatUtil.multMatrix(matrixArray, mTex_offset, m, m_offset); + modifiedBits |= MODIFIED_TEXTURE; + } } + @Override public final void glTranslatef(final float x, final float y, final float z) { - // Translation matrix: - // 1 0 0 x - // 0 1 0 y - // 0 0 1 z - // 0 0 0 1 - matrixTrans[0+4*3] = x; - matrixTrans[1+4*3] = y; - matrixTrans[2+4*3] = z; - glMultMatrixf(matrixTrans, 0); - } - - public final void glRotatef(final float angdeg, float x, float y, float z) { - final float angrad = angdeg * (float) Math.PI / 180.0f; - final float c = (float)Math.cos(angrad); - final float ic= 1.0f - c; - final float s = (float)Math.sin(angrad); - - vec3f[0]=x; vec3f[1]=y; vec3f[2]=z; - FloatUtil.normalize(vec3f); - x = vec3f[0]; y = vec3f[1]; z = vec3f[2]; - - // Rotation matrix: - // xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0 - // xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0 - // xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0 - // 0 0 0 1 - final float xy = x*y; - final float xz = x*z; - final float xs = x*s; - final float ys = y*s; - final float yz = y*z; - final float zs = z*s; - matrixRot[0*4+0] = x*x*ic+c; - matrixRot[0*4+1] = xy*ic+zs; - matrixRot[0*4+2] = xz*ic-ys; - - matrixRot[1*4+0] = xy*ic-zs; - matrixRot[1*4+1] = y*y*ic+c; - matrixRot[1*4+2] = yz*ic+xs; - - matrixRot[2*4+0] = xz*ic+ys; - matrixRot[2*4+1] = yz*ic-xs; - matrixRot[2*4+2] = z*z*ic+c; - - glMultMatrixf(matrixRot, 0); + glMultMatrixf(FloatUtil.makeTranslation(matrixTxSx, false, x, y, z), 0); } + @Override public final void glScalef(final float x, final float y, final float z) { - // Scale matrix: - // x 0 0 0 - // 0 y 0 0 - // 0 0 z 0 - // 0 0 0 1 - matrixScale[0+4*0] = x; - matrixScale[1+4*1] = y; - matrixScale[2+4*2] = z; + glMultMatrixf(FloatUtil.makeScale(matrixTxSx, false, x, y, z), 0); + } - glMultMatrixf(matrixScale, 0); + @Override + public final void glRotatef(final float ang_deg, final float x, final float y, final float z) { + glMultMatrixf(FloatUtil.makeRotationAxis(mat4Tmp1, 0, ang_deg * FloatUtil.PI / 180.0f, x, y, z, mat4Tmp2), 0); } + /** + * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. + */ + public final void glRotate(final Quaternion quat) { + glMultMatrixf(quat.toMatrix(mat4Tmp1, 0), 0); + } + + @Override public final void glOrthof(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { - // Ortho matrix: - // 2/dx 0 0 tx - // 0 2/dy 0 ty - // 0 0 2/dz tz - // 0 0 0 1 - final float dx=right-left; - final float dy=top-bottom; - final float dz=zFar-zNear; - final float tx=-1.0f*(right+left)/dx; - final float ty=-1.0f*(top+bottom)/dy; - final float tz=-1.0f*(zFar+zNear)/dz; - - matrixOrtho[0+4*0] = 2.0f/dx; - matrixOrtho[1+4*1] = 2.0f/dy; - matrixOrtho[2+4*2] = -2.0f/dz; - matrixOrtho[0+4*3] = tx; - matrixOrtho[1+4*3] = ty; - matrixOrtho[2+4*3] = tz; - - glMultMatrixf(matrixOrtho, 0); - } - - public final void gluPerspective(final float fovy, final float aspect, final float zNear, final float zFar) { - float top=(float)Math.tan(fovy*((float)Math.PI)/360.0f)*zNear; - float bottom=-1.0f*top; - float left=aspect*bottom; - float right=aspect*top; - glFrustumf(left, right, bottom, top, zNear, zFar); + glMultMatrixf( FloatUtil.makeOrtho(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar), 0 ); } + @Override public final void glFrustumf(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { - if(zNear<=0.0f||zFar<0.0f) { - throw new GLException("GL_INVALID_VALUE: zNear and zFar must be positive, and zNear>0"); - } - if(left==right || top==bottom) { - throw new GLException("GL_INVALID_VALUE: top,bottom and left,right must not be equal"); - } - // Frustum matrix: - // 2*zNear/dx 0 A 0 - // 0 2*zNear/dy B 0 - // 0 0 C D - // 0 0 -1 0 - final float zNear2 = 2.0f*zNear; - final float dx=right-left; - final float dy=top-bottom; - final float dz=zFar-zNear; - final float A=(right+left)/dx; - final float B=(top+bottom)/dy; - final float C=-1.0f*(zFar+zNear)/dz; - final float D=-2.0f*(zFar*zNear)/dz; - - matrixFrustum[0+4*0] = zNear2/dx; - matrixFrustum[1+4*1] = zNear2/dy; - matrixFrustum[2+4*2] = C; - - matrixFrustum[0+4*2] = A; - matrixFrustum[1+4*2] = B; + glMultMatrixf( FloatUtil.makeFrustum(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar), 0 ); + } - matrixFrustum[2+4*3] = D; - matrixFrustum[3+4*2] = -1.0f; + // + // Extra functionality + // - glMultMatrixf(matrixFrustum, 0); + /** + * {@link #glMultMatrixf(FloatBuffer) Multiply} the {@link #glGetMatrixMode() current matrix} with the perspective/frustum matrix. + * + * @param fovy_deg fov angle in degrees + * @param aspect aspect ratio width / height + * @param zNear + * @param zFar + */ + public final void gluPerspective(final float fovy_deg, final float aspect, final float zNear, final float zFar) { + glMultMatrixf( FloatUtil.makePerspective(mat4Tmp1, 0, true, fovy_deg * FloatUtil.PI / 180.0f, aspect, zNear, zFar), 0 ); } - public void gluLookAt(float eyex, float eyey, float eyez, - float centerx, float centery, float centerz, - float upx, float upy, float upz) { - projectFloat.gluLookAt(this, eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz); + /** + * {@link #glMultMatrixf(FloatBuffer) Multiply} and {@link #glTranslatef(float, float, float) translate} the {@link #glGetMatrixMode() current matrix} + * with the eye, object and orientation. + */ + public final void gluLookAt(final float eyex, final float eyey, final float eyez, + final float centerx, final float centery, final float centerz, + final float upx, final float upy, final float upz) { + mat4Tmp2[0+0] = eyex; + mat4Tmp2[1+0] = eyey; + mat4Tmp2[2+0] = eyez; + mat4Tmp2[0+4] = centerx; + mat4Tmp2[1+4] = centery; + mat4Tmp2[2+4] = centerz; + mat4Tmp2[0+8] = upx; + mat4Tmp2[1+8] = upy; + mat4Tmp2[2+8] = upz; + glMultMatrixf( + FloatUtil.makeLookAt(mat4Tmp1, 0, mat4Tmp2 /* eye */, 0, mat4Tmp2 /* center */, 4, mat4Tmp2 /* up */, 8, mat4Tmp3), 0); } /** - * Uses this instance {@link #glGetMvMatrixf()} and {@link #glGetPMatrixf()} - * + * Map object coordinates to window coordinates. + * <p> + * Traditional <code>gluProject</code> implementation. + * </p> + * * @param objx * @param objy * @param objz - * @param viewport + * @param viewport 4 component viewport vector * @param viewport_offset - * @param win_pos + * @param win_pos 3 component window coordinate, the result * @param win_pos_offset - * @return - */ - public boolean gluProject(float objx, float objy, float objz, - int[] viewport, int viewport_offset, - float[] win_pos, int win_pos_offset ) { - if(usesBackingArray) { - return projectFloat.gluProject(objx, objy, objz, - matrixMv.array(), 0, - matrixP.array(), 0, - viewport, viewport_offset, - win_pos, win_pos_offset); - } else { - return projectFloat.gluProject(objx, objy, objz, - matrixMv, - matrixP, - viewport, viewport_offset, - win_pos, win_pos_offset); - } + * @return true if successful, otherwise false (z is 1) + */ + public final boolean gluProject(final float objx, final float objy, final float objz, + final int[] viewport, final int viewport_offset, + final float[] win_pos, final int win_pos_offset ) { + return FloatUtil.mapObjToWinCoords(objx, objy, objz, + matrixArray, mMv_offset, + matrixArray, mP_offset, + viewport, viewport_offset, + win_pos, win_pos_offset, + mat4Tmp1, mat4Tmp2); } /** - * Uses this instance {@link #glGetMvMatrixf()} and {@link #glGetPMatrixf()} - * + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * * @param winx * @param winy * @param winz - * @param viewport + * @param viewport 4 component viewport vector * @param viewport_offset - * @param obj_pos + * @param obj_pos 3 component object coordinate, the result * @param obj_pos_offset - * @return - */ - public boolean gluUnProject(float winx, float winy, float winz, - int[] viewport, int viewport_offset, - float[] obj_pos, int obj_pos_offset) { - if(usesBackingArray) { - return projectFloat.gluUnProject(winx, winy, winz, - matrixMv.array(), 0, - matrixP.array(), 0, - viewport, viewport_offset, - obj_pos, obj_pos_offset); - } else { - return projectFloat.gluUnProject(winx, winy, winz, - matrixMv, - matrixP, - viewport, viewport_offset, - obj_pos, obj_pos_offset); - } - } - - public void gluPickMatrix(float x, float y, - float deltaX, float deltaY, - int[] viewport, int viewport_offset) { - projectFloat.gluPickMatrix(this, x, y, deltaX, deltaY, viewport, viewport_offset); - } - - // - // private - // - private int nioBackupArraySupported = 0; // -1 not supported, 0 - TBD, 1 - supported - private final String msgCantComputeInverse = "Invalid source Mv matrix, can't compute inverse"; - - private final void setMviMvit() { - if( 0 != (usesMviMvit & 1) ) { - if(nioBackupArraySupported>=0) { - try { - setMviMvitNIOBackupArray(); - nioBackupArraySupported = 1; - return; - } catch(UnsupportedOperationException uoe) { - nioBackupArraySupported = -1; - } + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public final boolean gluUnProject(final float winx, final float winy, final float winz, + final int[] viewport, final int viewport_offset, + final float[] obj_pos, final int obj_pos_offset) { + return FloatUtil.mapWinToObjCoords(winx, winy, winz, + matrixArray, mMv_offset, + matrixArray, mP_offset, + viewport, viewport_offset, + obj_pos, obj_pos_offset, + mat4Tmp1, mat4Tmp2); + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject4</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param clipw + * @param modelMatrix 4x4 modelview matrix + * @param modelMatrix_offset + * @param projMatrix 4x4 projection matrix + * @param projMatrix_offset + * @param viewport 4 component viewport vector + * @param viewport_offset + * @param near + * @param far + * @param obj_pos 4 component object coordinate, the result + * @param obj_pos_offset + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public boolean gluUnProject4(final float winx, final float winy, final float winz, final float clipw, + final int[] viewport, final int viewport_offset, + final float near, final float far, + final float[] obj_pos, final int obj_pos_offset ) { + return FloatUtil.mapWinToObjCoords(winx, winy, winz, clipw, + matrixArray, mMv_offset, + matrixArray, mP_offset, + viewport, viewport_offset, + near, far, + obj_pos, obj_pos_offset, + mat4Tmp1, mat4Tmp2); + } + + /** + * Make given matrix the <i>pick</i> matrix based on given parameters. + * <p> + * Traditional <code>gluPickMatrix</code> implementation. + * </p> + * <p> + * See {@link FloatUtil#makePick(float[], int, float, float, float, float, int[], int, float[]) FloatUtil.makePick(..)} for details. + * </p> + * @param x the center x-component of a picking region in window coordinates + * @param y the center y-component of a picking region in window coordinates + * @param deltaX the width of the picking region in window coordinates. + * @param deltaY the height of the picking region in window coordinates. + * @param viewport 4 component viewport vector + * @param viewport_offset + */ + public final void gluPickMatrix(final float x, final float y, + final float deltaX, final float deltaY, + final int[] viewport, final int viewport_offset) { + if( null != FloatUtil.makePick(mat4Tmp1, 0, x, y, deltaX, deltaY, viewport, viewport_offset, mat4Tmp2) ) { + glMultMatrixf(mat4Tmp1, 0); + } + } + + /** + * Map two window coordinates w/ shared X/Y and distinctive Z + * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> + * using a {@link AABBox#getRayIntersection(Ray, float[]) bounding box}. + * <p> + * Notes for picking <i>winz0</i> and <i>winz1</i>: + * <ul> + * <li>see {@link FloatUtil#getZBufferEpsilon(int, float, float)}</li> + * <li>see {@link FloatUtil#getZBufferValue(int, float, float, float)}</li> + * <li>see {@link FloatUtil#getOrthoWinZ(float, float, float)}</li> + * </ul> + * </p> + * @param winx + * @param winy + * @param winz0 + * @param winz1 + * @param viewport + * @param viewport_offset + * @param ray storage for the resulting {@link Ray} + * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity) + */ + public final boolean gluUnProjectRay(final float winx, final float winy, final float winz0, final float winz1, + final int[] viewport, final int viewport_offset, + final Ray ray) { + return FloatUtil.mapWinToRay(winx, winy, winz0, winz1, + matrixArray, mMv_offset, + matrixArray, mP_offset, + viewport, viewport_offset, + ray, + mat4Tmp1, mat4Tmp2, mat4Tmp3); + } + + public StringBuilder toString(StringBuilder sb, final String f) { + if(null == sb) { + sb = new StringBuilder(); + } + final boolean mviDirty = 0 != (DIRTY_INVERSE_MODELVIEW & dirtyBits); + final boolean mvitDirty = 0 != (DIRTY_INVERSE_TRANSPOSED_MODELVIEW & dirtyBits); + final boolean frustumDirty = 0 != (DIRTY_FRUSTUM & dirtyBits); + final boolean mviReq = 0 != (DIRTY_INVERSE_MODELVIEW & requestMask); + final boolean mvitReq = 0 != (DIRTY_INVERSE_TRANSPOSED_MODELVIEW & requestMask); + final boolean frustumReq = 0 != (DIRTY_FRUSTUM & requestMask); + final boolean modP = 0 != ( MODIFIED_PROJECTION & modifiedBits ); + final boolean modMv = 0 != ( MODIFIED_MODELVIEW & modifiedBits ); + final boolean modT = 0 != ( MODIFIED_TEXTURE & modifiedBits ); + + sb.append("PMVMatrix[modified[P ").append(modP).append(", Mv ").append(modMv).append(", T ").append(modT); + sb.append("], dirty/req[Mvi ").append(mviDirty).append("/").append(mviReq).append(", Mvit ").append(mvitDirty).append("/").append(mvitReq).append(", Frustum ").append(frustumDirty).append("/").append(frustumReq).append("]").append(PlatformPropsImpl.NEWLINE); + sb.append(", Projection").append(PlatformPropsImpl.NEWLINE); + matrixToString(sb, f, matrixP); + sb.append(", Modelview").append(PlatformPropsImpl.NEWLINE); + matrixToString(sb, f, matrixMv); + sb.append(", Texture").append(PlatformPropsImpl.NEWLINE); + matrixToString(sb, f, matrixTex); + if( 0 != ( requestMask & DIRTY_INVERSE_MODELVIEW ) ) { + sb.append(", Inverse Modelview").append(PlatformPropsImpl.NEWLINE); + matrixToString(sb, f, matrixMvi); + } + if( 0 != ( requestMask & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) { + sb.append(", Inverse Transposed Modelview").append(PlatformPropsImpl.NEWLINE); + matrixToString(sb, f, matrixMvit); + } + sb.append("]"); + return sb; + } + + @Override + public String toString() { + return toString(null, "%10.5f").toString(); + } + + /** + * Returns the modified bits due to mutable operations.. + * <p> + * A modified bit is set, if the corresponding matrix had been modified by a mutable operation + * since last {@link #update()} or {@link #getModifiedBits(boolean) getModifiedBits(true)} call. + * </p> + * @param clear if true, clears the modified bits, otherwise leaves them untouched. + * + * @see #MODIFIED_PROJECTION + * @see #MODIFIED_MODELVIEW + * @see #MODIFIED_TEXTURE + */ + public final int getModifiedBits(final boolean clear) { + final int r = modifiedBits; + if(clear) { + modifiedBits = 0; + } + return r; + } + + /** + * Returns the dirty bits due to mutable operations. + * <p> + * A dirty bit is set , if the corresponding matrix had been modified by a mutable operation + * since last {@link #update()} call. The latter clears the dirty state only if the dirty matrix (Mvi or Mvit) or {@link Frustum} + * has been requested by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} + * or {@link #glGetFrustum() Frustum get} methods. + * </p> + * + * @deprecated Function is exposed for debugging purposes only. + * @see #DIRTY_INVERSE_MODELVIEW + * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW + * @see #DIRTY_FRUSTUM + * @see #glGetMviMatrixf() + * @see #glGetMvitMatrixf() + * @see #glGetPMvMviMatrixf() + * @see #glGetPMvMvitMatrixf() + * @see #glGetFrustum() + */ + public final int getDirtyBits() { + return dirtyBits; + } + + /** + * Returns the request bit mask, which uses bit values equal to the dirty mask. + * <p> + * The request bit mask is set by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} + * or {@link #glGetFrustum() Frustum get} methods. + * </p> + * + * @deprecated Function is exposed for debugging purposes only. + * @see #clearAllUpdateRequests() + * @see #DIRTY_INVERSE_MODELVIEW + * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW + * @see #DIRTY_FRUSTUM + * @see #glGetMviMatrixf() + * @see #glGetMvitMatrixf() + * @see #glGetPMvMviMatrixf() + * @see #glGetPMvMvitMatrixf() + * @see #glGetFrustum() + */ + public final int getRequestMask() { + return requestMask; + } + + + /** + * Clears all {@link #update()} requests of the Mvi and Mvit matrix and Frustum + * after it has been enabled by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} + * or {@link #glGetFrustum() Frustum get} methods. + * <p> + * Allows user to disable subsequent Mvi, Mvit and {@link Frustum} updates if no more required. + * </p> + * + * @see #glGetMviMatrixf() + * @see #glGetMvitMatrixf() + * @see #glGetPMvMviMatrixf() + * @see #glGetPMvMvitMatrixf() + * @see #glGetFrustum() + * @see #getRequestMask() + */ + public final void clearAllUpdateRequests() { + requestMask &= ~DIRTY_ALL; + } + + /** + * Update the derived {@link #glGetMviMatrixf() inverse modelview (Mvi)}, + * {@link #glGetMvitMatrixf() inverse transposed modelview (Mvit)} matrices and {@link Frustum} + * <b>if</b> they are dirty <b>and</b> they were requested + * by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} + * or {@link #glGetFrustum() Frustum get} methods. + * <p> + * The Mvi and Mvit matrices and {@link Frustum} are considered dirty, if their corresponding + * {@link #glGetMvMatrixf() Mv matrix} has been modified since their last update. + * </p> + * <p> + * Method should be called manually in case mutable operations has been called + * and caller operates on already fetched references, i.e. not calling + * {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} + * or {@link #glGetFrustum() Frustum get} etc anymore. + * </p> + * <p> + * This method clears the modified bits like {@link #getModifiedBits(boolean) getModifiedBits(true)}, + * which are set by any mutable operation. The modified bits have no impact + * on this method, but the return value. + * </p> + * + * @return true if any matrix has been modified since last update call or + * if the derived matrices Mvi and Mvit or {@link Frustum} were updated, otherwise false. + * In other words, method returns true if any matrix used by the caller must be updated, + * e.g. uniforms in a shader program. + * + * @see #getModifiedBits(boolean) + * @see #MODIFIED_PROJECTION + * @see #MODIFIED_MODELVIEW + * @see #MODIFIED_TEXTURE + * @see #DIRTY_INVERSE_MODELVIEW + * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW + * @see #DIRTY_FRUSTUM + * @see #glGetMviMatrixf() + * @see #glGetMvitMatrixf() + * @see #glGetPMvMviMatrixf() + * @see #glGetPMvMvitMatrixf() + * @see #glGetFrustum() + * @see #clearAllUpdateRequests() + */ + public final boolean update() { + return updateImpl(true); + } + private final boolean updateImpl(final boolean clearModBits) { + boolean mod = 0 != modifiedBits; + if(clearModBits) { + modifiedBits = 0; + } + + if( 0 != ( dirtyBits & ( DIRTY_FRUSTUM & requestMask ) ) ) { + if( null == frustum ) { + frustum = new Frustum(); } - setMviMvitNIODirectAccess(); + FloatUtil.multMatrix(matrixArray, mP_offset, matrixArray, mMv_offset, mat4Tmp1, 0); + // FloatUtil.multMatrix(matrixP, matrixMv, mat4Tmp1, 0); + frustum.updateByPMV(mat4Tmp1, 0); + dirtyBits &= ~DIRTY_FRUSTUM; + mod = true; } + + if( 0 == ( dirtyBits & requestMask ) ) { + return mod; // nothing more requested which may have been dirty + } + + return setMviMvit() || mod; } - private final void setMviMvitNIOBackupArray() { + + // + // private + // + private static final String msgCantComputeInverse = "Invalid source Mv matrix, can't compute inverse"; + + private final boolean setMviMvit() { final float[] _matrixMvi = matrixMvi.array(); final int _matrixMviOffset = matrixMvi.position(); - if(!projectFloat.gluInvertMatrixf(matrixMv.array(), matrixMv.position(), _matrixMvi, _matrixMviOffset)) { - throw new GLException(msgCantComputeInverse); - } - if( 0 != (usesMviMvit & 2) ) { - // transpose matrix - final float[] _matrixMvit = matrixMvit.array(); - final int _matrixMvitOffset = matrixMvit.position(); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - _matrixMvit[_matrixMvitOffset+j+i*4] = _matrixMvi[_matrixMviOffset+i+j*4]; - } + boolean res = false; + if( 0 != ( dirtyBits & DIRTY_INVERSE_MODELVIEW ) ) { // only if dirt; always requested at this point, see update() + if( null == FloatUtil.invertMatrix(matrixArray, mMv_offset, _matrixMvi, _matrixMviOffset) ) { + throw new GLException(msgCantComputeInverse); } - } - } - - private final void setMviMvitNIODirectAccess() { - if(!projectFloat.gluInvertMatrixf(matrixMv, matrixMvi)) { - throw new GLException(msgCantComputeInverse); + dirtyBits &= ~DIRTY_INVERSE_MODELVIEW; + res = true; } - if( 0 != (usesMviMvit & 2) ) { - // transpose matrix - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - matrixMvit.put(j+i*4, matrixMvi.get(i+j*4)); - } - } - } - } - - protected final boolean usesBackingArray; - protected Buffer matrixBuffer; - protected FloatBuffer matrixIdent, matrixPMvMvit, matrixPMvMvi, matrixPMv, matrixP, matrixTex, matrixMv, matrixMvi, matrixMvit; - protected float[] matrixMult, matrixTrans, matrixRot, matrixScale, matrixOrtho, matrixFrustum, vec3f; - protected List<float[]> matrixTStack, matrixPStack, matrixMvStack; - protected int matrixMode = GL_MODELVIEW; - protected int modified = 0; - protected int usesMviMvit = 0; // 0 - none, 1 - Mvi, 2 - Mvit, 3 - MviMvit (ofc no Mvit w/o Mvi!) - protected ProjectFloat projectFloat; - - public static final int DIRTY_MODELVIEW = 1 << 0; - public static final int DIRTY_PROJECTION = 1 << 1; - public static final int DIRTY_TEXTURE = 1 << 2; + if( 0 != ( requestMask & ( dirtyBits & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty + FloatUtil.transposeMatrix(_matrixMvi, _matrixMviOffset, matrixMvit.array(), matrixMvit.position()); + dirtyBits &= ~DIRTY_INVERSE_TRANSPOSED_MODELVIEW; + res = true; + } + return res; + } + + private final float[] matrixArray; + private final int mP_offset, mMv_offset, mTex_offset; + private final FloatBuffer matrixPMvMvit, matrixPMvMvi, matrixPMv, matrixP, matrixTex, matrixMv, matrixMvi, matrixMvit; + private final float[] matrixTxSx; + private final float[] mat4Tmp1, mat4Tmp2, mat4Tmp3; + private final FloatStack matrixTStack, matrixPStack, matrixMvStack; + private int matrixMode = GL_MODELVIEW; + private int modifiedBits = MODIFIED_ALL; + private int dirtyBits = DIRTY_ALL; // contains the dirty bits, i.e. hinting for update operation + private int requestMask = 0; // may contain the requested dirty bits: DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW + private Frustum frustum; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java new file mode 100644 index 000000000..64da547c2 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/PNGPixelRect.java @@ -0,0 +1,401 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; +import javax.media.nativewindow.util.PixelRectangle; +import javax.media.nativewindow.util.PixelFormatUtil; + +import jogamp.opengl.Debug; +import jogamp.opengl.util.pngj.ImageInfo; +import jogamp.opengl.util.pngj.ImageLine; +import jogamp.opengl.util.pngj.ImageLineHelper; +import jogamp.opengl.util.pngj.PngReader; +import jogamp.opengl.util.pngj.PngWriter; +import jogamp.opengl.util.pngj.chunks.PngChunkPLTE; +import jogamp.opengl.util.pngj.chunks.PngChunkTRNS; +import jogamp.opengl.util.pngj.chunks.PngChunkTextVar; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; + +public class PNGPixelRect extends PixelRectangle.GenericPixelRect { + private static final boolean DEBUG = Debug.debug("PNG"); + + /** + * Reads a PNG image from the specified InputStream. + * <p> + * Implicitly converts the image to match the desired: + * <ul> + * <li>{@link PixelFormat}, see {@link #getPixelformat()}</li> + * <li><code>destStrideInBytes</code>, see {@link #getStride()}</li> + * <li><code>destIsGLOriented</code>, see {@link #isGLOriented()}</li> + * </ul> + * </p> + * + * @param in input stream + * @param destFmt desired destination {@link PixelFormat} incl. conversion, maybe <code>null</code> to use source {@link PixelFormat} + * @param destDirectBuffer if true, using a direct NIO buffer, otherwise an array backed buffer + * @param destMinStrideInBytes used if greater than PNG's stride, otherwise using PNG's stride. Stride is width * bytes-per-pixel. + * @param destIsGLOriented + * @return the newly created PNGPixelRect instance + * @throws IOException + */ + public static PNGPixelRect read(final InputStream in, + final PixelFormat ddestFmt, final boolean destDirectBuffer, final int destMinStrideInBytes, + final boolean destIsGLOriented) throws IOException { + final PngReader pngr = new PngReader(new BufferedInputStream(in), null); + final ImageInfo imgInfo = pngr.imgInfo; + final PngChunkPLTE plte = pngr.getMetadata().getPLTE(); + final PngChunkTRNS trns = pngr.getMetadata().getTRNS(); + final boolean indexed = imgInfo.indexed; + final boolean hasAlpha = indexed ? ( trns != null ) : imgInfo.alpha ; + + if(DEBUG) { + System.err.println("PNGPixelRect: "+imgInfo); + } + final int channels = indexed ? ( hasAlpha ? 4 : 3 ) : imgInfo.channels ; + final boolean isGrayAlpha = 2 == channels && imgInfo.greyscale && imgInfo.alpha; + if ( ! ( 1 == channels || 3 == channels || 4 == channels || isGrayAlpha ) ) { + throw new RuntimeException("PNGPixelRect can only handle Lum/RGB/RGBA [1/3/4 channels] or Lum+A (GA) images for now. Channels "+channels + " Paletted: " + indexed); + } + final int bytesPerPixel = indexed ? channels : imgInfo.bytesPixel ; + if ( ! ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel || isGrayAlpha ) ) { + throw new RuntimeException("PNGPixelRect can only handle Lum/RGB/RGBA [1/3/4 bpp] images for now. BytesPerPixel "+bytesPerPixel); + } + if( channels != bytesPerPixel ) { + throw new RuntimeException("PNGPixelRect currently only handles Channels [1/3/4] == BytePerPixel [1/3/4], channels: "+channels+", bytesPerPixel "+bytesPerPixel); + } + final int width = imgInfo.cols; + final int height = imgInfo.rows; + final double dpiX, dpiY; + { + final double[] dpi = pngr.getMetadata().getDpi(); + dpiX = dpi[0]; + dpiY = dpi[1]; + } + final PixelFormat srcFmt; + if ( indexed ) { + if ( hasAlpha ) { + srcFmt = PixelFormat.RGBA8888; + } else { + srcFmt = PixelFormat.RGB888; + } + } else { + switch( channels ) { + case 1: srcFmt = PixelFormat.LUMINANCE; break; + case 2: srcFmt = isGrayAlpha ? PixelFormat.LUMINANCE : null; break; + case 3: srcFmt = PixelFormat.RGB888; break; + case 4: srcFmt = PixelFormat.RGBA8888; break; + default: srcFmt = null; + } + if( null == srcFmt ) { + throw new InternalError("XXX: channels: "+channels+", bytesPerPixel "+bytesPerPixel); + } + } + final PixelFormat destFmt; + if( null == ddestFmt ) { + if( isGrayAlpha ) { + destFmt = PixelFormat.BGRA8888; // save alpha value on gray-alpha + } else { + destFmt = srcFmt; // 1:1 + } + } else { + destFmt = ddestFmt; // user choice + } + final int destStrideInBytes = Math.max(destMinStrideInBytes, destFmt.bytesPerPixel() * width); + final ByteBuffer destPixels = destDirectBuffer ? Buffers.newDirectByteBuffer(destStrideInBytes * height) : + ByteBuffer.allocate(destStrideInBytes * height); + { + final int reqBytes = destStrideInBytes * height; + if( destPixels.limit() < reqBytes ) { + throw new IndexOutOfBoundsException("Dest buffer has insufficient bytes left, needs "+reqBytes+": "+destPixels); + } + } + final boolean vert_flip = destIsGLOriented; + + int[] rgbaScanline = indexed ? new int[width * channels] : null; + if(DEBUG) { + System.err.println("PNGPixelRect: indexed "+indexed+", alpha "+hasAlpha+", grayscale "+imgInfo.greyscale+", channels "+channels+"/"+imgInfo.channels+ + ", bytesPerPixel "+bytesPerPixel+"/"+imgInfo.bytesPixel+ + ", grayAlpha "+isGrayAlpha+", pixels "+width+"x"+height+", dpi "+dpiX+"x"+dpiY+", format "+srcFmt); + System.err.println("PNGPixelRect: destFormat "+destFmt+" ("+ddestFmt+", bytesPerPixel "+destFmt.bytesPerPixel()+", fast-path "+(destFmt==srcFmt)+"), destDirectBuffer "+destDirectBuffer+", destIsGLOriented (flip) "+destIsGLOriented); + System.err.println("PNGPixelRect: destStrideInBytes "+destStrideInBytes+" (destMinStrideInBytes "+destMinStrideInBytes+")"); + } + + for (int row = 0; row < height; row++) { + final ImageLine l1 = pngr.readRow(row); + int lineOff = 0; + int dataOff = vert_flip ? ( height - 1 - row ) * destStrideInBytes : row * destStrideInBytes; + if( indexed ) { + for (int j = width - 1; j >= 0; j--) { + rgbaScanline = ImageLineHelper.palette2rgb(l1, plte, trns, rgbaScanline); // reuse rgbaScanline and update if resized + dataOff = getPixelRGBA8ToAny(destFmt, destPixels, dataOff, rgbaScanline, lineOff, hasAlpha); + lineOff += bytesPerPixel; + } + } else if( 1 == channels ) { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelLUMToAny(destFmt, destPixels, dataOff, (byte)l1.scanline[lineOff++], (byte)0xff); // Luminance, 1 bytesPerPixel + } + } else if( isGrayAlpha ) { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelLUMToAny(destFmt, destPixels, dataOff, (byte)l1.scanline[lineOff++], (byte)l1.scanline[lineOff++]); // Luminance+Alpha, 2 bytesPerPixel + } + } else if( srcFmt == destFmt ) { // fast-path + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelRGBSame(destPixels, dataOff, l1.scanline, lineOff, bytesPerPixel); + lineOff += bytesPerPixel; + } + } else { + for (int j = width - 1; j >= 0; j--) { + dataOff = getPixelRGBA8ToAny(destFmt, destPixels, dataOff, l1.scanline, lineOff, hasAlpha); + lineOff += bytesPerPixel; + } + } + } + pngr.end(); + + return new PNGPixelRect(destFmt, new Dimension(width, height), destStrideInBytes, destIsGLOriented, destPixels, dpiX, dpiY); + } + + private static final int getPixelLUMToAny(final PixelFormat dest_fmt, final ByteBuffer d, int dOff, final byte lum, final byte alpha) { + switch(dest_fmt) { + case LUMINANCE: + d.put(dOff++, lum); + break; + case BGR888: + case RGB888: + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + break; + case ABGR8888: + case ARGB8888: + d.put(dOff++, alpha); // A + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + break; + case BGRA8888: + case RGBA8888: + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, lum); + d.put(dOff++, alpha); // A + break; + default: + throw new InternalError("Unhandled format "+dest_fmt); + } + return dOff; + } + private static final int getPixelRGBA8ToAny(final PixelFormat dest_fmt, final ByteBuffer d, int dOff, final int[] scanline, final int lineOff, final boolean srcHasAlpha) { + final int p = PixelFormatUtil.convertToInt32(dest_fmt, (byte)scanline[lineOff], // R + (byte)scanline[lineOff+1], // G + (byte)scanline[lineOff+2], // B + srcHasAlpha ? (byte)scanline[lineOff+3] : (byte)0xff); // A + final int dbpp = dest_fmt.bytesPerPixel(); + d.put(dOff++, (byte) ( p )); // 1 + if( 1 < dbpp ) { + d.put(dOff++, (byte) ( p >>> 8 )); // 2 + d.put(dOff++, (byte) ( p >>> 16 )); // 3 + if( 4 == dbpp ) { + d.put(dOff++, (byte) ( p >>> 24 )); // 4 + } + } + return dOff; + } + private static final int getPixelRGBSame(final ByteBuffer d, int dOff, final int[] scanline, final int lineOff, final int bpp) { + d.put(dOff++, (byte)scanline[lineOff]); // R + if( 1 < bpp ) { + d.put(dOff++, (byte)scanline[lineOff + 1]); // G + d.put(dOff++, (byte)scanline[lineOff + 2]); // B + if( 4 == bpp ) { + d.put(dOff++, (byte)scanline[lineOff + 3]); // A + } + } + return dOff; + } + private int setPixelRGBA8(final ImageLine line, final int lineOff, final ByteBuffer src, final int srcOff, final int bytesPerPixel, final boolean hasAlpha) { + final int b = hasAlpha ? 4-1 : 3-1; + if( src.limit() <= srcOff + b ) { + throw new IndexOutOfBoundsException("Buffer has unsufficient bytes left, needs ["+srcOff+".."+(srcOff+b)+"]: "+src); + } + final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, src, srcOff); + line.scanline[lineOff ] = 0xff & p; // R + line.scanline[lineOff + 1] = 0xff & ( p >>> 8 ); // G + line.scanline[lineOff + 2] = 0xff & ( p >>> 16 ); // B + if(hasAlpha) { + line.scanline[lineOff + 3] = 0xff & ( p >>> 24 ); // A + } + return srcOff + pixelformat.bytesPerPixel(); + } + + private static void setPixelRGBA8(final PixelFormat pixelformat, final ImageLine line, final int lineOff, final int srcPix, final int bytesPerPixel, final boolean hasAlpha) { + final int p = PixelFormatUtil.convertToInt32(hasAlpha ? PixelFormat.RGBA8888 : PixelFormat.RGB888, pixelformat, srcPix); + line.scanline[lineOff ] = 0xff & p; // R + line.scanline[lineOff + 1] = 0xff & ( p >>> 8 ); // G + line.scanline[lineOff + 2] = 0xff & ( p >>> 16 ); // B + if(hasAlpha) { + line.scanline[lineOff + 3] = 0xff & ( p >>> 24 ); // A + } + } + + /** + * Creates a PNGPixelRect from data supplied by the end user. Shares + * data with the passed ByteBuffer. + * + * @param pixelformat + * @param size + * @param strideInBytes + * @param isGLOriented see {@link #isGLOriented()}. + * @param pixels + * @param dpiX + * @param dpiY + */ + public PNGPixelRect(final PixelFormat pixelformat, final DimensionImmutable size, + final int strideInBytes, final boolean isGLOriented, final ByteBuffer pixels, + final double dpiX, final double dpiY) { + super(pixelformat, size, strideInBytes, isGLOriented, pixels); + this.dpi = new double[] { dpiX, dpiY }; + } + public PNGPixelRect(final PixelRectangle src, final double dpiX, final double dpiY) { + super(src); + this.dpi = new double[] { dpiX, dpiY }; + } + private final double[] dpi; + + /** Returns the dpi of the image. */ + public double[] getDpi() { return dpi; } + + public void write(final OutputStream outstream, final boolean closeOutstream) throws IOException { + final int width = size.getWidth(); + final int height = size.getHeight(); + final int bytesPerPixel = pixelformat.bytesPerPixel(); + final ImageInfo imi = new ImageInfo(width, height, 8 /* bitdepth */, + (4 == bytesPerPixel) ? true : false /* alpha */, + (1 == bytesPerPixel) ? true : false /* grayscale */, + false /* indexed */); + + // open image for writing to a output stream + try { + final PngWriter png = new PngWriter(outstream, imi); + // add some optional metadata (chunks) + png.getMetadata().setDpi(dpi[0], dpi[1]); + png.getMetadata().setTimeNow(0); // 0 seconds from now = now + png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGPixelRect"); + final boolean hasAlpha = 4 == bytesPerPixel; + + final ImageLine l1 = new ImageLine(imi); + for (int row = 0; row < height; row++) { + int dataOff = isGLOriented ? ( height - 1 - row ) * strideInBytes : row * strideInBytes; + int lineOff = 0; + if(1 == bytesPerPixel) { + for (int j = width - 1; j >= 0; j--) { + l1.scanline[lineOff++] = pixels.get(dataOff++); // // Luminance, 1 bytesPerPixel + } + } else { + for (int j = width - 1; j >= 0; j--) { + dataOff = setPixelRGBA8(l1, lineOff, pixels, dataOff, bytesPerPixel, hasAlpha); + lineOff += bytesPerPixel; + } + } + png.writeRow(l1, row); + } + png.end(); + } finally { + if( closeOutstream ) { + IOUtil.close(outstream, false); + } + } + } + + public static void write(final PixelFormat pixelformat, final DimensionImmutable size, + int strideInPixels, final boolean isGLOriented, final IntBuffer pixels, + final double dpiX, final double dpiY, + final OutputStream outstream, final boolean closeOutstream) throws IOException { + final int width = size.getWidth(); + final int height = size.getHeight(); + final int bytesPerPixel = pixelformat.bytesPerPixel(); + final ImageInfo imi = new ImageInfo(width, height, 8 /* bitdepth */, + (4 == bytesPerPixel) ? true : false /* alpha */, + (1 == bytesPerPixel) ? true : false /* grayscale */, + false /* indexed */); + if( 0 != strideInPixels ) { + if( strideInPixels < size.getWidth()) { + throw new IllegalArgumentException("Invalid stride "+bytesPerPixel+", must be greater than width "+size.getWidth()); + } + } else { + strideInPixels = size.getWidth(); + } + final int reqPixels = strideInPixels * size.getHeight(); + if( pixels.limit() < reqPixels ) { + throw new IndexOutOfBoundsException("Dest buffer has insufficient pixels left, needs "+reqPixels+": "+pixels); + } + + // open image for writing to a output stream + try { + final PngWriter png = new PngWriter(outstream, imi); + // add some optional metadata (chunks) + png.getMetadata().setDpi(dpiX, dpiY); + png.getMetadata().setTimeNow(0); // 0 seconds from now = now + png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGPixelRect"); + final boolean hasAlpha = 4 == bytesPerPixel; + + final ImageLine l1 = new ImageLine(imi); + for (int row = 0; row < height; row++) { + int dataOff = isGLOriented ? ( height - 1 - row ) * strideInPixels : row * strideInPixels; + int lineOff = 0; + if(1 == bytesPerPixel) { + for (int j = width - 1; j >= 0; j--) { + l1.scanline[lineOff++] = pixels.get(dataOff++); // // Luminance, 1 bytesPerPixel + } + } else { + for (int j = width - 1; j >= 0; j--) { + setPixelRGBA8(pixelformat, l1, lineOff, pixels.get(dataOff++), bytesPerPixel, hasAlpha); + lineOff += bytesPerPixel; + } + } + png.writeRow(l1, row); + } + png.end(); + } finally { + if( closeOutstream ) { + IOUtil.close(outstream, false); + } + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java new file mode 100644 index 000000000..3b65b0824 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java @@ -0,0 +1,236 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLException; + +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; + +/** + * Variation of {@link TileRenderer} w/o using fixed tiles but arbitrary rectangular regions. + * <p> + * See {@link TileRendererBase} for details. + * </p> + */ +public class RandomTileRenderer extends TileRendererBase { + private boolean tileRectSet = false; + + /** + * Creates a new TileRenderer object + */ + public RandomTileRenderer() { + super(); + } + + @Override + public final int getParam(final int pname) { + switch (pname) { + case TR_IMAGE_WIDTH: + return imageSize.getWidth(); + case TR_IMAGE_HEIGHT: + return imageSize.getHeight(); + case TR_CURRENT_TILE_X_POS: + return currentTileXPos; + case TR_CURRENT_TILE_Y_POS: + return currentTileYPos; + case TR_CURRENT_TILE_WIDTH: + return currentTileWidth; + case TR_CURRENT_TILE_HEIGHT: + return currentTileHeight; + default: + throw new IllegalArgumentException("Invalid pname: "+pname); + } + } + + /** + * Set the tile rectangle for the subsequent rendering calls. + * + * @throws IllegalArgumentException is tile x/y are < 0 or tile size is <= 0x0 + */ + public void setTileRect(final int tX, final int tY, final int tWidth, final int tHeight) throws IllegalStateException, IllegalArgumentException { + if( 0 > tX || 0 > tY ) { + throw new IllegalArgumentException("Tile pos must be >= 0/0"); + } + if( 0 >= tWidth || 0 >= tHeight ) { + throw new IllegalArgumentException("Tile size must be > 0x0"); + } + this.currentTileXPos = tX; + this.currentTileYPos = tY; + this.currentTileWidth = tWidth; + this.currentTileHeight = tHeight; + tileRectSet = true; + } + + @Override + public final boolean isSetup() { + return 0 < imageSize.getWidth() && 0 < imageSize.getHeight() && tileRectSet; + } + + /** + * {@inheritDoc} + * + * <p> + * <i>end of tiling</i> is never reached w/ {@link RandomRileRenderer}, + * i.e. method always returns false. + * </p> + */ + @Override + public final boolean eot() { return false; } + + /** + * {@inheritDoc} + * + * Reset internal states of {@link RandomTileRenderer} are: <i>none</i>. + */ + @Override + public final void reset() { } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if {@link #setImageSize(int, int) image-size} has not been set or + * {@link #setTileRect(int, int, int, int) tile-rect} has not been set. + */ + @Override + public final void beginTile(final GL gl) throws IllegalStateException, GLException { + if( 0 >= imageSize.getWidth() || 0 >= imageSize.getHeight() ) { + throw new IllegalStateException("Image size has not been set"); + } + if( !tileRectSet ) { + throw new IllegalStateException("tileRect has not been set"); + } + validateGL(gl); + + gl.glViewport( 0, 0, currentTileWidth, currentTileHeight ); + + if( DEBUG ) { + System.err.println("TileRenderer.begin.X: "+this.toString()); + } + + // Do not forget to issue: + // reshape( 0, 0, tW, tH ); + // which shall reflect tile renderer fileds: currentTileXPos, currentTileYPos and imageSize + + beginCalled = true; + } + + @Override + public void endTile( final GL gl ) throws IllegalStateException, GLException { + if( !beginCalled ) { + throw new IllegalStateException("beginTile(..) has not been called"); + } + validateGL(gl); + + // be sure OpenGL rendering is finished + gl.glFlush(); + + // implicitly save current glPixelStore values + psm.setPackAlignment(gl, 1); + final GL2ES3 gl2es3; + final int readBuffer; + if( gl.isGL2ES3() ) { + gl2es3 = gl.getGL2ES3(); + readBuffer = gl2es3.getDefaultReadBuffer(); + gl2es3.glReadBuffer(readBuffer); + } else { + gl2es3 = null; + readBuffer = 0; // undef. probably default: GL_FRONT (single buffering) GL_BACK (double buffering) + } + if( DEBUG ) { + System.err.println("TileRenderer.end.0: readBuffer 0x"+Integer.toHexString(readBuffer)+", "+this.toString()); + } + + final int tmp[] = new int[1]; + + if( tileBuffer != null ) { + final GLPixelAttributes pixelAttribs = tileBuffer.pixelAttributes; + final int srcX = 0; + final int srcY = 0; + final int srcWidth = currentTileWidth; + final int srcHeight = currentTileHeight; + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + tileBuffer.clear(); + if( tileBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { + throw new IndexOutOfBoundsException("Required " + readPixelSize + " bytes of buffer, only had " + tileBuffer); + } + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, pixelAttribs.format, pixelAttribs.type, tileBuffer.buffer); + // be sure OpenGL rendering is finished + gl.glFlush(); + tileBuffer.position( readPixelSize ); + tileBuffer.flip(); + } + + if( imageBuffer != null ) { + final GLPixelAttributes pixelAttribs = imageBuffer.pixelAttributes; + final int srcX = 0; + final int srcY = 0; + final int srcWidth = currentTileWidth; + final int srcHeight = currentTileHeight; + + /* setup pixel store for glReadPixels */ + final int rowLength = imageSize.getWidth(); + psm.setPackRowLength(gl2es3, rowLength); + + /* read the tile into the final image */ + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + + final int ibPos = ( currentTileXPos + ( currentTileYPos * rowLength ) ) * pixelAttribs.bytesPerPixel; // skipPixels + skipRows + final int ibLim = ibPos + readPixelSize; + imageBuffer.clear(); + if( imageBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { + throw new IndexOutOfBoundsException("Required " + ibLim + " bytes of buffer, only had " + imageBuffer); + } + imageBuffer.position(ibPos); + + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, pixelAttribs.format, pixelAttribs.type, imageBuffer.buffer); + // be sure OpenGL rendering is finished + gl.glFlush(); + imageBuffer.position( ibLim ); + imageBuffer.flip(); + } + + /* restore previous glPixelStore values */ + psm.restore(gl); + + beginCalled = false; + } + + /** + * Rendering one tile, by simply calling {@link GLAutoDrawable#display()}. + * + * @throws IllegalStateException if no {@link GLAutoDrawable} is {@link #attachAutoDrawable(GLAutoDrawable) attached} + * or imageSize is not set + */ + public void display(final int tX, final int tY, final int tWidth, final int tHeight) throws IllegalStateException { + setTileRect(tX, tY, tWidth, tHeight); + display(); + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/TGAWriter.java b/src/jogl/classes/com/jogamp/opengl/util/TGAWriter.java deleted file mode 100644 index b949f0e39..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/TGAWriter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - */ - -package com.jogamp.opengl.util; - -import java.io.*; -import java.nio.*; -import java.nio.channels.*; - -/** - * Utility class which helps take fast screenshots of OpenGL rendering - * results into Targa-format files. Used by the {@link com.jogamp.opengl.util.awt.Screenshot} - * class; can also be used in conjunction with the {@link com.jogamp.opengl.util.gl2.TileRenderer} class. - */ -public class TGAWriter { - - private static final int TARGA_HEADER_SIZE = 18; - - private FileChannel ch; - private ByteBuffer buf; - - /** Constructor for the TGAWriter. */ - public TGAWriter() { - } - - /** - * Opens the specified Targa file for writing, overwriting any - * existing file, and sets up the header of the file expecting the - * data to be filled in before closing it. - * - * @param file the file to write containing the screenshot - * @param width the width of the current drawable - * @param height the height of the current drawable - * @param alpha whether the alpha channel should be saved. If true, - * requires GL_EXT_abgr extension to be present. - * - * @throws IOException if an I/O error occurred while writing the - * file - */ - public void open(File file, - int width, - int height, - boolean alpha) throws IOException { - RandomAccessFile out = new RandomAccessFile(file, "rw"); - ch = out.getChannel(); - int pixelSize = (alpha ? 32 : 24); - int numChannels = (alpha ? 4 : 3); - - int fileLength = TARGA_HEADER_SIZE + width * height * numChannels; - out.setLength(fileLength); - MappedByteBuffer image = ch.map(FileChannel.MapMode.READ_WRITE, 0, fileLength); - - // write the TARGA header - image.put(0, (byte) 0).put(1, (byte) 0); - image.put(2, (byte) 2); // uncompressed type - image.put(12, (byte) (width & 0xFF)); // width - image.put(13, (byte) (width >> 8)); // width - image.put(14, (byte) (height & 0xFF)); // height - image.put(15, (byte) (height >> 8)); // height - image.put(16, (byte) pixelSize); // pixel size - - // go to image data position - image.position(TARGA_HEADER_SIZE); - // jogl needs a sliced buffer - buf = image.slice(); - } - - /** - * Returns the ByteBuffer corresponding to the data for the image. - * This must be filled in with data in either BGR or BGRA format - * depending on whether an alpha channel was specified during - * open(). - */ - public ByteBuffer getImageData() { - return buf; - } - - public void close() throws IOException { - // close the file channel - ch.close(); - buf = null; - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java new file mode 100644 index 000000000..d8410a102 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java @@ -0,0 +1,536 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + * + * --------------------- + * + * Based on Brian Paul's tile rendering library, found + * at <a href = "http://www.mesa3d.org/brianp/TR.html">http://www.mesa3d.org/brianp/TR.html</a>. + * + * Copyright (C) 1997-2005 Brian Paul. + * Licensed under BSD-compatible terms with permission of the author. + * See LICENSE.txt for license information. + */ +package com.jogamp.opengl.util; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GLException; + +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; + +/** + * A fairly direct port of Brian Paul's tile rendering library, found + * at <a href = "http://www.mesa3d.org/brianp/TR.html"> + * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but + * the functionality is the same. + * <p> + * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under + * BSD-compatible terms with permission of the author. See LICENSE.txt + * for license information. + * </p> + * <p> + * Enhanced for {@link GL2ES3}. + * </p> + * <p> + * See {@link TileRendererBase} for details. + * </p> + * + * @author ryanm, sgothel + */ +public class TileRenderer extends TileRendererBase { + /** + * The width of the final clipped image. See {@link #getParam(int)}. + */ + public static final int TR_IMAGE_CLIPPING_WIDTH = 7; + /** + * The height of the final clipped image. See {@link #getParam(int)}. + */ + public static final int TR_IMAGE_CLIPPING_HEIGHT = 8; + /** + * The width of the tiles. See {@link #getParam(int)}. + */ + public static final int TR_TILE_WIDTH = 9; + /** + * The height of the tiles. See {@link #getParam(int)}. + */ + public static final int TR_TILE_HEIGHT = 10; + /** + * The width of the border around the tiles. See {@link #getParam(int)}. + */ + public static final int TR_TILE_BORDER = 11; + /** + * The tiles x-offset. See {@link #getParam(int)}. + */ + public static final int TR_TILE_X_OFFSET = 12; + /** + * The tiles y-offset. See {@link #getParam(int)}. + */ + public static final int TR_TILE_Y_OFFSET = 13; + /** + * The number of rows of tiles. See {@link #getParam(int)}. + */ + public static final int TR_ROWS = 14; + /** + * The number of columns of tiles. See {@link #getParam(int)}. + */ + public static final int TR_COLUMNS = 15; + /** + * The current tile number. Has value -1 if {@link #eot()}. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_TILE_NUM = 16; + /** + * The current row number. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_ROW = 17; + /** + * The current column number. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_COLUMN = 18; + /** + * The order that the rows are traversed. See {@link #getParam(int)}. + */ + public static final int TR_ROW_ORDER = 19; + /** + * Indicates we are traversing rows from the top to the bottom. See {@link #getParam(int)}. + */ + public static final int TR_TOP_TO_BOTTOM = 20; + /** + * Indicates we are traversing rows from the bottom to the top (default). See {@link #getParam(int)}. + */ + public static final int TR_BOTTOM_TO_TOP = 21; + + private static final int DEFAULT_TILE_WIDTH = 256; + private static final int DEFAULT_TILE_HEIGHT = 256; + private static final int DEFAULT_TILE_BORDER = 0; + + private final Dimension tileSize = new Dimension(DEFAULT_TILE_WIDTH, DEFAULT_TILE_HEIGHT); + private final Dimension tileSizeNB = new Dimension(DEFAULT_TILE_WIDTH - 2 * DEFAULT_TILE_BORDER, DEFAULT_TILE_HEIGHT - 2 * DEFAULT_TILE_BORDER); + + private boolean isInit = false; + private Dimension imageClippingDim = null; // not set - default + private int tileBorder = DEFAULT_TILE_BORDER; + private int rowOrder = TR_BOTTOM_TO_TOP; + private int rows; + private int columns; + private int currentTile = 0; + private int currentRow; + private int currentColumn; + private int offsetX; + private int offsetY; + + @Override + protected StringBuilder tileDetails(final StringBuilder sb) { + sb.append("# "+currentTile+": ["+currentColumn+"]["+currentRow+"] / "+columns+"x"+rows+", ") + .append("rowOrder "+rowOrder+", offset/size "+offsetX+"/"+offsetY+" "+tileSize.getWidth()+"x"+tileSize.getHeight()+" brd "+tileBorder+", "); + return super.tileDetails(sb); + } + + /** + * Creates a new TileRenderer object + */ + public TileRenderer() { + super(); + } + + /** + * {@inheritDoc} + * <p> + * Implementation {@link #reset()} internal states. + * </p> + */ + @Override + public final void setImageSize(final int width, final int height) { + super.setImageSize(width, height); + reset(); + } + + /** + * Clips the image-size this tile-renderer iterates through, + * which can be retrieved via {@link #getClippedImageSize()}. + * <p> + * Original image-size stored in this tile-renderer is unmodified. + * </p> + * <p> + * Implementation {@link #reset()} internal states. + * </p> + * + * @param width The image-clipping.width + * @param height The image-clipping.height + * @see #getClippedImageSize() + */ + public final void clipImageSize(final int width, final int height) { + if( null == imageClippingDim ) { + imageClippingDim = new Dimension(width, height); + } else { + imageClippingDim.set(width, height); + } + reset(); + } + + /** + * Returns the clipped image-size. + * <p> + * If a image-size is clipped via {@link #clipImageSize(int, int)}, + * method returns: + * <ul> + * <li><code>min( image-clipping, image-size )</code>, otherwise</li> + * <li><code> image-size </code></li> + * </ul> + * </p> + * <p> + * The clipping width and height can be retrieved via {@link #TR_IMAGE_CLIPPING_WIDTH} + * {@link #TR_IMAGE_CLIPPING_HEIGHT}. + * </p> + */ + public final DimensionImmutable getClippedImageSize() { + if( null != imageClippingDim ) { + return new Dimension(Math.min(imageClippingDim.getWidth(), imageSize.getWidth()), + Math.min(imageClippingDim.getHeight(), imageSize.getHeight()) ); + } else { + return imageSize; + } + } + + /** + * Sets the size of the tiles to use in rendering. The actual + * effective size of the tile depends on the border size, ie ( + * width - 2*border ) * ( height - 2 * border ) + * <p> + * Implementation {@link #reset()} internal states. + * </p> + * + * @param width + * The width of the tiles. Must not be larger than the GL + * context + * @param height + * The height of the tiles. Must not be larger than the + * GL context + * @param border + * The width of the borders on each tile. This is needed + * to avoid artifacts when rendering lines or points with + * thickness > 1. + */ + public final void setTileSize(final int width, final int height, final int border) { + if( 0 > border ) { + throw new IllegalArgumentException("Tile border must be >= 0"); + } + if( 2 * border >= width || 2 * border >= height ) { + throw new IllegalArgumentException("Tile size must be > 0x0 minus 2*border"); + } + tileBorder = border; + tileSize.set( width, height ); + tileSizeNB.set( width - 2 * border, height - 2 * border ); + reset(); + } + + /** + * Sets an xy offset for the resulting tiles + * {@link TileRendererBase#TR_CURRENT_TILE_X_POS x-pos} and {@link TileRendererBase#TR_CURRENT_TILE_Y_POS y-pos}. + * @see #TR_TILE_X_OFFSET + * @see #TR_TILE_Y_OFFSET + **/ + public void setTileOffset(final int xoff, final int yoff) { + offsetX = xoff; + offsetY = yoff; + } + + /** + * {@inheritDoc} + * + * Reset internal states of {@link TileRenderer} are: + * <ul> + * <li>{@link #TR_ROWS}</li> + * <li>{@link #TR_COLUMNS}</li> + * <li>{@link #TR_CURRENT_COLUMN}</li> + * <li>{@link #TR_CURRENT_ROW}</li> + * <li>{@link #TR_CURRENT_TILE_NUM}</li> + * <li>{@link #TR_CURRENT_TILE_X_POS}</li> + * <li>{@link #TR_CURRENT_TILE_Y_POS}</li> + * <li>{@link #TR_CURRENT_TILE_WIDTH}</li> + * <li>{@link #TR_CURRENT_TILE_HEIGHT}</li> + *</ul> + */ + @Override + public final void reset() { + final DimensionImmutable clippedImageSize = getClippedImageSize(); + columns = ( clippedImageSize.getWidth() + tileSizeNB.getWidth() - 1 ) / tileSizeNB.getWidth(); + rows = ( clippedImageSize.getHeight() + tileSizeNB.getHeight() - 1 ) / tileSizeNB.getHeight(); + currentRow = 0; + currentColumn = 0; + currentTile = 0; + currentTileXPos = 0; + currentTileYPos = 0; + currentTileWidth = 0; + currentTileHeight = 0; + + assert columns >= 0; + assert rows >= 0; + + beginCalled = false; + isInit = true; + } + + /* pp */ final int getCurrentTile() { return currentTile; } + + @Override + public final int getParam(final int pname) { + switch (pname) { + case TR_IMAGE_WIDTH: + return imageSize.getWidth(); + case TR_IMAGE_HEIGHT: + return imageSize.getHeight(); + case TR_CURRENT_TILE_X_POS: + return currentTileXPos; + case TR_CURRENT_TILE_Y_POS: + return currentTileYPos; + case TR_CURRENT_TILE_WIDTH: + return currentTileWidth; + case TR_CURRENT_TILE_HEIGHT: + return currentTileHeight; + case TR_IMAGE_CLIPPING_WIDTH: + return null != imageClippingDim ? imageClippingDim.getWidth() : 0; + case TR_IMAGE_CLIPPING_HEIGHT: + return null != imageClippingDim ? imageClippingDim.getHeight() : 0; + case TR_TILE_WIDTH: + return tileSize.getWidth(); + case TR_TILE_HEIGHT: + return tileSize.getHeight(); + case TR_TILE_BORDER: + return tileBorder; + case TR_TILE_X_OFFSET: + return offsetX; + case TR_TILE_Y_OFFSET: + return offsetY; + case TR_ROWS: + return rows; + case TR_COLUMNS: + return columns; + case TR_CURRENT_TILE_NUM: + return currentTile; + case TR_CURRENT_ROW: + return currentRow; + case TR_CURRENT_COLUMN: + return currentColumn; + case TR_ROW_ORDER: + return rowOrder; + default: + throw new IllegalArgumentException("Invalid pname: "+pname); + } + } + + /** + * Sets the order of row traversal, default is {@link #TR_BOTTOM_TO_TOP}. + * + * @param order The row traversal order, must be either {@link #TR_TOP_TO_BOTTOM} or {@link #TR_BOTTOM_TO_TOP}. + */ + public final void setRowOrder(final int order) { + if (order == TR_TOP_TO_BOTTOM || order == TR_BOTTOM_TO_TOP) { + rowOrder = order; + } else { + throw new IllegalArgumentException("Must pass TR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP"); + } + } + + @Override + public final boolean isSetup() { + return 0 < imageSize.getWidth() && 0 < imageSize.getHeight(); + } + + /** + * {@inheritDoc} + * + * <p> + * <i>end of tiling</i> is reached w/ {@link TileRenderer}, if at least one of the following is true: + * <ul> + * <li>all tiles have been rendered, i.e. {@link #TR_CURRENT_TILE_NUM} is -1</li> + * <li>no tiles to render, i.e. {@link #TR_COLUMNS} or {@link #TR_ROWS} is 0</li> + * </ul> + * </p> + */ + @Override + public final boolean eot() { + if ( !isInit ) { // ensure at least one reset-call + reset(); + } + return 0 > currentTile || 0 >= columns*rows; + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if {@link #setImageSize(int, int) image-size} has not been set or + * {@link #eot() end-of-tiling} has been reached. + */ + @Override + public final void beginTile( final GL gl ) throws IllegalStateException, GLException { + if( !isSetup() ) { + throw new IllegalStateException("Image size has not been set: "+this); + } + if ( eot() ) { + throw new IllegalStateException("EOT reached: "+this); + } + validateGL(gl); + + /* which tile (by row and column) we're about to render */ + if (rowOrder == TR_BOTTOM_TO_TOP) { + currentRow = currentTile / columns; + currentColumn = currentTile % columns; + } else { + currentRow = rows - ( currentTile / columns ) - 1; + currentColumn = currentTile % columns; + } + assert ( currentRow < rows ); + assert ( currentColumn < columns ); + + final int border = tileBorder; + + final DimensionImmutable clippedImageSize = getClippedImageSize(); + int tH, tW; + + /* Compute actual size of this tile with border */ + if (currentRow < rows - 1) { + tH = tileSize.getHeight(); + } else { + tH = clippedImageSize.getHeight() - ( rows - 1 ) * ( tileSizeNB.getHeight() ) + 2 * border; + } + + if (currentColumn < columns - 1) { + tW = tileSize.getWidth(); + } else { + tW = clippedImageSize.getWidth() - ( columns - 1 ) * ( tileSizeNB.getWidth() ) + 2 * border; + } + + currentTileXPos = currentColumn * tileSizeNB.getWidth() + offsetX; + currentTileYPos = currentRow * tileSizeNB.getHeight() + offsetY; + + /* Save tile size, with border */ + currentTileWidth = tW; + currentTileHeight = tH; + + gl.glViewport( 0, 0, tW, tH ); + + if( DEBUG ) { + System.err.println("TileRenderer.begin: "+this.toString()); + } + + // Do not forget to issue: + // reshape( 0, 0, tW, tH ); + // which shall reflect tile renderer tiles: currentTileXPos, currentTileYPos and imageSize + beginCalled = true; + } + + @Override + public void endTile( final GL gl ) throws IllegalStateException, GLException { + if( !beginCalled ) { + throw new IllegalStateException("beginTile(..) has not been called"); + } + validateGL(gl); + + // be sure OpenGL rendering is finished + gl.glFlush(); + + // implicitly save current glPixelStore values + psm.setPackAlignment(gl, 1); + final GL2ES3 gl2es3; + final int readBuffer; + if( gl.isGL2ES3() ) { + gl2es3 = gl.getGL2ES3(); + readBuffer = gl2es3.getDefaultReadBuffer(); + gl2es3.glReadBuffer(readBuffer); + } else { + gl2es3 = null; + readBuffer = 0; // undef. probably default: GL_FRONT (single buffering) GL_BACK (double buffering) + } + if( DEBUG ) { + System.err.println("TileRenderer.end.0: readBuffer 0x"+Integer.toHexString(readBuffer)+", "+this.toString()); + } + + final int tmp[] = new int[1]; + + if( tileBuffer != null ) { + final GLPixelAttributes pixelAttribs = tileBuffer.pixelAttributes; + final int srcX = tileBorder; + final int srcY = tileBorder; + final int srcWidth = tileSizeNB.getWidth(); + final int srcHeight = tileSizeNB.getHeight(); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + tileBuffer.clear(); + if( tileBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { + throw new IndexOutOfBoundsException("Required " + readPixelSize + " bytes of buffer, only had " + tileBuffer); + } + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, pixelAttribs.format, pixelAttribs.type, tileBuffer.buffer); + // be sure OpenGL rendering is finished + gl.glFlush(); + tileBuffer.position( readPixelSize ); + tileBuffer.flip(); + } + + if( imageBuffer != null ) { + final GLPixelAttributes pixelAttribs = imageBuffer.pixelAttributes; + final int srcX = tileBorder; + final int srcY = tileBorder; + final int srcWidth = currentTileWidth - 2 * tileBorder; + final int srcHeight = currentTileHeight - 2 * tileBorder; + + /* setup pixel store for glReadPixels */ + final int rowLength = imageSize.getWidth(); + psm.setPackRowLength(gl2es3, rowLength); + + /* read the tile into the final image */ + final int readPixelSize = GLBuffers.sizeof(gl, tmp, pixelAttribs.bytesPerPixel, srcWidth, srcHeight, 1, true); + + final int skipPixels = currentColumn * tileSizeNB.getWidth(); + final int skipRows = currentRow * tileSizeNB.getHeight(); + final int ibPos = ( skipPixels + ( skipRows * rowLength ) ) * pixelAttribs.bytesPerPixel; + final int ibLim = ibPos + readPixelSize; + imageBuffer.clear(); + if( imageBuffer.requiresNewBuffer(gl, srcWidth, srcHeight, readPixelSize) ) { + throw new IndexOutOfBoundsException("Required " + ibLim + " bytes of buffer, only had " + imageBuffer); + } + imageBuffer.position(ibPos); + + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, pixelAttribs.format, pixelAttribs.type, imageBuffer.buffer); + // be sure OpenGL rendering is finished + gl.glFlush(); + imageBuffer.position( ibLim ); + imageBuffer.flip(); + } + + /* restore previous glPixelStore values */ + psm.restore(gl); + + beginCalled = false; + + /* increment tile counter, return 1 if more tiles left to render */ + currentTile++; + if( currentTile >= rows * columns ) { + currentTile = -1; /* all done */ + } + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java b/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java new file mode 100644 index 000000000..a1735766e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java @@ -0,0 +1,664 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + * + * --------------------- + * + * Based on Brian Paul's tile rendering library, found + * at <a href = "http://www.mesa3d.org/brianp/TR.html">http://www.mesa3d.org/brianp/TR.html</a>. + * + * Copyright (C) 1997-2005 Brian Paul. + * Licensed under BSD-compatible terms with permission of the author. + * See LICENSE.txt for license information. + */ +package com.jogamp.opengl.util; + +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import jogamp.opengl.Debug; + +/** + * A fairly direct port of Brian Paul's tile rendering library, found + * at <a href = "http://www.mesa3d.org/brianp/TR.html"> + * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but + * the functionality is the same. + * <p> + * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under + * BSD-compatible terms with permission of the author. See LICENSE.txt + * for license information. + * </p> + * <p> + * Enhanced for {@link GL} and {@link GL2ES3}, abstracted to suit {@link TileRenderer} and {@link RandomTileRenderer}. + * </p> + * <a name="pmvmatrix"><h5>PMV Matrix Considerations</h5></a> + * <p> + * The PMV matrix needs to be reshaped in user code + * after calling {@link #beginTile(GL)}, See {@link #beginTile(GL)}. + * </p> + * <p> + * If {@link #attachAutoDrawable(GLAutoDrawable) attaching to} an {@link GLAutoDrawable}, + * the {@link TileRendererListener#reshapeTile(TileRendererBase, int, int, int, int, int, int)} method + * is being called after {@link #beginTile(GL)} for each rendered tile. + * It's implementation shall reshape the PMV matrix according to {@link #beginTile(GL)}. + * </p> + * <a name="glprequirement"><h5>GL Profile Requirement</h5></a> + * <p> + * Note that {@link #setImageBuffer(GLPixelBuffer) image buffer} can only be used + * in conjunction w/ a {@link GL} instance ≥ {@link GL2ES3} passed to {@link #beginTile(GL)} and {@link #endTile(GL)}.<br> + * This is due to setting up the {@link GL2ES3#GL_PACK_ROW_LENGTH pack row length} + * for an {@link #setImageSize(int, int) image width} != tile-width, which usually is the case.<br> + * Hence a {@link GLException} is thrown in both methods, + * if using an {@link #setImageBuffer(GLPixelBuffer) image buffer} + * and passing a {@link GL} instance < {@link GL2ES3}. + * </p> + * <p> + * Further more, reading back of MSAA buffers is only supported since {@link GL2ES3} + * since it requires to set the {@link GL2ES3#glReadBuffer(int) read-buffer}. + * </p> + * + * @author ryanm, sgothel + */ +public abstract class TileRendererBase { + /** + * The width of the final image. See {@link #getParam(int)}. + */ + public static final int TR_IMAGE_WIDTH = 1; + /** + * The height of the final image. See {@link #getParam(int)}. + */ + public static final int TR_IMAGE_HEIGHT = 2; + /** + * The x-pos of the current tile. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_TILE_X_POS = 3; + /** + * The y-pos of the current tile. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_TILE_Y_POS = 4; + /** + * The width of the current tile. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_TILE_WIDTH = 5; + /** + * The height of the current tile. See {@link #getParam(int)}. + */ + public static final int TR_CURRENT_TILE_HEIGHT = 6; + + /* pp */ static final boolean DEBUG = Debug.debug("TileRenderer"); + + /** + * Listener for tile renderer events, intended to extend {@link GLEventListener} implementations, + * enabling tile rendering via {@link TileRendererBase#attachAutoDrawable(GLAutoDrawable)}. + */ + public static interface TileRendererListener { + /** + * The owning {@link GLAutoDrawable} is {@link TileRendererBase#attachAutoDrawable(GLAutoDrawable) attached} + * to the given {@link TileRendererBase} instance. + * <p> + * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} + * <i>is not</i> current. + * </p> + * @param tr the associated {@link TileRendererBase} + * @see TileRendererBase#getAttachedDrawable() + */ + public void addTileRendererNotify(TileRendererBase tr); + + /** + * The owning {@link GLAutoDrawable} is {@link TileRendererBase#detachAutoDrawable() detached} + * from the given {@link TileRendererBase} instance. + * <p> + * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} + * <i>is not</i> current. + * </p> + * @param tr the disassociated {@link TileRendererBase} + * @see TileRendererBase#getAttachedDrawable() + */ + public void removeTileRendererNotify(TileRendererBase tr); + + /** + * Called by the {@link TileRendererBase} during tile-rendering via an + * {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable}'s + * {@link GLAutoDrawable#display()} call for each tile before {@link #display(GLAutoDrawable)}. + * <p> + * The <a href="TileRendererBase#pmvmatrix">PMV Matrix</a> shall be reshaped + * according to the given + * <ul> + * <li>current tile-position</li> + * <li>current tile-size</li> + * <li>final image-size</li> + * </ul> + * The GL viewport is already set to origin 0/0 and the current tile-size.<br> + * See details in {@link TileRendererBase#beginTile(GL)}.<br> + * </p> + * <p> + * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} + * <i>is</i> current. + * </p> + * @param tr the issuing {@link TileRendererBase} + * @param tileX the {@link TileRendererBase#TR_CURRENT_TILE_X_POS current tile's x-pos} + * @param tileY the {@link TileRendererBase#TR_CURRENT_TILE_Y_POS current tile's y-pos} + * @param tileWidth the {@link TileRendererBase#TR_CURRENT_TILE_WIDTH current tile's width} + * @param tileHeight the {@link TileRendererBase#TR_CURRENT_TILE_HEIGHT current tile's height} + * @param imageWidth the {@link TileRendererBase#TR_IMAGE_WIDTH final image width} + * @param imageHeight the {@link TileRendererBase#TR_IMAGE_HEIGHT final image height} + * @see TileRendererBase#getAttachedDrawable() + */ + public void reshapeTile(TileRendererBase tr, + int tileX, int tileY, int tileWidth, int tileHeight, + int imageWidth, int imageHeight); + + /** + * Called by the {@link TileRendererBase} during tile-rendering + * after {@link TileRendererBase#beginTile(GL)} and before {@link #reshapeTile(TileRendererBase, int, int, int, int, int, int) reshapeTile(..)}. + * <p> + * If {@link TileRendererBase} is of type {@link TileRenderer}, + * method is called for the first tile of all tiles.<br> + * Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile. + * </p> + * <p> + * The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} + * <i>is</i> current. + * </p> + * @param tr the issuing {@link TileRendererBase} + */ + public void startTileRendering(TileRendererBase tr); + + /** + * Called by the {@link TileRenderer} during tile-rendering + * after {@link TileRendererBase#endTile(GL)} and {@link GLAutoDrawable#swapBuffers()}. + * <p> + * If {@link TileRendererBase} is of type {@link TileRenderer}, + * method is called for the last tile of all tiles.<br> + * Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile. + * </p> + * <p> + * The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} + * <i>is</i> current. + * </p> + * @param tr the issuing {@link TileRendererBase} + */ + public void endTileRendering(TileRendererBase tr); + } + + protected final Dimension imageSize = new Dimension(0, 0); + protected final GLPixelStorageModes psm = new GLPixelStorageModes(); + protected GLPixelBuffer imageBuffer; + protected GLPixelBuffer tileBuffer; + protected boolean beginCalled = false; + protected int currentTileXPos; + protected int currentTileYPos; + protected int currentTileWidth; + protected int currentTileHeight; + protected GLAutoDrawable glad; + protected boolean gladRequiresPreSwap; + protected boolean gladAutoSwapBufferMode = true; + protected GLEventListener[] listeners; + protected boolean[] listenersInit; + protected GLEventListener glEventListenerPre = null; + protected GLEventListener glEventListenerPost = null; + + private final String hashStr(final Object o) { + final int h = null != o ? o.hashCode() : 0; + return "0x"+Integer.toHexString(h); + } + protected StringBuilder tileDetails(final StringBuilder sb) { + return sb.append("cur "+currentTileXPos+"/"+currentTileYPos+" "+currentTileWidth+"x"+currentTileHeight+", buffer "+hashStr(tileBuffer)); + } + public StringBuilder toString(final StringBuilder sb) { + final int gladListenerCount = null != listeners ? listeners.length : 0; + sb.append("tile["); + tileDetails(sb); + sb.append("], image[size "+imageSize+", buffer "+hashStr(imageBuffer)+"], glad["+ + gladListenerCount+" listener, pre "+(null!=glEventListenerPre)+", post "+(null!=glEventListenerPost)+", preSwap "+gladRequiresPreSwap+"]"); + sb.append(", isSetup "+isSetup()); + return sb; + } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + return getClass().getSimpleName()+ + "["+toString(sb).toString()+"]"; + } + + protected TileRendererBase() { + } + + /** + * Gets the parameters of this TileRenderer object + * + * @param pname The parameter name that is to be retrieved + * @return the value of the parameter + * @throws IllegalArgumentException if <code>pname</code> is not handled + */ + public abstract int getParam(int pname) throws IllegalArgumentException; + + /** + * Specify a buffer the tiles to be copied to. This is not + * necessary for the creation of the final image, but useful if you + * want to inspect each tile in turn. + * + * @param buffer The buffer itself. Must be large enough to contain a random tile + */ + public final void setTileBuffer(final GLPixelBuffer buffer) { + tileBuffer = buffer; + if( DEBUG ) { + System.err.println("TileRenderer: tile-buffer "+tileBuffer); + } + } + + /** @see #setTileBuffer(GLPixelBuffer) */ + public final GLPixelBuffer getTileBuffer() { return tileBuffer; } + + /** + * Sets the desired size of the final image + * + * @param width The width of the final image + * @param height The height of the final image + */ + public void setImageSize(final int width, final int height) { + imageSize.set(width, height); + } + + /** @see #setImageSize(int, int) */ + public final DimensionImmutable getImageSize() { return imageSize; } + + /** + * Sets the buffer in which to store the final image + * + * @param buffer the buffer itself, must be large enough to hold the final image + */ + public final void setImageBuffer(final GLPixelBuffer buffer) { + imageBuffer = buffer; + if( DEBUG ) { + System.err.println("TileRenderer: image-buffer "+imageBuffer); + } + } + + /** @see #setImageBuffer(GLPixelBuffer) */ + public final GLPixelBuffer getImageBuffer() { return imageBuffer; } + + /* pp */ final void validateGL(final GL gl) throws GLException { + if( imageBuffer != null && !gl.isGL2ES3()) { + throw new GLException("Using image-buffer w/ inssufficient GL context: "+gl.getContext().getGLVersion()+", "+gl.getGLProfile()); + } + } + + /** + * Returns true if this instance is setup properly, i.e. {@link #setImageSize(int, int)} .., + * and ready for {@link #beginTile(GL)}. + * Otherwise returns false. + */ + public abstract boolean isSetup(); + + /** + * Returns true if <i>end of tiling</i> has been reached, otherwise false. + * <p> + * <i>end of tiling</i> criteria is implementation specific and may never be reached. + * </p> + * <p> + * User needs to {@link #reset()} tiling after reaching <i>end of tiling</i> + * before calling {@link #beginTile(GL)} again. + * </p> + */ + public abstract boolean eot(); + + /** + * Method resets implementation's internal state to <i>start of tiling</i> + * as required for {@link #beginTile(GL)} if {@link #eot() end of tiling} has been reached. + * <p> + * Implementation is a <i>nop</i> where {@link #eot() end of tiling} is never reached. + * </p> + */ + public abstract void reset(); + + /** + * Begins rendering a tile. + * <p> + * This method modifies the viewport, see below. + * User shall reset the viewport when finishing all tile rendering, + * i.e. after very last call of {@link #endTile(GL)}! + * </p> + * <p> + * The <a href="TileRendererBase.html#pmvmatrix">PMV Matrix</a> + * must be reshaped after this call using: + * <ul> + * <li>Current Viewport + * <ul> + * <li>x 0</li> + * <li>y 0</li> + * <li>{@link #TR_CURRENT_TILE_WIDTH current tile's width}</li> + * <li>{@link #TR_CURRENT_TILE_HEIGHT current tile's height}</li> + * </ul></li> + * <li>{@link #TR_CURRENT_TILE_X_POS current tile's x-pos}</li> + * <li>{@link #TR_CURRENT_TILE_Y_POS current tile's y-pos}</li> + * <li>{@link #TR_IMAGE_WIDTH final image width}</li> + * <li>{@link #TR_IMAGE_HEIGHT final image height}</li> + * </ul> + * </p> + * <p> + * Use shall render the scene afterwards, concluded with a call to + * this renderer {@link #endTile(GL)}. + * </p> + * <p> + * User has to comply with the <a href="TileRendererBase.html#glprequirement">GL profile requirement</a>. + * </p> + * <p> + * If {@link #eot() end of tiling} has been reached, + * user needs to {@link #reset()} tiling before calling this method. + * </p> + * + * @param gl The gl context + * @throws IllegalStateException if {@link #setImageSize(int, int) image-size} is undefined, + * an {@link #isSetup() implementation related setup} has not be performed + * or {@ link #eot()} has been reached. See implementing classes. + * @throws GLException if {@link #setImageBuffer(GLPixelBuffer) image buffer} is used but <code>gl</code> instance is < {@link GL2ES3} + * @see #isSetup() + * @see #eot() + * @see #reset() + */ + public abstract void beginTile(GL gl) throws IllegalStateException, GLException; + + /** + * Must be called after rendering the scene, + * see {@link #beginTile(GL)}. + * <p> + * Please consider {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)} to determine + * whether you need to perform {@link GLDrawable#swapBuffers() swap-buffers} before or after + * calling this method! + * </p> + * <p> + * User has to comply with the <a href="TileRendererBase.html#glprequirement">GL profile requirement</a>. + * </p> + * + * @param gl the gl context + * @throws IllegalStateException if beginTile(gl) has not been called + * @throws GLException if {@link #setImageBuffer(GLPixelBuffer) image buffer} is used but <code>gl</code> instance is < {@link GL2ES3} + */ + public abstract void endTile( GL gl ) throws IllegalStateException, GLException; + + /** + * Determines whether the chosen {@link GLCapabilitiesImmutable} + * requires a <i>pre-{@link GLDrawable#swapBuffers() swap-buffers}</i> + * before accessing the results, i.e. before {@link #endTile(GL)}. + * <p> + * See {@link GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable)}. + * </p> + */ + public final boolean reqPreSwapBuffers(final GLCapabilitiesImmutable chosenCaps) { + return GLDrawableUtil.swapBuffersBeforeRead(chosenCaps); + } + + /** + * Attaches the given {@link GLAutoDrawable} to this tile renderer. + * <p> + * The {@link GLAutoDrawable}'s original {@link GLEventListener} are moved to this tile renderer. + * </p> + * <p> + * {@link GLEventListeners} not implementing {@link TileRendererListener} are ignored while tile rendering. + * </p> + * <p> + * The {@link GLAutoDrawable}'s {@link GLAutoDrawable#getAutoSwapBufferMode() auto-swap mode} is cached + * and set to <code>false</code>, since {@link GLAutoDrawable#swapBuffers() swapBuffers()} maybe issued before {@link #endTile(GL)}, + * see {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)}. + * </p> + * <p> + * This tile renderer's internal {@link GLEventListener} is then added to the attached {@link GLAutoDrawable} + * to handle the tile rendering, replacing the original {@link GLEventListener}.<br> + * It's {@link GLEventListener#display(GLAutoDrawable) display} implementations issues: + * <ul> + * <li>Optional {@link #setGLEventListener(GLEventListener, GLEventListener) pre-glel}.{@link GLEventListener#display(GLAutoDrawable) display(..)}</li> + * <li>{@link #beginTile(GL)}</li> + * <li>for all original {@link TileRendererListener}: + * <ul> + * <li>{@link TileRendererListener#reshapeTile(TileRendererBase, int, int, int, int, int, int) reshapeTile(tileX, tileY, tileWidth, tileHeight, imageWidth, imageHeight)}</li> + * <li>{@link GLEventListener#display(GLAutoDrawable) display(autoDrawable)}</li> + * </ul></li> + * <li>if ( {@link #reqPreSwapBuffers(GLCapabilitiesImmutable) pre-swap} ) { {@link GLAutoDrawable#swapBuffers() swapBuffers()} }</li> + * <li>{@link #endTile(GL)}</li> + * <li>if ( !{@link #reqPreSwapBuffers(GLCapabilitiesImmutable) pre-swap} ) { {@link GLAutoDrawable#swapBuffers() swapBuffers()} }</li> + * <li>Optional {@link #setGLEventListener(GLEventListener, GLEventListener) post-glel}.{@link GLEventListener#display(GLAutoDrawable) display(..)}</li> + * </ul> + * </p> + * <p> + * Consider using {@link #setGLEventListener(GLEventListener, GLEventListener)} to add pre- and post + * hooks to be performed on this renderer {@link GLEventListener}.<br> + * The pre-hook is able to allocate memory and setup parameters, since it's called before {@link #beginTile(GL)}.<br> + * The post-hook is able to use the rendering result and can even shutdown tile-rendering, + * since it's called after {@link #endTile(GL)}. + * </p> + * <p> + * Call {@link #detachAutoDrawable()} to remove the attached {@link GLAutoDrawable} from this tile renderer + * and to restore it's original {@link GLEventListener}. + * </p> + * @param glad the {@link GLAutoDrawable} to attach. + * @throws IllegalStateException if an {@link GLAutoDrawable} is already attached + * @see #getAttachedDrawable() + * @see #detachAutoDrawable() + */ + public final void attachAutoDrawable(final GLAutoDrawable glad) throws IllegalStateException { + if( null != this.glad ) { + throw new IllegalStateException("GLAutoDrawable already attached"); + } + this.glad = glad; + + final int aSz = glad.getGLEventListenerCount(); + listeners = new GLEventListener[aSz]; + listenersInit = new boolean[aSz]; + for(int i=0; i<aSz; i++) { + final GLEventListener l = glad.getGLEventListener(0); + listenersInit[i] = glad.getGLEventListenerInitState(l); + listeners[i] = glad.removeGLEventListener( l ); + final boolean trn; + if( listeners[i] instanceof TileRendererListener ) { + trn = true; + ((TileRendererListener)listeners[i]).addTileRendererNotify(this); + } else { + trn = false; + } + if( DEBUG ) { + System.err.println("TileRenderer.attach["+i+"]: isInit "+listenersInit[i]+", isTRN "+trn+", "+listeners[i].getClass().getName()); + } + } + glad.addGLEventListener(tiledGLEL); + gladAutoSwapBufferMode = glad.getAutoSwapBufferMode(); + gladRequiresPreSwap = this.reqPreSwapBuffers(glad.getChosenGLCapabilities()); + glad.setAutoSwapBufferMode(false); + if( DEBUG ) { + System.err.println("TileRenderer: attached: "+glad); + System.err.println("TileRenderer: preSwap "+gladRequiresPreSwap+", "+glad.getChosenGLCapabilities()+", cached "+listeners.length+" listener"); + } + } + + /** + * Returns a previously {@link #attachAutoDrawable(GLAutoDrawable) attached} {@link GLAutoDrawable}, + * <code>null</code> if none is attached. + * <p> + * If called from {@link TileRendererListener#addTileRendererNotify(TileRendererBase)} + * or {@link TileRendererListener#removeTileRendererNotify(TileRendererBase)}, method returns the + * just attached or soon to be detached {@link GLAutoDrawable}. + * </p> + * @see #attachAutoDrawable(GLAutoDrawable) + * @see #detachAutoDrawable() + */ + public final GLAutoDrawable getAttachedDrawable() { + return glad; + } + + /** + * Detaches the given {@link GLAutoDrawable} from this tile renderer. + * @see #attachAutoDrawable(GLAutoDrawable) + * @see #getAttachedDrawable() + */ + public final void detachAutoDrawable() { + if( null != glad ) { + glad.removeGLEventListener(tiledGLEL); + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + if( l instanceof TileRendererListener ) { + ((TileRendererListener)l).removeTileRendererNotify(this); + } + glad.addGLEventListener(l); + glad.setGLEventListenerInitState(l, listenersInit[i]); + } + glad.setAutoSwapBufferMode(gladAutoSwapBufferMode); + if( DEBUG ) { + System.err.println("TileRenderer: detached: "+glad); + System.err.println("TileRenderer: "+glad.getChosenGLCapabilities()); + } + + listeners = null; + listenersInit = null; + glad = null; + } + } + + /** + * Set {@link GLEventListener} for pre- and post operations when used w/ + * {@link #attachAutoDrawable(GLAutoDrawable)} + * for each {@link GLEventListener} callback. + * @param preTile the pre operations + * @param postTile the post operations + */ + public final void setGLEventListener(final GLEventListener preTile, final GLEventListener postTile) { + glEventListenerPre = preTile; + glEventListenerPost = postTile; + } + + /** + * Rendering one tile, by simply calling {@link GLAutoDrawable#display()}. + * + * @throws IllegalStateException if no {@link GLAutoDrawable} is {@link #attachAutoDrawable(GLAutoDrawable) attached} + * or imageSize is not set + */ + public final void display() throws IllegalStateException { + if( null == glad ) { + throw new IllegalStateException("No GLAutoDrawable attached"); + } + glad.display(); + } + + private final GLEventListener tiledGLEL = new GLEventListener() { + final TileRenderer tileRenderer = TileRendererBase.this instanceof TileRenderer ? (TileRenderer) TileRendererBase.this : null; + + @Override + public void init(final GLAutoDrawable drawable) { + if( null != glEventListenerPre ) { + glEventListenerPre.init(drawable); + } + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + if( !listenersInit[i] && l instanceof TileRendererListener ) { + l.init(drawable); + listenersInit[i] = true; + } + } + if( null != glEventListenerPost ) { + glEventListenerPost.init(drawable); + } + } + @Override + public void dispose(final GLAutoDrawable drawable) { + if( null != glEventListenerPre ) { + glEventListenerPre.dispose(drawable); + } + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { // dispose all GLEventListener, last chance! + listeners[i].dispose(drawable); + } + if( null != glEventListenerPost ) { + glEventListenerPost.dispose(drawable); + } + } + @Override + public void display(final GLAutoDrawable drawable) { + if( null != glEventListenerPre ) { + glEventListenerPre.reshape(drawable, 0, 0, currentTileWidth, currentTileHeight); + glEventListenerPre.display(drawable); + } + if( !isSetup() ) { + if( DEBUG ) { + System.err.println("TileRenderer.glel.display: !setup: "+TileRendererBase.this); + } + return; + } + if( eot() ) { + if( DEBUG ) { + System.err.println("TileRenderer.glel.display: EOT: "+TileRendererBase.this); + } + return; + } + final GL gl = drawable.getGL(); + + beginTile(gl); + + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + if( l instanceof TileRendererListener ) { + final TileRendererListener tl = (TileRendererListener)l; + if( null == tileRenderer || 0 == tileRenderer.getCurrentTile() ) { + tl.startTileRendering(TileRendererBase.this); + } + tl.reshapeTile(TileRendererBase.this, + currentTileXPos, currentTileYPos, currentTileWidth, currentTileHeight, + imageSize.getWidth(), imageSize.getHeight()); + l.display(drawable); + } + } + + if( gladRequiresPreSwap ) { + glad.swapBuffers(); + endTile(gl); + } else { + endTile(gl); + glad.swapBuffers(); + } + if( null == tileRenderer || tileRenderer.eot() ) { + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + if( l instanceof TileRendererListener ) { + ((TileRendererListener)l).endTileRendering(TileRendererBase.this); + } + } + } + if( null != glEventListenerPost ) { + glEventListenerPost.reshape(drawable, 0, 0, currentTileWidth, currentTileHeight); + glEventListenerPost.display(drawable); + } + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } + }; +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java b/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java new file mode 100644 index 000000000..99def6f5c --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java @@ -0,0 +1,81 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +/** + * Integer time frame in milliseconds, maybe specialized for texture/video, audio, .. animated content. + * <p> + * Type and value range has been chosen to suit embedded CPUs + * and characteristics of audio / video streaming and animations. + * Milliseconds of type integer with a maximum value of {@link Integer#MAX_VALUE} + * will allow tracking time up 2,147,483.647 seconds or + * 24 days 20 hours 31 minutes and 23 seconds. + * </p> + * <p> + * Milliseconds granularity is also more than enough to deal with A-V synchronization, + * where the threshold usually lies within 22ms. + * </p> + * <p> + * Milliseconds granularity for displaying video frames might seem inaccurate + * for each single frame, i.e. 60Hz != 16ms, however, accumulated values diminish + * this error and vertical sync is achieved by build-in V-Sync of the video drivers. + * </p> + */ +public class TimeFrameI { + /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ native code. */ + public static final int INVALID_PTS = 0x80000000; + + /** Constant marking the end of the stream PTS, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ native code. */ + public static final int END_OF_STREAM_PTS = 0x7FFFFFFF; + + protected int pts; + protected int duration; + + public TimeFrameI() { + pts = INVALID_PTS; + duration = 0; + } + public TimeFrameI(final int pts, final int duration) { + this.pts = pts; + this.duration = duration; + } + + /** Get this frame's presentation timestamp (PTS) in milliseconds. */ + public final int getPTS() { return pts; } + /** Set this frame's presentation timestamp (PTS) in milliseconds. */ + public final void setPTS(final int pts) { this.pts = pts; } + /** Get this frame's duration in milliseconds. */ + public final int getDuration() { return duration; } + /** Set this frame's duration in milliseconds. */ + public final void setDuration(final int duration) { this.duration = duration; } + + @Override + public String toString() { + return "TimeFrame[pts " + pts + " ms, l " + duration + " ms]"; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java new file mode 100644 index 000000000..fd6172e13 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java @@ -0,0 +1,436 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.av; + +import java.nio.ByteBuffer; + +import com.jogamp.opengl.util.TimeFrameI; + +import jogamp.opengl.Debug; + +public interface AudioSink { + public static final boolean DEBUG = Debug.debug("AudioSink"); + + /** Default frame duration in millisecond, i.e. 1 frame per {@value} ms. */ + public static final int DefaultFrameDuration = 32; + + /** Initial audio queue size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ + public static final int DefaultInitialQueueSize = 16 * 32; // 512 ms + /** Audio queue grow size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ + public static final int DefaultQueueGrowAmount = 16 * 32; // 512 ms + /** Audio queue limit w/ video in milliseconds. {@value} ms, i.e. 96 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ + public static final int DefaultQueueLimitWithVideo = 96 * 32; // 3072 ms + /** Audio queue limit w/o video in milliseconds. {@value} ms, i.e. 32 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ + public static final int DefaultQueueLimitAudioOnly = 32 * 32; // 1024 ms + + /** + * Specifies the linear audio PCM format. + */ + public static class AudioFormat { + /** + * @param sampleRate sample rate in Hz (1/s) + * @param sampleSize sample size in bits + * @param channelCount number of channels + * @param signed true if signed number, false for unsigned + * @param fixedP true for fixed point value, false for unsigned floating point value with a sampleSize of 32 (float) or 64 (double) + * @param planar true for planar data package (each channel in own data buffer), false for packed data channels interleaved in one buffer. + * @param littleEndian true for little-endian, false for big endian + */ + public AudioFormat(final int sampleRate, final int sampleSize, final int channelCount, final boolean signed, final boolean fixedP, final boolean planar, final boolean littleEndian) { + this.sampleRate = sampleRate; + this.sampleSize = sampleSize; + this.channelCount = channelCount; + this.signed = signed; + this.fixedP = fixedP; + this.planar = planar; + this.littleEndian = littleEndian; + if( !fixedP ) { + if( sampleSize != 32 && sampleSize != 64 ) { + throw new IllegalArgumentException("Floating point: sampleSize "+sampleSize+" bits"); + } + if( !signed ) { + throw new IllegalArgumentException("Floating point: unsigned"); + } + } + } + + /** Sample rate in Hz (1/s). */ + public final int sampleRate; + /** Sample size in bits. */ + public final int sampleSize; + /** Number of channels. */ + public final int channelCount; + public final boolean signed; + /** Fixed or floating point values. Floating point 'float' has {@link #sampleSize} 32, 'double' has {@link #sampleSize} 64. */ + public final boolean fixedP; + /** Planar or packed samples. If planar, each channel has their own data buffer. If packed, channel data is interleaved in one buffer. */ + public final boolean planar; + public final boolean littleEndian; + + + // + // Time <-> Bytes + // + + /** + * Returns the byte size of the given milliseconds + * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. + * <p> + * Time -> Byte Count + * </p> + */ + public final int getDurationsByteSize(final int millisecs) { + final int bytesPerSample = sampleSize >>> 3; // /8 + return millisecs * ( channelCount * bytesPerSample * ( sampleRate / 1000 ) ); + } + + /** + * Returns the duration in milliseconds of the given byte count + * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. + * <p> + * Byte Count -> Time + * </p> + */ + public final int getBytesDuration(final int byteCount) { + final int bytesPerSample = sampleSize >>> 3; // /8 + return byteCount / ( channelCount * bytesPerSample * ( sampleRate / 1000 ) ); + } + + /** + * Returns the duration in milliseconds of the given sample count per frame and channel + * according to the {@link #sampleRate}, i.e. + * <pre> + * ( 1000f * sampleCount ) / sampleRate + * </pre> + * <p> + * Sample Count -> Time + * </p> + * @param sampleCount sample count per frame and channel + */ + public final float getSamplesDuration(final int sampleCount) { + return ( 1000f * sampleCount ) / sampleRate; + } + + /** + * Returns the rounded frame count of the given milliseconds and frame duration. + * <pre> + * Math.max( 1, millisecs / frameDuration + 0.5f ) + * </pre> + * <p> + * Note: <code>frameDuration</code> can be derived by <i>sample count per frame and channel</i> + * via {@link #getSamplesDuration(int)}. + * </p> + * <p> + * Frame Time -> Frame Count + * </p> + * @param millisecs time in milliseconds + * @param frameDuration duration per frame in milliseconds. + */ + public final int getFrameCount(final int millisecs, final float frameDuration) { + return Math.max(1, (int) ( millisecs / frameDuration + 0.5f )); + } + + /** + * Returns the byte size of given sample count + * according to the {@link #sampleSize}, i.e.: + * <pre> + * sampleCount * ( sampleSize / 8 ) + * </pre> + * <p> + * Note: To retrieve the byte size for all channels, + * you need to pre-multiply <code>sampleCount</code> with {@link #channelCount}. + * </p> + * <p> + * Sample Count -> Byte Count + * </p> + * @param sampleCount sample count + */ + public final int getSamplesByteCount(final int sampleCount) { + return sampleCount * ( sampleSize >>> 3 ); + } + + /** + * Returns the sample count of given byte count + * according to the {@link #sampleSize}, i.e.: + * <pre> + * ( byteCount * 8 ) / sampleSize + * </pre> + * <p> + * Note: If <code>byteCount</code> covers all channels and you request the sample size per channel, + * you need to divide the result by <code>sampleCount</code> by {@link #channelCount}. + * </p> + * <p> + * Byte Count -> Sample Count + * </p> + * @param byteCount number of bytes + */ + public final int getBytesSampleCount(final int byteCount) { + return ( byteCount << 3 ) / sampleSize; + } + + @Override + public String toString() { + return "AudioDataFormat[sampleRate "+sampleRate+", sampleSize "+sampleSize+", channelCount "+channelCount+ + ", signed "+signed+", fixedP "+fixedP+", "+(planar?"planar":"packed")+", "+(littleEndian?"little":"big")+"-endian]"; } + } + /** Default {@link AudioFormat}, [type PCM, sampleRate 44100, sampleSize 16, channelCount 2, signed, fixedP, !planar, littleEndian]. */ + public static final AudioFormat DefaultFormat = new AudioFormat(44100, 16, 2, true /* signed */, + true /* fixed point */, false /* planar */, true /* littleEndian */); + + public static abstract class AudioFrame extends TimeFrameI { + protected int byteSize; + + public AudioFrame() { + this.byteSize = 0; + } + public AudioFrame(final int pts, final int duration, final int byteCount) { + super(pts, duration); + this.byteSize=byteCount; + } + + /** Get this frame's size in bytes. */ + public final int getByteSize() { return byteSize; } + /** Set this frame's size in bytes. */ + public final void setByteSize(final int size) { this.byteSize=size; } + + @Override + public String toString() { + return "AudioFrame[pts " + pts + " ms, l " + duration + " ms, "+byteSize + " bytes]"; + } + } + public static class AudioDataFrame extends AudioFrame { + protected final ByteBuffer data; + + public AudioDataFrame(final int pts, final int duration, final ByteBuffer bytes, final int byteCount) { + super(pts, duration, byteCount); + if( byteCount > bytes.remaining() ) { + throw new IllegalArgumentException("Give size "+byteCount+" exceeds remaining bytes in ls "+bytes+". "+this); + } + this.data=bytes; + } + + /** Get this frame's data. */ + public final ByteBuffer getData() { return data; } + + @Override + public String toString() { + return "AudioDataFrame[pts " + pts + " ms, l " + duration + " ms, "+byteSize + " bytes, " + data + "]"; + } + } + + /** + * Returns the <code>initialized state</code> of this instance. + * <p> + * The <code>initialized state</code> is affected by this instance + * overall availability, i.e. after instantiation, + * as well as by {@link #destroy()}. + * </p> + */ + public boolean isInitialized(); + + /** Returns the playback speed. */ + public float getPlaySpeed(); + + /** + * Sets the playback speed. + * <p> + * To simplify test, play speed is <i>normalized</i>, i.e. + * <ul> + * <li><code>1.0f</code>: if <code> Math.abs(1.0f - rate) < 0.01f </code></li> + * </ul> + * </p> + * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. + */ + public boolean setPlaySpeed(float s); + + /** Returns the volume. */ + public float getVolume(); + + /** + * Sets the volume [0f..1f]. + * <p> + * To simplify test, volume is <i>normalized</i>, i.e. + * <ul> + * <li><code>0.0f</code>: if <code> Math.abs(v) < 0.01f </code></li> + * <li><code>1.0f</code>: if <code> Math.abs(1.0f - v) < 0.01f </code></li> + * </ul> + * </p> + * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. + */ + public boolean setVolume(float v); + + /** + * Returns the preferred {@link AudioFormat} by this sink. + * <p> + * The preferred format is guaranteed to be supported + * and shall reflect this sinks most native format, + * i.e. best performance w/o data conversion. + * </p> + * <p> + * Known {@link #AudioFormat} attributes considered by implementations: + * <ul> + * <li>ALAudioSink: {@link AudioFormat#sampleRate}. + * </ul> + * </p> + * @see #initSink(AudioFormat) + * @see #isSupported(AudioFormat) + */ + public AudioFormat getPreferredFormat(); + + /** Return the maximum number of supported channels. */ + public int getMaxSupportedChannels(); + + /** + * Returns true if the given format is supported by the sink, otherwise false. + * @see #initSink(AudioFormat) + * @see #getPreferredFormat() + */ + public boolean isSupported(AudioFormat format); + + /** + * Initializes the sink. + * <p> + * Implementation must match the given <code>requestedFormat</code> {@link AudioFormat}. + * </p> + * <p> + * Caller shall validate <code>requestedFormat</code> via {@link #isSupported(AudioFormat)} + * beforehand and try to find a suitable supported one. + * {@link #getPreferredFormat()} and {@link #getMaxSupportedChannels()} may help. + * </p> + * @param requestedFormat the requested {@link AudioFormat}. + * @param frameDuration average or fixed frame duration in milliseconds + * helping a caching {@link AudioFrame} based implementation to determine the frame count in the queue. + * See {@link #DefaultFrameDuration}. + * @param initialQueueSize initial time in milliseconds to queue in this sink, see {@link #DefaultInitialQueueSize}. + * @param queueGrowAmount time in milliseconds to grow queue if full, see {@link #DefaultQueueGrowAmount}. + * @param queueLimit maximum time in milliseconds the queue can hold (and grow), see {@link #DefaultQueueLimitWithVideo} and {@link #DefaultQueueLimitAudioOnly}. + * @return true if successful, otherwise false + */ + public boolean init(AudioFormat requestedFormat, float frameDuration, + int initialQueueSize, int queueGrowAmount, int queueLimit); + + /** + * Returns the {@link AudioFormat} as chosen by {@link #init(AudioFormat, float, int, int, int)}, + * i.e. it shall match the <i>requestedFormat</i>. + */ + public AudioFormat getChosenFormat(); + + /** + * Returns true, if {@link #play()} has been requested <i>and</i> the sink is still playing, + * otherwise false. + */ + public boolean isPlaying(); + + /** + * Play buffers queued via {@link #enqueueData(AudioFrame)} from current internal position. + * If no buffers are yet queued or the queue runs empty, playback is being continued when buffers are enqueued later on. + * @see #enqueueData(AudioFrame) + * @see #pause() + */ + public void play(); + + /** + * Pause playing buffers while keeping enqueued data incl. it's internal position. + * @see #play() + * @see #flush() + * @see #enqueueData(AudioFrame) + */ + public void pause(); + + /** + * Flush all queued buffers, implies {@link #pause()}. + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + * @see #play() + * @see #pause() + * @see #enqueueData(AudioFrame) + */ + public void flush(); + + /** Destroys this instance, i.e. closes all streams and devices allocated. */ + public void destroy(); + + /** + * Returns the number of allocated buffers as requested by + * {@link #init(AudioFormat, float, int, int, int)}. + */ + public int getFrameCount(); + + /** @return the current enqueued frames count since {@link #init(AudioFormat, float, int, int, int)}. */ + public int getEnqueuedFrameCount(); + + /** + * Returns the current number of frames queued for playing. + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + */ + public int getQueuedFrameCount(); + + /** + * Returns the current number of bytes queued for playing. + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + */ + public int getQueuedByteCount(); + + /** + * Returns the current queued frame time in milliseconds for playing. + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + */ + public int getQueuedTime(); + + /** + * Return the current audio presentation timestamp (PTS) in milliseconds. + */ + public int getPTS(); + + /** + * Returns the current number of frames in the sink available for writing. + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + */ + public int getFreeFrameCount(); + + /** + * Enqueue <code>byteCount</code> bytes of the remaining bytes of the given NIO {@link ByteBuffer} to this sink. + * <p> + * The data must comply with the chosen {@link AudioFormat} as returned by {@link #initSink(AudioFormat)}. + * </p> + * <p> + * {@link #init(AudioFormat, float, int, int, int)} must be called first. + * </p> + * @returns the enqueued internal {@link AudioFrame}. + */ + public AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java new file mode 100644 index 000000000..a2b29b4eb --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java @@ -0,0 +1,68 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.av; + +import jogamp.opengl.util.av.NullAudioSink; + +import com.jogamp.common.util.ReflectionUtil; + +public class AudioSinkFactory { + private static final String ALAudioSinkClazzName = "jogamp.opengl.openal.av.ALAudioSink"; + private static final String JavaAudioSinkClazzName = "jogamp.opengl.util.av.JavaSoundAudioSink"; + + public static AudioSink createDefault() { + final ClassLoader cl = GLMediaPlayerFactory.class.getClassLoader(); + AudioSink sink = create(cl, ALAudioSinkClazzName); + if( null == sink ) { + sink = create(cl, JavaAudioSinkClazzName); + } + if( null == sink ) { + sink = createNull(); + } + return sink; + } + public static AudioSink createNull() { + return new NullAudioSink(); + } + + public static AudioSink create(final ClassLoader cl, final String implName) { + final AudioSink audioSink; + if(ReflectionUtil.isClassAvailable(implName, cl)){ + try { + audioSink = (AudioSink) ReflectionUtil.createInstance(implName, cl); + if( audioSink.isInitialized() ) { + return audioSink; + } + } catch (final Throwable t) { + if(AudioSink.DEBUG) { System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); t.printStackTrace(); } + } + } + return null; + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java index 3eca01986..2ad102235 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,181 +20,540 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.util.av; -import java.io.IOException; -import java.net.URLConnection; - import javax.media.opengl.GL; +import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; +import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; +import com.jogamp.opengl.util.TimeFrameI; /** - * Lifecycle of an GLMediaPlayer: + * GLMediaPlayer interface specifies a {@link TextureSequence} state machine + * using a multiplexed audio/video stream as it's source. + * <p> + * Audio maybe supported and played back internally or via an {@link AudioSink} implementation. + * </p> + * <p> + * Audio and video streams can be selected or muted via {@link #initStream(Uri, int, int, int)} + * using the appropriate <a href="#streamIDs">stream id</a>'s. + * </p> + * <p> + * Camera input can be selected using the {@link #CameraInputScheme} Uri. + * </p> + * + * <a name="streamworker"><h5><i>StreamWorker</i> Decoding Thread</h5></a> + * <p> + * Most of the stream processing is performed on the decoding thread, a.k.a. <i>StreamWorker</i>: + * <ul> + * <li>Stream initialization triggered by {@link #initStream(Uri, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> + * <li>Stream decoding - User gets notified of a new frame via {@link GLMediaEventListener#newFrameAvailable(GLMediaPlayer, com.jogamp.opengl.util.texture.TextureSequence.TextureFrame, long) newFrameAvailable(...)}.</li> + * <li>Caught <a href="#streamerror">exceptions on the decoding thread</a> are delivered as {@link StreamException}s.</li> + * </ul> + * <i>StreamWorker</i> generates it's own {@link GLContext}, shared with the one passed to {@link #initGL(GL)}. + * The shared {@link GLContext} allows the decoding thread to push the video frame data directly into + * the designated {@link TextureFrame}, later returned via {@link #getNextTexture(GL)} and used by the user. + * </p> + * <a name="streamerror"><h7><i>StreamWorker</i> Error Handling</h7></a> + * <p> + * Caught exceptions on <a href="#streamworker">StreamWorker</a> are delivered as {@link StreamException}s, + * which either degrades the {@link State} to {@link State#Uninitialized} or {@link State#Paused}. + * </p> + * <p> + * An occurring {@link StreamException} triggers a {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} event, + * which can be listened to via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long)}. + * </p> + * <p> + * An occurred {@link StreamException} can be read via {@link #getStreamException()}. + * </p> + * + * </p> + * <a name="lifecycle"><h5>GLMediaPlayer Lifecycle</h5></a> + * <p> + * <table border="1"> + * <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link GLMediaEventListener Event}</th></tr> + * <tr><td>{@link #initStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> + * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}</td> <td>{@link State#Paused Paused}, , {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> + * <tr><td>{@link #play()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr> + * <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> + * <tr><td>{@link #seek(int)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> + * <tr><td>{@link #getNextTexture(GL)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> + * <tr><td>{@link #getLastTexture()}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> + * <tr><td>{@link TextureFrame#END_OF_STREAM_PTS END_OF_STREAM}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_EOS EVENT_CHANGE_EOS} + {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> + * <tr><td>{@link StreamException}</td> <td>ANY</td> <td>{@link State#Paused Paused}, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + ( {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> + * <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT}</td></tr> + * </table> + * </p> + * + * <a name="streamIDs"><h5>Audio and video Stream IDs</h5></a> + * <p> * <table border="1"> - * <tr><th>action</th> <th>state before</th> <th>state after</th></tr> - * <tr><td>{@link #initGLStream(GL, URLConnection)}</td> <td>Uninitialized</td> <td>Stopped</td></tr> - * <tr><td>{@link #start()}</td> <td>Stopped, Paused</td> <td>Playing</td></tr> - * <tr><td>{@link #stop()}</td> <td>Playing, Paused</td> <td>Stopped</td></tr> - * <tr><td>{@link #pause()}</td> <td>Playing</td> <td>Paused</td></tr> - * <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>Uninitialized</td></tr> + * <tr><th>value</th> <th>request</th> <th>get</th></tr> + * <tr><td>{@link #STREAM_ID_NONE}</td> <td>mute</td> <td>not available</td></tr> + * <tr><td>{@link #STREAM_ID_AUTO}</td> <td>auto</td> <td>unspecified</td></tr> + * <tr><td>≥0</td> <td>specific stream</td> <td>specific stream</td></tr> * </table> + * </p> * <p> * Current implementations (check each API doc link for details): * <ul> * <li>{@link jogamp.opengl.util.av.NullGLMediaPlayer}</li> * <li>{@link jogamp.opengl.util.av.impl.OMXGLMediaPlayer}</li> * <li>{@link jogamp.opengl.util.av.impl.FFMPEGMediaPlayer}</li> - * <li>{@link jogamp.opengl.android.av.AndroidGLMediaPlayerAPI14}</li> + * <li>{@link jogamp.opengl.android.av.AndroidGLMediaPlayerAPI14}</li> * </ul> * </p> * <p> - * Variable type, value range and dimension has been chosen to suit embedded CPUs - * and characteristics of audio and video streaming. - * Milliseconds of type integer with a maximum value of {@link Integer#MAX_VALUE} - * will allow tracking time up 2,147,483.647 seconds or - * 24 days 20 hours 31 minutes and 23 seconds. - * Milliseconds granularity is also more than enough to deal with A-V synchronization, - * where the threshold usually lies within 100ms. + * Implementations of this interface must implement: + * <pre> + * public static final boolean isAvailable(); + * </pre> + * to be properly considered by {@link GLMediaPlayerFactory#create(ClassLoader, String)} + * and {@link GLMediaPlayerFactory#createDefault()}. + * </p> + * <a name="timestampaccuracy"><h5>Timestamp Accuracy</h5></a> + * <p> + * <p> + * Timestamp type and value range has been chosen to suit embedded CPUs + * and characteristics of audio and video streaming. See {@link TimeFrameI}. + * </p> + * + * <a name="synchronization"><h5>Audio and video synchronization</h5></a> + * <p> + * The class follows a passive A/V synchronization pattern. + * Audio is being untouched, while {@link #getNextTexture(GL)} delivers a new video frame + * only, if its timestamp is less than {@link #MAXIMUM_VIDEO_ASYNC} ahead of <i>time</i>. + * If its timestamp is more than {@link #MAXIMUM_VIDEO_ASYNC} ahead of <i>time</i>, + * the previous frame is returned. + * If its timestamp is more than {@link #MAXIMUM_VIDEO_ASYNC} after <i>time</i>, + * the frame is dropped and the next frame is being fetched. + * </p> + * <p> + * https://en.wikipedia.org/wiki/Audio_to_video_synchronization + * <pre> + * d_av = v_pts - a_pts; + * </pre> + * </p> + * <p> + * Recommendation of audio/video pts time lead/lag at production: + * <ul> + * <li>Overall: +40ms and -60ms audio ahead video / audio after video</li> + * <li>Each stage: +5ms and -15ms. audio ahead video / audio after video</li> + * </ul> + * </p> + * <p> + * Recommendation of av pts time lead/lag at presentation: + * <ul> + * <li>TV: +15ms and -45ms. audio ahead video / audio after video.</li> + * <li>Film: +22ms and -22ms. audio ahead video / audio after video.</li> + * </ul> + * </p> + * + * <a name="teststreams"><h5>Test Streams</h5></a> + * <p> + * <table border="1"> + * <tr><th colspan=5>Big Buck Bunny 24f 16:9</th></tr> + * <tr><td>Big Buck Bunny</td><td>320p</td><td>h264<td>aac 48000Hz 2 chan</td><td>http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4</td></tr> + * <tr><td>Big Buck Bunny</td><td>240p</td><td>h264<td>aac 48000Hz 2 chan</td><td>http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4</td></tr> + * <tr><td>Big Buck Bunny</td><td>720p</td><td>mpeg4<td>ac3 48000Hz 5.1 chan</td><td>http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_surround.avi</td></tr> + * <tr><td>Big Buck Bunny</td><td>720p</td><td>msmpeg4v2<td>mp3 48000Hz 2 chan</td><td>http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_stereo.avi</td></tr> + * <tr><td>Big Buck Bunny</td><td>720p</td><td>theora<td>vorbis 48000Hz 2 chan</td><td>http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_stereo.ogg</td></tr> + * <tr><td>Big Buck Bunny</td><td>1080p</td><td>mpeg4<td>ac3 48000Hz 5.1 chan</td><td>http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_1080p_surround.avi</td></tr> + * <tr><th colspan=5>WebM/Matroska (vp8/vorbis)</th></tr> + * <tr><td>Big Buck Bunny Trailer</td><td>640p</td><td>vp8<td>vorbis 44100Hz 1 chan</td><td>http://video.webmfiles.org/big-buck-bunny_trailer.webm</td></tr> + * <tr><td>Elephants Dream</td><td>540p</td><td>vp8<td>vorbis 44100Hz 1 chan</td><td>http://video.webmfiles.org/elephants-dream.webm</td></tr> + * <tr><th colspan=5>You Tube http/rtsp</th></tr> + * <tr><td>Sintel</td><td colspan=3>http://www.youtube.com/watch?v=eRsGyueVLvQ</td><td>rtsp://v3.cache1.c.youtube.com/CiILENy73wIaGQn0LpXnygYbeRMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp</td></tr> + * <tr><th colspan=5>Audio/Video Sync</th></tr> + * <tr><td>Five-minute-sync-test1080p</td><td colspan=3>https://www.youtube.com/watch?v=szoOsG9137U</td><td>rtsp://v7.cache8.c.youtube.com/CiILENy73wIaGQm133VvsA46sxMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp</td></tr> + * <tr><td>Audio-Video-Sync-Test-Calibration-23.98fps-24fps</td><td colspan=4>https://www.youtube.com/watch?v=cGgf_dbDMsw</td></tr> + * <tr><td>sound_in_sync_test</td><td colspan=4>https://www.youtube.com/watch?v=O-zIZkhXNLE</td></tr> + * <!-- <tr><td> title </td><td>1080p</td><td>mpeg4<td>ac3 48000Hz 5.1 chan</td><td> url </td></tr> --> + * <!-- <tr><td> title </td><td colspan=3> url1 </td><td> url2 </td></tr> + * </table> + * </p> + * <p> + * Since 2.3.0 this interface uses {@link Uri} instead of {@link java.net.URI}. * </p> */ public interface GLMediaPlayer extends TextureSequence { public static final boolean DEBUG = Debug.debug("GLMediaPlayer"); - + public static final boolean DEBUG_NATIVE = Debug.debug("GLMediaPlayer.Native"); + + /** Default texture count, value {@value}. */ + public static final int TEXTURE_COUNT_DEFAULT = 4; + + /** Minimum texture count, value {@value}. Using the minimum texture count disables multi-threaded decoding. */ + public static final int TEXTURE_COUNT_MIN = 1; + + /** Constant {@value} for <i>mute</i> or <i>not available</i>. See <a href="#streamIDs">Audio and video Stream IDs</a>. */ + public static final int STREAM_ID_NONE = -2; + /** Constant {@value} for <i>auto</i> or <i>unspecified</i>. See <a href="#streamIDs">Audio and video Stream IDs</a>. */ + public static final int STREAM_ID_AUTO = -1; + + /** + * {@link Uri#scheme Uri scheme} name {@value} for camera input. E.g. <code>camera:/0</code> + * for the 1st camera device. + * <p> + * The {@link Uri#path Uri path} is being used to identify the camera (<i>ID</i>), + * where the root fwd-slash is being cut-off. + * </p> + * <p> + * The <i>ID</i> is usually an integer value indexing the camera + * ranging from [0..<i>max-number</i>]. + * </p> + * <p> + * The {@link Uri#query Uri query} is used to pass options to the camera + * using <i>;</i> as the separator. The latter avoids trouble w/ escaping. + * </p> + * <pre> + * camera:/<id> + * camera://somewhere/<id> + * camera://somewhere/<id>?width=640;height=480;rate=15 + * camera://somewhere/<id>?size=640x480;rate=15 + * </pre> + * <pre> + * Uri: [scheme:][//authority][path][?query][#fragment] + * w/ authority: [user-info@]host[:port] + * Note: 'path' starts w/ fwd slash + * </pre> + * </p> + */ + public static final Uri.Encoded CameraInputScheme = Uri.Encoded.cast("camera"); + /** Camera property {@value}, size as string, e.g. <code>1280x720</code>, <code>hd720</code>. May not be supported on all platforms. See {@link #CameraInputScheme}. */ + public static final String CameraPropSizeS = "size"; + /** Camera property {@value}. See {@link #CameraInputScheme}. */ + public static final String CameraPropWidth = "width"; + /** Camera property {@value}. See {@link #CameraInputScheme}. */ + public static final String CameraPropHeight = "height"; + /** Camera property {@value}. See {@link #CameraInputScheme}. */ + public static final String CameraPropRate = "rate"; + + /** Maximum video frame async of {@value} milliseconds. */ + public static final int MAXIMUM_VIDEO_ASYNC = 22; + + /** + * A StreamException encapsulates a caught exception in the decoder thread, a.k.a <i>StreamWorker</i>, + * see See <a href="#streamerror"><i>StreamWorker</i> Error Handling</a>. + */ + @SuppressWarnings("serial") + public static class StreamException extends Exception { + public StreamException(final Throwable cause) { + super(cause); + } + public StreamException(final String message, final Throwable cause) { + super(message, cause); + } + } + + /** + * {@inheritDoc} + * <p> + * As the contract of {@link TexSeqEventListener} requests, + * implementations of {@link GLMediaEventListener} shall also: + * <ul> + * <li>off-load complex or {@link GLMediaPlayer} commands on another thread, or</li> + * <li>simply changing a volatile state of their {@link GLEventListener} implementation.</li> + * </ul> + * </p> + */ public interface GLMediaEventListener extends TexSeqEventListener<GLMediaPlayer> { - - static final int EVENT_CHANGE_SIZE = 1<<0; - static final int EVENT_CHANGE_FPS = 1<<1; - static final int EVENT_CHANGE_BPS = 1<<2; - static final int EVENT_CHANGE_LENGTH = 1<<3; - static final int EVENT_CHANGE_CODEC = 1<<3; - + + /** State changed to {@link State#Initialized}. See <a href="#lifecycle">Lifecycle</a>.*/ + static final int EVENT_CHANGE_INIT = 1<<0; + /** State changed to {@link State#Uninitialized}. See <a href="#lifecycle">Lifecycle</a>.*/ + static final int EVENT_CHANGE_UNINIT = 1<<1; + /** State changed to {@link State#Playing}. See <a href="#lifecycle">Lifecycle</a>.*/ + static final int EVENT_CHANGE_PLAY = 1<<2; + /** State changed to {@link State#Paused}. See <a href="#lifecycle">Lifecycle</a>.*/ + static final int EVENT_CHANGE_PAUSE = 1<<3; + /** End of stream reached. See <a href="#lifecycle">Lifecycle</a>.*/ + static final int EVENT_CHANGE_EOS = 1<<4; + /** An error occurred, e.g. during off-thread initialization. See {@link StreamException} and <a href="#lifecycle">Lifecycle</a>. */ + static final int EVENT_CHANGE_ERR = 1<<5; + + /** Stream video id change. */ + static final int EVENT_CHANGE_VID = 1<<16; + /** Stream audio id change. */ + static final int EVENT_CHANGE_AID = 1<<17; + /** TextureFrame size or vertical flip change. */ + static final int EVENT_CHANGE_SIZE = 1<<18; + /** Stream fps change. */ + static final int EVENT_CHANGE_FPS = 1<<19; + /** Stream bps change. */ + static final int EVENT_CHANGE_BPS = 1<<20; + /** Stream length change. */ + static final int EVENT_CHANGE_LENGTH = 1<<21; + /** Stream codec change. */ + static final int EVENT_CHANGE_CODEC = 1<<22; + /** - * @param mp the event source + * @param mp the event source * @param event_mask the changes attributes - * @param when system time in msec. + * @param when system time in msec. */ - public void attributesChanges(GLMediaPlayer mp, int event_mask, long when); + public void attributesChanged(GLMediaPlayer mp, int event_mask, long when); } - + + /** + * See <a href="#lifecycle">Lifecycle</a>. + */ public enum State { - Uninitialized(0), Stopped(1), Playing(2), Paused(3); - + /** Uninitialized player, no resources shall be hold. */ + Uninitialized(0), + /** Stream has been initialized, user may play or call {@link #initGL(GL)}. */ + Initialized(1), + /** Stream is playing. */ + Playing(2), + /** Stream is pausing. */ + Paused(3); + public final int id; - State(int id){ + State(final int id){ this.id = id; } } - + public int getTextureCount(); - - /** Defaults to 0 */ + + /** Sets the texture unit. Defaults to 0. */ public void setTextureUnit(int u); + /** Sets the texture min-mag filter, defaults to {@link GL#GL_NEAREST}. */ public void setTextureMinMagFilter(int[] minMagFilter); /** Sets the texture min-mag filter, defaults to {@link GL#GL_CLAMP_TO_EDGE}. */ public void setTextureWrapST(int[] wrapST); - - /** - * Sets the stream to be used. Initializes all stream related states inclusive OpenGL ones, - * if <code>gl</code> is not null. - * <p> - * Uninitialized -> Stopped - * </p> - * @param gl current GL object. If null, no video output and textures will be available. - * @param urlConn the stream connection - * @return the new state - * - * @throws IllegalStateException if not invoked in state Uninitialized - * @throws IOException in case of difficulties to open or process the stream + + /** + * Issues asynchronous stream initialization. + * <p> + * <a href="#lifecycle">Lifecycle</a>: {@link State#Uninitialized} -> {@link State#Initialized}<sup><a href="#streamworker">1</a></sup> or {@link State#Uninitialized} + * </p> + * <p> + * {@link State#Initialized} is reached asynchronous, + * i.e. user gets notified via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}. + * </p> + * <p> + * A possible caught asynchronous {@link StreamException} while initializing the stream off-thread + * will be thrown at {@link #initGL(GL)}. + * </p> + * <p> + * Muted audio can be achieved by passing {@link #STREAM_ID_NONE} to <code>aid</code>. + * </p> + * <p> + * Muted video can be achieved by passing {@link #STREAM_ID_NONE} to <code>vid</code>, + * in which case <code>textureCount</code> is ignored as well as the passed GL object of the subsequent {@link #initGL(GL)} call. + * </p> + * @param streamLoc the stream location + * @param vid video stream id, see <a href="#streamIDs">audio and video Stream IDs</a> + * @param aid video stream id, see <a href="#streamIDs">audio and video Stream IDs</a> + * @param textureCount desired number of buffered textures to be decoded off-thread, will be validated by implementation. + * The minimum value is {@link #TEXTURE_COUNT_DEFAULT}. + * Ignored if video is muted. + * @throws IllegalStateException if not invoked in {@link State#Uninitialized} + * @throws IllegalArgumentException if arguments are invalid + * @since 2.3.0 + */ + public void initStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; + + /** + * Returns the {@link StreamException} caught in the decoder thread, or <code>null</code> if none occured. + * <p> + * Method clears the cached {@link StreamException}, hence an immediate subsequent call will return <code>null</code>. + * </p> + * @see GLMediaEventListener#EVENT_CHANGE_ERR + * @see StreamException + */ + public StreamException getStreamException(); + + /** + * Initializes OpenGL related resources. + * <p> + * <a href="#lifecycle">Lifecycle</a>: {@link State#Initialized} -> {@link State#Paused} or {@link State#Initialized} + * </p> + * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(Uri, int, int, int)}. + * + * @param gl current GL object. Maybe <code>null</code>, for audio only. + * @throws IllegalStateException if not invoked in {@link State#Initialized}. + * @throws StreamException forwarded from the off-thread stream initialization * @throws GLException in case of difficulties to initialize the GL resources */ - public State initGLStream(GL gl, URLConnection urlConn) throws IllegalStateException, GLException, IOException; - + public void initGL(GL gl) throws IllegalStateException, StreamException, GLException; + /** - * Releases the GL and stream resources. + * If implementation uses a {@link AudioSink}, it's instance will be returned. * <p> - * <code>ANY</code> -> Uninitialized + * The {@link AudioSink} instance is available after {@link #initStream(Uri, int, int, int)}, + * if used by implementation. + * </p> + */ + public AudioSink getAudioSink(); + + /** + * Releases the GL, stream and other resources, including {@link #attachObject(String, Object) attached user objects}. + * <p> + * <a href="#lifecycle">Lifecycle</a>: <code>ANY</code> -> {@link State#Uninitialized} * </p> */ public State destroy(GL gl); - public void setPlaySpeed(float rate); + /** + * Sets the playback speed. + * <p> + * To simplify test, play speed is <i>normalized</i>, i.e. + * <ul> + * <li><code>1.0f</code>: if <code> Math.abs(1.0f - rate) < 0.01f </code></li> + * </ul> + * </p> + * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. + */ + public boolean setPlaySpeed(float rate); + /** Returns the playback speed. */ public float getPlaySpeed(); /** - * Stopped/Paused -> Playing + * Sets the audio volume, [0f..1f]. + * <p> + * To simplify test, volume is <i>normalized</i>, i.e. + * <ul> + * <li><code>0.0f</code>: if <code> Math.abs(v) < 0.01f </code></li> + * <li><code>1.0f</code>: if <code> Math.abs(1.0f - v) < 0.01f </code></li> + * </ul> + * </p> + * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. */ - public State start(); + public boolean setAudioVolume(float v); + + /** Returns the audio volume. */ + public float getAudioVolume(); /** - * Playing -> Paused + * Starts or resumes the <i>StreamWorker</i> decoding thread. + * <p> + * <a href="#lifecycle">Lifecycle</a>: {@link State#Paused} -> {@link State#Playing} + * </p> */ - public State pause(); + public State play(); /** - * Playing/Paused -> Stopped + * Pauses the <i>StreamWorker</i> decoding thread. + * <p> + * <a href="#lifecycle">Lifecycle</a>: {@link State#Playing} -> {@link State#Paused} + * </p> + * <p> + * If a <i>new</i> frame is desired after the next {@link #play()} call, + * e.g. to make a snapshot of a camera input stream, + * <code>flush</code> shall be set to <code>true</code>. + * </p> + * @param flush if <code>true</code> flushes the video and audio buffers, otherwise keep them intact. */ - public State stop(); - + public State pause(boolean flush); + /** - * @return the current state, either Uninitialized, Stopped, Playing, Paused + * Seeks to the new absolute position. The <i>StreamWorker</i> decoding thread + * is paused while doing so and the A/V buffers are flushed. + * <p> + * Allowed in state {@link State#Playing} and {@link State#Paused}, otherwise ignored, + * see <a href="#lifecycle">Lifecycle</a>. + * </p> + * + * @param msec absolute desired time position in milliseconds + * @return time current position in milliseconds, after seeking to the desired position + **/ + public int seek(int msec); + + /** + * See <a href="#lifecycle">Lifecycle</a>. + * @return the current state, either {@link State#Uninitialized}, {@link State#Initialized}, {@link State#Playing} or {@link State#Paused} */ public State getState(); - + /** - * @return time current position in milliseconds + * Return the video stream id, see <a href="#streamIDs">audio and video Stream IDs</a>. + */ + public int getVID(); + + /** + * Return the audio stream id, see <a href="#streamIDs">audio and video Stream IDs</a>. + */ + public int getAID(); + + /** + * @return the current decoded frame count since {@link #play()} and {@link #seek(int)} + * as increased by {@link #getNextTexture(GL)} or the decoding thread. + */ + public int getDecodedFrameCount(); + + /** + * @return the current presented frame count since {@link #play()} and {@link #seek(int)} + * as increased by {@link #getNextTexture(GL)} for new frames. + */ + public int getPresentedFrameCount(); + + /** + * @return current video presentation timestamp (PTS) in milliseconds of {@link #getLastTexture()} **/ - public int getCurrentPosition(); + public int getVideoPTS(); /** - * Allowed in state Stopped, Playing and Paused, otherwise ignored. - * - * @param msec absolute desired time position in milliseconds - * @return time current position in milliseconds, after seeking to the desired position + * @return current audio presentation timestamp (PTS) in milliseconds. **/ - public int seek(int msec); + public int getAudioPTS(); /** * {@inheritDoc} + * <p> + * See <a href="#synchronization">audio and video synchronization</a>. + * </p> + * @throws IllegalStateException if not invoked in {@link State#Paused} or {@link State#Playing} */ @Override public TextureSequence.TextureFrame getLastTexture() throws IllegalStateException; /** * {@inheritDoc} - * + * * <p> * In case the current state is not {@link State#Playing}, {@link #getLastTexture()} is returned. * </p> - * + * <p> + * See <a href="#synchronization">audio and video synchronization</a>. + * </p> + * @throws IllegalStateException if not invoked in {@link State#Paused} or {@link State#Playing} + * * @see #addEventListener(GLMediaEventListener) - * @see GLMediaEventListener#newFrameAvailable(GLMediaPlayer, long) + * @see GLMediaEventListener#newFrameAvailable(GLMediaPlayer, TextureFrame, long) */ @Override - public TextureSequence.TextureFrame getNextTexture(GL gl, boolean blocking) throws IllegalStateException; - - public URLConnection getURLConnection(); + public TextureSequence.TextureFrame getNextTexture(GL gl) throws IllegalStateException; + + /** + * Return the stream location, as set by {@link #initStream(Uri, int, int, int)}. + * @since 2.3.0 + */ + public Uri getUri(); /** * <i>Warning:</i> Optional information, may not be supported by implementation. - * @return the code of the video stream, if available + * @return the code of the video stream, if available */ public String getVideoCodec(); /** * <i>Warning:</i> Optional information, may not be supported by implementation. - * @return the code of the audio stream, if available + * @return the code of the audio stream, if available */ public String getAudioCodec(); @@ -202,47 +561,100 @@ public interface GLMediaPlayer extends TextureSequence { * <i>Warning:</i> Optional information, may not be supported by implementation. * @return the total number of video frames */ - public long getTotalFrames(); + public int getVideoFrames(); + + /** + * <i>Warning:</i> Optional information, may not be supported by implementation. + * @return the total number of audio frames + */ + public int getAudioFrames(); /** * @return total duration of stream in msec. */ public int getDuration(); - + /** * <i>Warning:</i> Optional information, may not be supported by implementation. - * @return the overall bitrate of the stream. + * @return the overall bitrate of the stream. */ public long getStreamBitrate(); /** * <i>Warning:</i> Optional information, may not be supported by implementation. - * @return video bitrate + * @return video bitrate */ public int getVideoBitrate(); - + /** * <i>Warning:</i> Optional information, may not be supported by implementation. - * @return the audio bitrate + * @return the audio bitrate */ public int getAudioBitrate(); - + /** * <i>Warning:</i> Optional information, may not be supported by implementation. * @return the framerate of the video */ public float getFramerate(); + /** + * Returns <code>true</code> if the video frame is oriented in + * OpenGL's coordinate system, <i>origin at bottom left</i>. + * <p> + * Otherwise returns <code>false</code>, i.e. + * video frame is oriented <i>origin at top left</i>. + * </p> + * <p> + * <code>false</code> is the default assumption for videos, + * but user shall not rely on. + * </p> + * <p> + * <code>false</code> GL orientation leads to + * {@link Texture#getMustFlipVertically()} == <code>true</code>, + * as reflected by all {@link TextureFrame}'s {@link Texture}s + * retrieved via {@link #getLastTexture()} or {@link #getNextTexture(GL)}. + * </p> + */ + public boolean isGLOriented(); + + /** Returns the width of the video. */ public int getWidth(); + /** Returns the height of the video. */ public int getHeight(); + /** Returns a string represantation of this player, incl. state and audio/video details. */ + @Override public String toString(); + /** Returns a string represantation of this player's performance values. */ + public String getPerfString(); + + /** Adds a {@link GLMediaEventListener} to this player. */ public void addEventListener(GLMediaEventListener l); + /** Removes a {@link GLMediaEventListener} to this player. */ public void removeEventListener(GLMediaEventListener l); - public GLMediaEventListener[] getEventListeners(); + /** Return all {@link GLMediaEventListener} of this player. */ + public GLMediaEventListener[] getEventListeners(); + + /** + * Returns the attached user object for the given name. + */ + public Object getAttachedObject(String name); + + /** + * Attaches the user object for the given name. + * Returns the previously set object, may be null. + */ + public Object attachObject(String name, Object obj); + + /** + * Detaches the user object for the given name. + * Returns the previously set object, may be null. + */ + public Object detachObject(String name); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayerFactory.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayerFactory.java index 6fcf20ed2..9378cec91 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayerFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayerFactory.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -29,27 +29,38 @@ package com.jogamp.opengl.util.av; import jogamp.opengl.util.av.NullGLMediaPlayer; -import com.jogamp.common.os.AndroidVersion; -import com.jogamp.common.os.Platform; import com.jogamp.common.util.ReflectionUtil; public class GLMediaPlayerFactory { private static final String AndroidGLMediaPlayerAPI14ClazzName = "jogamp.opengl.android.av.AndroidGLMediaPlayerAPI14"; private static final String FFMPEGMediaPlayerClazzName = "jogamp.opengl.util.av.impl.FFMPEGMediaPlayer"; + private static final String OMXGLMediaPlayerClazzName = "jogamp.opengl.util.av.impl.OMXGLMediaPlayer"; private static final String isAvailableMethodName = "isAvailable"; - - public static GLMediaPlayer create() { + + public static GLMediaPlayer createDefault() { final ClassLoader cl = GLMediaPlayerFactory.class.getClassLoader(); - if(Platform.OS_TYPE.equals(Platform.OSType.ANDROID)) { - if(AndroidVersion.SDK_INT >= 14) { - if(((Boolean)ReflectionUtil.callStaticMethod(AndroidGLMediaPlayerAPI14ClazzName, isAvailableMethodName, null, null, cl)).booleanValue()) { - return (GLMediaPlayer) ReflectionUtil.createInstance(AndroidGLMediaPlayerAPI14ClazzName, cl); - } - } + GLMediaPlayer sink = create(cl, OMXGLMediaPlayerClazzName); + if( null == sink ) { + sink = create(cl, AndroidGLMediaPlayerAPI14ClazzName); } - if(((Boolean)ReflectionUtil.callStaticMethod(FFMPEGMediaPlayerClazzName, isAvailableMethodName, null, null, cl)).booleanValue()) { - return (GLMediaPlayer) ReflectionUtil.createInstance(FFMPEGMediaPlayerClazzName, cl); + if( null == sink ) { + sink = create(cl, FFMPEGMediaPlayerClazzName); } + if( null == sink ) { + sink = createNull(); + } + return sink; + } + public static GLMediaPlayer createNull() { return new NullGLMediaPlayer(); } + + public static GLMediaPlayer create(final ClassLoader cl, final String implName) { + try { + if(((Boolean)ReflectionUtil.callStaticMethod(implName, isAvailableMethodName, null, null, cl)).booleanValue()) { + return (GLMediaPlayer) ReflectionUtil.createInstance(implName, cl); + } + } catch (final Throwable t) { if(GLMediaPlayer.DEBUG) { System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); t.printStackTrace(); } } + return null; + } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java new file mode 100644 index 000000000..04bc0a15d --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java @@ -0,0 +1,249 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.awt; + +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.Buffer; +import java.nio.IntBuffer; + +import javax.media.opengl.GL; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.GLPixelBuffer; + +/** + * AWT {@link GLPixelBuffer} backed by an {@link BufferedImage} of type + * {@link BufferedImage#TYPE_INT_ARGB} or {@link BufferedImage#TYPE_INT_RGB}. + * <p> + * Implementation uses an array backed {@link IntBuffer}. + * </p> + * <p> + * {@link AWTGLPixelBuffer} can be produced via {@link AWTGLPixelBufferProvider}'s + * {@link AWTGLPixelBufferProvider#allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocate(..)}. + * </p> + * <p> + * See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)} for {@link #allowRowStride} details. + * </p> + * <p> + * If using <code>allowRowStride == true</code>, user may needs to get the {@link #getAlignedImage(int, int) aligned image} + * since {@link #requiresNewBuffer(GL, int, int, int)} will allow different width in this case. + * </p> + */ +public class AWTGLPixelBuffer extends GLPixelBuffer { + public static final GLPixelAttributes awtPixelAttributesIntRGBA4 = new GLPixelAttributes(4, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); + public static final GLPixelAttributes awtPixelAttributesIntRGB3 = new GLPixelAttributes(3, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE); + + /** The underlying {@link BufferedImage}. */ + public final BufferedImage image; + + /** + * + * @param pixelAttributes the desired {@link GLPixelAttributes} + * @param width in pixels + * @param height in pixels + * @param depth in pixels + * @param pack true for read mode GPU -> CPU, otherwise false for write mode CPU -> GPU + * @param image the AWT image + * @param buffer the backing array + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link #requiresNewBuffer(GL, int, int, int)}. + * If <code>true</code>, user shall decide whether to use a {@link #getAlignedImage(int, int) width-aligned image}. + */ + public AWTGLPixelBuffer(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final BufferedImage image, + final Buffer buffer, final boolean allowRowStride) { + super(pixelAttributes, width, height, depth, pack, buffer, allowRowStride); + this.image = image; + } + + @Override + public void dispose() { + image.flush(); + super.dispose(); + } + + /** + * Returns a width- and height-aligned image representation sharing data w/ {@link #image}. + * @param width + * @param height + * @return + * @throws IllegalArgumentException if requested size exceeds image size + */ + public BufferedImage getAlignedImage(final int width, final int height) throws IllegalArgumentException { + if( width * height > image.getWidth() * image.getHeight() ) { + throw new IllegalArgumentException("Requested size exceeds image size: "+width+"x"+height+" > "+image.getWidth()+"x"+image.getHeight()); + } + if( width == image.getWidth() && height == image.getHeight() ) { + return image; + } else { + final ColorModel cm = image.getColorModel(); + final WritableRaster raster0 = image.getRaster(); + final DataBuffer dataBuffer = raster0.getDataBuffer(); + final SinglePixelPackedSampleModel sppsm0 = (SinglePixelPackedSampleModel) raster0.getSampleModel(); + final SinglePixelPackedSampleModel sppsm1 = new SinglePixelPackedSampleModel(dataBuffer.getDataType(), + width, height, width /* scanLineStride */, sppsm0.getBitMasks()); + final WritableRaster raster1 = Raster.createWritableRaster(sppsm1, dataBuffer, null); + return new BufferedImage (cm, raster1, cm.isAlphaPremultiplied(), null); + } + } + + public final boolean isDataBufferSource(final BufferedImage imageU) { + final DataBuffer dataBuffer0 = image.getRaster().getDataBuffer(); + final DataBuffer dataBufferU = imageU.getRaster().getDataBuffer(); + return dataBufferU == dataBuffer0; + } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb = super.toString(sb); + sb.append(", allowRowStride ").append(allowRowStride).append(", image [").append(image.getWidth()).append("x").append(image.getHeight()).append(", ").append(image.toString()).append("]"); + return sb; + } + @Override + public String toString() { + return "AWTGLPixelBuffer["+toString(null).toString()+"]"; + } + + /** + * Provider for {@link AWTGLPixelBuffer} instances. + */ + public static class AWTGLPixelBufferProvider implements GLPixelBufferProvider { + private final boolean allowRowStride; + + /** + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. + * See {@link #getAllowRowStride()} and {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + * If <code>true</code>, user shall decide whether to use a {@link AWTGLPixelBuffer#getAlignedImage(int, int) width-aligned image}. + */ + public AWTGLPixelBufferProvider(final boolean allowRowStride) { + this.allowRowStride = allowRowStride; + } + @Override + public boolean getAllowRowStride() { return allowRowStride; } + + @Override + public GLPixelAttributes getAttributes(final GL gl, final int componentCount) { + return 4 == componentCount ? awtPixelAttributesIntRGBA4 : awtPixelAttributesIntRGB3; + } + + /** + * {@inheritDoc} + * <p> + * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>. + * </p> + */ + @Override + public AWTGLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { + final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer ); + return new AWTGLPixelBuffer(pixelAttributes, width, height, depth, pack, image, ibuffer, allowRowStride); + } + } + + /** + * Provider for singleton {@link AWTGLPixelBuffer} instances. + * <p> + * Provider instance holds the last {@link AWTGLPixelBuffer} instance + * {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * A new {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocation} + * will return same instance, if a new buffer is not {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int) required}. + * The latter is true if size are compatible, hence <code>allowRowStride</code> should be enabled, if possible. + * </p> + */ + public static class SingleAWTGLPixelBufferProvider extends AWTGLPixelBufferProvider implements SingletonGLPixelBufferProvider { + private AWTGLPixelBuffer singleRGBA4 = null; + private AWTGLPixelBuffer singleRGB3 = null; + + /** + * @param allowRowStride If <code>true</code>, allow row-stride, otherwise not. See {@link AWTGLPixelBuffer#requiresNewBuffer(GL, int, int, int)}. + */ + public SingleAWTGLPixelBufferProvider(final boolean allowRowStride) { + super(allowRowStride); + } + + /** + * {@inheritDoc} + * <p> + * Returns an array backed {@link IntBuffer} of size <pre>width*height*{@link Buffers#SIZEOF_INT SIZEOF_INT}</code>. + * </p> + */ + @Override + public AWTGLPixelBuffer allocate(final GL gl, final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { + if( 4 == pixelAttributes.componentCount ) { + if( null == singleRGBA4 || singleRGBA4.requiresNewBuffer(gl, width, height, minByteSize) ) { + singleRGBA4 = allocateImpl(pixelAttributes, width, height, depth, pack, minByteSize); + } + return singleRGBA4; + } else { + if( null == singleRGB3 || singleRGB3.requiresNewBuffer(gl, width, height, minByteSize) ) { + singleRGB3 = allocateImpl(pixelAttributes, width, height, depth, pack, minByteSize); + } + return singleRGB3; + } + } + + private AWTGLPixelBuffer allocateImpl(final GLPixelAttributes pixelAttributes, final int width, final int height, final int depth, final boolean pack, final int minByteSize) { + final BufferedImage image = new BufferedImage(width, height, 4 == pixelAttributes.componentCount ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + final int[] readBackIntBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final Buffer ibuffer = IntBuffer.wrap( readBackIntBuffer ); + return new AWTGLPixelBuffer(pixelAttributes, width, height, depth, pack, image, ibuffer, getAllowRowStride()); + } + + /** Return the last {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated} {@link AWTGLPixelBuffer} w/ {@link GLPixelAttributes#componentCount}. */ + @Override + public AWTGLPixelBuffer getSingleBuffer(final GLPixelAttributes pixelAttributes) { + return 4 == pixelAttributes.componentCount ? singleRGBA4 : singleRGB3; + } + + /** + * Initializes the single {@link AWTGLPixelBuffer} w/ a given size, if not yet {@link #allocate(GL, GLPixelAttributes, int, int, int, boolean, int) allocated}. + * @return the newly initialized single {@link AWTGLPixelBuffer}, or null if already allocated. + */ + @Override + public AWTGLPixelBuffer initSingleton(final int componentCount, final int width, final int height, final int depth, final boolean pack) { + if( 4 == componentCount ) { + if( null != singleRGBA4 ) { + return null; + } + singleRGBA4 = allocateImpl(AWTGLPixelBuffer.awtPixelAttributesIntRGBA4, width, height, depth, pack, 0); + return singleRGBA4; + } else { + if( null != singleRGB3 ) { + return null; + } + singleRGB3 = allocateImpl(AWTGLPixelBuffer.awtPixelAttributesIntRGB3, width, height, depth, pack, 0); + return singleRGB3; + } + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java new file mode 100644 index 000000000..b75ad3b17 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java @@ -0,0 +1,112 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.awt; + +import java.awt.image.BufferedImage; + +import javax.media.opengl.GL; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLProfile; + +import com.jogamp.opengl.util.GLReadBufferUtil; + +/** + * {@link GLReadBufferUtil} specialization allowing to + * read out a frambuffer to an AWT BufferedImage + * utilizing {@link AWTPixelBufferProviderInt} for further AWT processing. + */ +public class AWTGLReadBufferUtil extends GLReadBufferUtil { + /** + * {@inheritDoc} + * + * @param alpha + */ + public AWTGLReadBufferUtil(final GLProfile glp, final boolean alpha) { + super(new AWTGLPixelBuffer.AWTGLPixelBufferProvider( glp.isGL2ES3() /* allowRowStride */ ), alpha, false); + } + + /** + * Returns the {@link AWTGLPixelBuffer}, as filled by previous call to {@link #readPixels(GL, int, int, int, int, boolean)}. + */ + public AWTGLPixelBuffer getAWTGLPixelBuffer() { return (AWTGLPixelBuffer)this.getPixelBuffer(); } + + /** + * Read the drawable's pixels to TextureData and Texture, if requested at construction, + * and returns an aligned {@link BufferedImage}. + * + * @param gl the current GL context object. It's read drawable is being used as the pixel source. + * @param awtOrientation flips the data vertically if <code>true</code>. + * The context's drawable {@link GLDrawable#isGLOriented()} state + * is taken into account. + * Vertical flipping is propagated to TextureData + * and handled in a efficient manner there (TextureCoordinates and TextureIO writer). + * @see #AWTGLReadBufferUtil(GLProfile, boolean) + */ + public BufferedImage readPixelsToBufferedImage(final GL gl, final boolean awtOrientation) { + return readPixelsToBufferedImage(gl, 0, 0, 0, 0, awtOrientation); + } + + /** + * Read the drawable's pixels to TextureData and Texture, if requested at construction, + * and returns an aligned {@link BufferedImage}. + * + * @param gl the current GL context object. It's read drawable is being used as the pixel source. + * @param inX readPixel x offset + * @param inY readPixel y offset + * @param inWidth optional readPixel width value, used if [1 .. drawable.width], otherwise using drawable.width + * @param inHeight optional readPixel height, used if [1 .. drawable.height], otherwise using drawable.height + * @param awtOrientation flips the data vertically if <code>true</code>. + * The context's drawable {@link GLDrawable#isGLOriented()} state + * is taken into account. + * Vertical flipping is propagated to TextureData + * and handled in a efficient manner there (TextureCoordinates and TextureIO writer). + * @see #AWTGLReadBufferUtil(GLProfile, boolean) + */ + public BufferedImage readPixelsToBufferedImage(final GL gl, final int inX, final int inY, final int inWidth, final int inHeight, final boolean awtOrientation) { + final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + final int width, height; + if( 0 >= inWidth || drawable.getSurfaceWidth() < inWidth ) { + width = drawable.getSurfaceWidth(); + } else { + width = inWidth; + } + if( 0 >= inHeight || drawable.getSurfaceHeight() < inHeight ) { + height = drawable.getSurfaceHeight(); + } else { + height= inHeight; + } + if( readPixelsImpl(drawable, gl, inX, inY, width, height, awtOrientation) ) { + final BufferedImage image = getAWTGLPixelBuffer().getAlignedImage(width, height); + if( getTextureData().getMustFlipVertically() ) { + ImageUtil.flipImageVertically(image); + } + return image; + } + return null; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/ImageUtil.java b/src/jogl/classes/com/jogamp/opengl/util/awt/ImageUtil.java index a3139b16a..4023a06f0 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/ImageUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/ImageUtil.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -50,11 +50,11 @@ public class ImageUtil { /** Flips the supplied BufferedImage vertically. This is often a necessary conversion step to display a Java2D image correctly with OpenGL and vice versa. */ - public static void flipImageVertically(BufferedImage image) { - WritableRaster raster = image.getRaster(); + public static void flipImageVertically(final BufferedImage image) { + final WritableRaster raster = image.getRaster(); Object scanline1 = null; Object scanline2 = null; - + for (int i = 0; i < image.getHeight() / 2; i++) { scanline1 = raster.getDataElements(0, i, image.getWidth(), 1, scanline1); scanline2 = raster.getDataElements(0, image.getHeight() - i - 1, image.getWidth(), 1, scanline2); @@ -73,8 +73,8 @@ public class ImageUtil { * * @return A instance of <code>BufferedImage</code> with a type compatible with the graphics card. */ - public static BufferedImage createCompatibleImage(int width, int height) { - GraphicsConfiguration configuration = + public static BufferedImage createCompatibleImage(final int width, final int height) { + final GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(); return configuration.createCompatibleImage(width, height); @@ -92,35 +92,35 @@ public class ImageUtil { * * @return A thumbnail with the requested width or the original picture if thumbWidth = image.getWidth() */ - public static BufferedImage createThumbnail(BufferedImage image, int thumbWidth) { + public static BufferedImage createThumbnail(final BufferedImage image, final int thumbWidth) { // Thanks to Romain Guy for this utility if (thumbWidth > image.getWidth()) { throw new IllegalArgumentException("Thumbnail width must be greater than image width"); } - + if (thumbWidth == image.getWidth()) { return image; } - - float ratio = (float) image.getWidth() / (float) image.getHeight(); + + final float ratio = (float) image.getWidth() / (float) image.getHeight(); int width = image.getWidth(); BufferedImage thumb = image; - + do { width /= 2; if (width < thumbWidth) { width = thumbWidth; } - - BufferedImage temp = createCompatibleImage(width, (int) (width / ratio)); - Graphics2D g2 = temp.createGraphics(); + + final BufferedImage temp = createCompatibleImage(width, (int) (width / ratio)); + final Graphics2D g2 = temp.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null); g2.dispose(); thumb = temp; } while (width != thumbWidth); - + return thumb; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/Overlay.java b/src/jogl/classes/com/jogamp/opengl/util/awt/Overlay.java index 73d694cd9..a78bc9bb3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/Overlay.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/Overlay.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -49,13 +49,13 @@ import javax.media.opengl.*; efficiency. */ public class Overlay { - private GLDrawable drawable; + private final GLDrawable drawable; private TextureRenderer renderer; private boolean contentsLost; /** Creates a new Java 2D overlay on top of the specified GLDrawable. */ - public Overlay(GLDrawable drawable) { + public Overlay(final GLDrawable drawable) { this.drawable = drawable; } @@ -103,7 +103,7 @@ public class Overlay { @param height the height of the region to update @throws GLException If an OpenGL context is not current when this method is called */ - public void markDirty(int x, int y, int width, int height) { + public void markDirty(final int x, final int y, final int width, final int height) { renderer.markDirty(x, y, width, height); } @@ -119,7 +119,7 @@ public class Overlay { */ public void drawAll() throws GLException { beginRendering(); - draw(0, 0, drawable.getWidth(), drawable.getHeight()); + draw(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); endRendering(); } @@ -130,7 +130,7 @@ public class Overlay { @throws GLException If an OpenGL context is not current when this method is called */ public void beginRendering() throws GLException { - renderer.beginOrthoRendering(drawable.getWidth(), drawable.getHeight()); + renderer.beginOrthoRendering(drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); } /** Ends the OpenGL rendering process for the overlay. This is @@ -159,7 +159,7 @@ public class Overlay { @throws GLException If an OpenGL context is not current when this method is called */ - public void draw(int x, int y, int width, int height) throws GLException { + public void draw(final int x, final int y, final int width, final int height) throws GLException { draw(x, y, x, y, width, height); } @@ -184,9 +184,9 @@ public class Overlay { @throws GLException If an OpenGL context is not current when this method is called */ - public void draw(int screenx, int screeny, - int overlayx, int overlayy, - int width, int height) throws GLException { + public void draw(final int screenx, final int screeny, + final int overlayx, final int overlayy, + final int width, final int height) throws GLException { renderer.drawOrthoRect(screenx, screeny, overlayx, overlayy, width, height); @@ -198,13 +198,13 @@ public class Overlay { private void validateRenderer() { if (renderer == null) { - renderer = new TextureRenderer(drawable.getWidth(), - drawable.getHeight(), + renderer = new TextureRenderer(drawable.getSurfaceWidth(), + drawable.getSurfaceHeight(), true); contentsLost = true; - } else if (renderer.getWidth() != drawable.getWidth() || - renderer.getHeight() != drawable.getHeight()) { - renderer.setSize(drawable.getWidth(), drawable.getHeight()); + } else if (renderer.getWidth() != drawable.getSurfaceWidth() || + renderer.getHeight() != drawable.getSurfaceHeight()) { + renderer.setSize(drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); contentsLost = true; } else { contentsLost = false; diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java b/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java deleted file mode 100644 index 0022d5c2d..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - */ - -package com.jogamp.opengl.util.awt; - -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; - -import javax.imageio.ImageIO; -import javax.media.opengl.GL2; -import javax.media.opengl.GLException; -import javax.media.opengl.glu.gl2.GLUgl2; - -import com.jogamp.common.util.IOUtil; -import com.jogamp.opengl.GLExtensions; -import com.jogamp.opengl.util.GLPixelStorageModes; -import com.jogamp.opengl.util.TGAWriter; - -/** Utilities for taking screenshots of OpenGL applications. */ - -public class Screenshot { - private Screenshot() {} - - /** - * Takes a fast screenshot of the current OpenGL drawable to a Targa - * file. Requires the OpenGL context for the desired drawable to be - * current. Takes the screenshot from the last assigned read buffer, - * or the OpenGL default read buffer if none has been specified by - * the user (GL_FRONT for single-buffered configurations and GL_BACK - * for double-buffered configurations). This is the fastest - * mechanism for taking a screenshot of an application. Contributed - * by Carsten Weisse of Bytonic Software (http://bytonic.de/). <p> - * - * No alpha channel is written with this variant. - * - * @param file the file to write containing the screenshot - * @param width the width of the current drawable - * @param height the height of the current drawable - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * @throws IOException if an I/O error occurred while writing the - * file - */ - public static void writeToTargaFile(File file, - int width, - int height) throws GLException, IOException { - writeToTargaFile(file, width, height, false); - } - - /** - * Takes a fast screenshot of the current OpenGL drawable to a Targa - * file. Requires the OpenGL context for the desired drawable to be - * current. Takes the screenshot from the last assigned read buffer, - * or the OpenGL default read buffer if none has been specified by - * the user (GL_FRONT for single-buffered configurations and GL_BACK - * for double-buffered configurations). This is the fastest - * mechanism for taking a screenshot of an application. Contributed - * by Carsten Weisse of Bytonic Software (http://bytonic.de/). - * - * @param file the file to write containing the screenshot - * @param width the width of the current drawable - * @param height the height of the current drawable - * @param alpha whether the alpha channel should be saved. If true, - * requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * @throws IOException if an I/O error occurred while writing the - * file - */ - public static void writeToTargaFile(File file, - int width, - int height, - boolean alpha) throws GLException, IOException { - writeToTargaFile(file, 0, 0, width, height, alpha); - } - - /** - * Takes a fast screenshot of the current OpenGL drawable to a Targa - * file. Requires the OpenGL context for the desired drawable to be - * current. Takes the screenshot from the last assigned read buffer, - * or the OpenGL default read buffer if none has been specified by - * the user (GL_FRONT for single-buffered configurations and GL_BACK - * for double-buffered configurations). This is the fastest - * mechanism for taking a screenshot of an application. Contributed - * by Carsten Weisse of Bytonic Software (http://bytonic.de/). - * - * @param file the file to write containing the screenshot - * @param x the starting x coordinate of the screenshot, measured from the lower-left - * @param y the starting y coordinate of the screenshot, measured from the lower-left - * @param width the width of the desired screenshot area - * @param height the height of the desired screenshot area - * @param alpha whether the alpha channel should be saved. If true, - * requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * @throws IOException if an I/O error occurred while writing the - * file - */ - public static void writeToTargaFile(File file, - int x, - int y, - int width, - int height, - boolean alpha) throws GLException, IOException { - if (alpha) { - checkExtABGR(); - } - - TGAWriter writer = new TGAWriter(); - writer.open(file, width, height, alpha); - ByteBuffer bgr = writer.getImageData(); - - GL2 gl = GLUgl2.getCurrentGL2(); - - // Set up pixel storage modes - GLPixelStorageModes psm = new GLPixelStorageModes(); - psm.setPackAlignment(gl, 1); - - int readbackType = (alpha ? GL2.GL_ABGR_EXT : GL2.GL_BGR); - - // read the BGR values into the image buffer - gl.glReadPixels(x, y, width, height, readbackType, - GL2.GL_UNSIGNED_BYTE, bgr); - - // Restore pixel storage modes - psm.restore(gl); - - // close the file - writer.close(); - } - - /** - * Takes a screenshot of the current OpenGL drawable to a - * BufferedImage. Requires the OpenGL context for the desired - * drawable to be current. Takes the screenshot from the last - * assigned read buffer, or the OpenGL default read buffer if none - * has been specified by the user (GL_FRONT for single-buffered - * configurations and GL_BACK for double-buffered configurations). - * Note that the scanlines of the resulting image are flipped - * vertically in order to correctly match the OpenGL contents, which - * takes time and is therefore not as fast as the Targa screenshot - * function. <P> - * - * No alpha channel is read back with this variant. - * - * @param width the width of the current drawable - * @param height the height of the current drawable - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - */ - public static BufferedImage readToBufferedImage(int width, - int height) throws GLException { - return readToBufferedImage(width, height, false); - } - - /** - * Takes a screenshot of the current OpenGL drawable to a - * BufferedImage. Requires the OpenGL context for the desired - * drawable to be current. Takes the screenshot from the last - * assigned read buffer, or the OpenGL default read buffer if none - * has been specified by the user (GL_FRONT for single-buffered - * configurations and GL_BACK for double-buffered configurations). - * Note that the scanlines of the resulting image are flipped - * vertically in order to correctly match the OpenGL contents, which - * takes time and is therefore not as fast as the Targa screenshot - * function. - * - * @param width the width of the current drawable - * @param height the height of the current drawable - * @param alpha whether the alpha channel should be read back. If - * true, requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - */ - public static BufferedImage readToBufferedImage(int width, - int height, - boolean alpha) throws GLException { - return readToBufferedImage(0, 0, width, height, alpha); - } - - /** - * Takes a screenshot of the current OpenGL drawable to a - * BufferedImage. Requires the OpenGL context for the desired - * drawable to be current. Takes the screenshot from the last - * assigned read buffer, or the OpenGL default read buffer if none - * has been specified by the user (GL_FRONT for single-buffered - * configurations and GL_BACK for double-buffered configurations). - * Note that the scanlines of the resulting image are flipped - * vertically in order to correctly match the OpenGL contents, which - * takes time and is therefore not as fast as the Targa screenshot - * function. - * - * @param x the starting x coordinate of the screenshot, measured from the lower-left - * @param y the starting y coordinate of the screenshot, measured from the lower-left - * @param width the width of the desired screenshot area - * @param height the height of the desired screenshot area - * @param alpha whether the alpha channel should be read back. If - * true, requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - */ - public static BufferedImage readToBufferedImage(int x, - int y, - int width, - int height, - boolean alpha) throws GLException { - int bufImgType = (alpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR); - int readbackType = (alpha ? GL2.GL_ABGR_EXT : GL2.GL_BGR); - - if (alpha) { - checkExtABGR(); - } - - // Allocate necessary storage - BufferedImage image = new BufferedImage(width, height, bufImgType); - - GL2 gl = GLUgl2.getCurrentGL2(); - - // Set up pixel storage modes - GLPixelStorageModes psm = new GLPixelStorageModes(); - psm.setPackAlignment(gl, 1); - - // read the BGR values into the image - gl.glReadPixels(x, y, width, height, readbackType, - GL2.GL_UNSIGNED_BYTE, - ByteBuffer.wrap(((DataBufferByte) image.getRaster().getDataBuffer()).getData())); - - // Restore pixel storage modes - psm.restore(gl); - - // Must flip BufferedImage vertically for correct results - ImageUtil.flipImageVertically(image); - return image; - } - - /** - * Takes a screenshot of the current OpenGL drawable to the - * specified file on disk using the ImageIO package. Requires the - * OpenGL context for the desired drawable to be current. Takes the - * screenshot from the last assigned read buffer, or the OpenGL - * default read buffer if none has been specified by the user - * (GL_FRONT for single-buffered configurations and GL_BACK for - * double-buffered configurations). This is not the fastest - * mechanism for taking a screenshot but may be more convenient than - * others for getting images for consumption by other packages. The - * file format is inferred from the suffix of the given file. <P> - * - * No alpha channel is saved with this variant. - * - * @param file the file to write containing the screenshot - * @param width the width of the current drawable - * @param height the height of the current drawable - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * - * @throws IOException if an I/O error occurred or if the file could - * not be written to disk due to the requested file format being - * unsupported by ImageIO - */ - public static void writeToFile(File file, - int width, - int height) throws IOException, GLException { - writeToFile(file, width, height, false); - } - - /** - * Takes a screenshot of the current OpenGL drawable to the - * specified file on disk using the ImageIO package. Requires the - * OpenGL context for the desired drawable to be current. Takes the - * screenshot from the last assigned read buffer, or the OpenGL - * default read buffer if none has been specified by the user - * (GL_FRONT for single-buffered configurations and GL_BACK for - * double-buffered configurations). This is not the fastest - * mechanism for taking a screenshot but may be more convenient than - * others for getting images for consumption by other packages. The - * file format is inferred from the suffix of the given file. <P> - * - * Note that some file formats, in particular JPEG, can not handle - * an alpha channel properly. If the "alpha" argument is specified - * as true for such a file format it will be silently ignored. - * - * @param file the file to write containing the screenshot - * @param width the width of the current drawable - * @param height the height of the current drawable - * @param alpha whether an alpha channel should be saved. If true, - * requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * - * @throws IOException if an I/O error occurred or if the file could - * not be written to disk due to the requested file format being - * unsupported by ImageIO - */ - public static void writeToFile(File file, - int width, - int height, - boolean alpha) throws IOException, GLException { - writeToFile(file, 0, 0, width, height, alpha); - } - - /** - * Takes a screenshot of the current OpenGL drawable to the - * specified file on disk using the ImageIO package. Requires the - * OpenGL context for the desired drawable to be current. Takes the - * screenshot from the last assigned read buffer, or the OpenGL - * default read buffer if none has been specified by the user - * (GL_FRONT for single-buffered configurations and GL_BACK for - * double-buffered configurations). This is not the fastest - * mechanism for taking a screenshot but may be more convenient than - * others for getting images for consumption by other packages. The - * file format is inferred from the suffix of the given file. <P> - * - * Note that some file formats, in particular JPEG, can not handle - * an alpha channel properly. If the "alpha" argument is specified - * as true for such a file format it will be silently ignored. - * - * @param file the file to write containing the screenshot - * @param x the starting x coordinate of the screenshot, measured from the lower-left - * @param y the starting y coordinate of the screenshot, measured from the lower-left - * @param width the width of the current drawable - * @param height the height of the current drawable - * @param alpha whether an alpha channel should be saved. If true, - * requires GL_EXT_abgr extension to be present. - * - * @throws GLException if an OpenGL context was not current or - * another OpenGL-related error occurred - * - * @throws IOException if an I/O error occurred or if the file could - * not be written to disk due to the requested file format being - * unsupported by ImageIO - */ - public static void writeToFile(File file, - int x, - int y, - int width, - int height, - boolean alpha) throws IOException, GLException { - String fileSuffix = IOUtil.getFileSuffix(file); - if (alpha && (fileSuffix.equals("jpg") || fileSuffix.equals("jpeg"))) { - // JPEGs can't deal properly with alpha channels - alpha = false; - } - - BufferedImage image = readToBufferedImage(x, y, width, height, alpha); - if (!ImageIO.write(image, fileSuffix, file)) { - throw new IOException("Unsupported file format " + fileSuffix); - } - } - - private static void checkExtABGR() { - GL2 gl = GLUgl2.getCurrentGL2(); - if (!gl.isExtensionAvailable(GLExtensions.EXT_abgr)) { - throw new IllegalArgumentException("Saving alpha channel requires GL_EXT_abgr"); - } - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java index 1735fcddd..fc0861eaa 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java @@ -40,7 +40,7 @@ package com.jogamp.opengl.util.awt; import com.jogamp.common.nio.Buffers; - +import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.*; import com.jogamp.opengl.util.packrect.*; @@ -60,14 +60,12 @@ import java.awt.RenderingHints; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; - import java.nio.*; - import java.text.*; - import java.util.*; import javax.media.opengl.*; +import javax.media.opengl.fixedfunc.GLPointerFunc; import javax.media.opengl.glu.*; import javax.media.opengl.awt.*; @@ -128,7 +126,12 @@ import jogamp.opengl.Debug; @author Kenneth Russell */ public class TextRenderer { - private static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.TextRenderer", true); + private static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = PropertyAccess.isPropertyDefined("jogl.debug.TextRenderer", true); + } // These are occasionally useful for more in-depth debugging private static final boolean DISABLE_GLYPH_CACHE = false; @@ -154,20 +157,20 @@ public class TextRenderer { static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4; static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4; static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4; - private Font font; - private boolean antialiased; - private boolean useFractionalMetrics; + private final Font font; + private final boolean antialiased; + private final boolean useFractionalMetrics; // Whether we're attempting to use automatic mipmap generation support private boolean mipmap; private RectanglePacker packer; private boolean haveMaxSize; - private RenderDelegate renderDelegate; + private final RenderDelegate renderDelegate; private TextureRenderer cachedBackingStore; private Graphics2D cachedGraphics; private FontRenderContext cachedFontRenderContext; - private Map /*<String,Rect>*/ stringLocations = new HashMap /*<String,Rect>*/(); - private GlyphProducer mGlyphProducer; + private final Map<String, Rect> stringLocations = new HashMap<String, Rect>(); + private final GlyphProducer mGlyphProducer; private int numRenderCycles; @@ -195,10 +198,10 @@ public class TextRenderer { // Debugging purposes only private boolean debugged; Pipelined_QuadRenderer mPipelinedQuadRenderer; - + //emzic: added boolean flag private boolean useVertexArrays = true; - + //emzic: added boolean flag private boolean isExtensionAvailable_GL_VERSION_1_5; private boolean checkFor_isExtensionAvailable_GL_VERSION_1_5; @@ -213,7 +216,7 @@ public class TextRenderer { @param font the font to render with */ - public TextRenderer(Font font) { + public TextRenderer(final Font font) { this(font, false, false, null, false); } @@ -227,7 +230,7 @@ public class TextRenderer { @param font the font to render with @param mipmap whether to attempt use of automatic mipmap generation */ - public TextRenderer(Font font, boolean mipmap) { + public TextRenderer(final Font font, final boolean mipmap) { this(font, false, false, null, mipmap); } @@ -244,8 +247,8 @@ public class TextRenderer { @param useFractionalMetrics whether to use fractional font metrics at the Java 2D level */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics) { + public TextRenderer(final Font font, final boolean antialiased, + final boolean useFractionalMetrics) { this(font, antialiased, useFractionalMetrics, null, false); } @@ -263,8 +266,8 @@ public class TextRenderer { @param renderDelegate the render delegate to use to draw the text's bitmap, or null to use the default one */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate) { + public TextRenderer(final Font font, final boolean antialiased, + final boolean useFractionalMetrics, final RenderDelegate renderDelegate) { this(font, antialiased, useFractionalMetrics, renderDelegate, false); } @@ -285,9 +288,9 @@ public class TextRenderer { text's bitmap, or null to use the default one @param mipmap whether to attempt use of automatic mipmap generation */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate, - boolean mipmap) { + public TextRenderer(final Font font, final boolean antialiased, + final boolean useFractionalMetrics, RenderDelegate renderDelegate, + final boolean mipmap) { this.font = font; this.antialiased = antialiased; this.useFractionalMetrics = useFractionalMetrics; @@ -309,7 +312,7 @@ public class TextRenderer { /** Returns the bounding rectangle of the given String, assuming it was rendered at the origin. See {@link #getBounds(CharSequence) getBounds(CharSequence)}. */ - public Rectangle2D getBounds(String str) { + public Rectangle2D getBounds(final String str) { return getBounds((CharSequence) str); } @@ -328,12 +331,12 @@ public class TextRenderer { {@link java.awt.font.GlyphVector#getPixelBounds getPixelBounds}, etc.) the returned bounds correspond to, although every effort is made to ensure an accurate bound. */ - public Rectangle2D getBounds(CharSequence str) { + public Rectangle2D getBounds(final CharSequence str) { // FIXME: this should be more optimized and use the glyph cache - Rect r = null; + final Rect r = stringLocations.get(str); - if ((r = (Rect) stringLocations.get(str)) != null) { - TextData data = (TextData) r.getUserData(); + if (r != null) { + final TextData data = (TextData) r.getUserData(); // Reconstitute the Java 2D results based on the cached values return new Rectangle2D.Double(-data.origin().x, -data.origin().y, @@ -380,7 +383,7 @@ public class TextRenderer { @param height the height of the current on-screen OpenGL drawable @throws javax.media.opengl.GLException If an OpenGL context is not current when this method is called */ - public void beginRendering(int width, int height) throws GLException { + public void beginRendering(final int width, final int height) throws GLException { beginRendering(width, height, true); } @@ -400,7 +403,7 @@ public class TextRenderer { @param disableDepthTest whether to disable the depth test @throws GLException If an OpenGL context is not current when this method is called */ - public void beginRendering(int width, int height, boolean disableDepthTest) + public void beginRendering(final int width, final int height, final boolean disableDepthTest) throws GLException { beginRendering(true, width, height, disableDepthTest); } @@ -427,8 +430,8 @@ public class TextRenderer { @param color the new color to use for rendering text @throws GLException If an OpenGL context is not current when this method is called */ - public void setColor(Color color) throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) && + public void setColor(final Color color) throws GLException { + final boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) && color.equals(cachedColor)); if (!noNeedForFlush) { @@ -455,9 +458,9 @@ public class TextRenderer { transparent, 1.0f = completely opaque @throws GLException If an OpenGL context is not current when this method is called */ - public void setColor(float r, float g, float b, float a) + public void setColor(final float r, final float g, final float b, final float a) throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) && + final boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) && (r == cachedR) && (g == cachedG) && (b == cachedB) && (a == cachedA)); @@ -485,14 +488,14 @@ public class TextRenderer { @param y the y coordinate at which to draw @throws GLException If an OpenGL context is not current when this method is called */ - public void draw(CharSequence str, int x, int y) throws GLException { + public void draw(final CharSequence str, final int x, final int y) throws GLException { draw3D(str, x, y, 0, 1); } /** Draws the supplied String at the desired location using the renderer's current color. See {@link #draw(CharSequence, int, int) draw(CharSequence, int, int)}. */ - public void draw(String str, int x, int y) throws GLException { + public void draw(final String str, final int x, final int y) throws GLException { draw3D(str, x, y, 0, 1); } @@ -508,8 +511,8 @@ public class TextRenderer { @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle @throws GLException If an OpenGL context is not current when this method is called */ - public void draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { + public void draw3D(final CharSequence str, final float x, final float y, final float z, + final float scaleFactor) { internal_draw3D(str, x, y, z, scaleFactor); } @@ -517,12 +520,12 @@ public class TextRenderer { renderer's current color. See {@link #draw3D(CharSequence, float, float, float, float) draw3D(CharSequence, float, float, float, float)}. */ - public void draw3D(String str, float x, float y, float z, float scaleFactor) { + public void draw3D(final String str, final float x, final float y, final float z, final float scaleFactor) { internal_draw3D(str, x, y, z, scaleFactor); } /** Returns the pixel width of the given character. */ - public float getCharWidth(char inChar) { + public float getCharWidth(final char inChar) { return mGlyphProducer.getGlyphPixelWidth(inChar); } @@ -577,20 +580,20 @@ public class TextRenderer { // Internals only below this point // - private static Rectangle2D preNormalize(Rectangle2D src) { + private static Rectangle2D preNormalize(final Rectangle2D src) { // Need to round to integer coordinates // Also give ourselves a little slop around the reported // bounds of glyphs because it looks like neither the visual // nor the pixel bounds works perfectly well - int minX = (int) Math.floor(src.getMinX()) - 1; - int minY = (int) Math.floor(src.getMinY()) - 1; - int maxX = (int) Math.ceil(src.getMaxX()) + 1; - int maxY = (int) Math.ceil(src.getMaxY()) + 1; + final int minX = (int) Math.floor(src.getMinX()) - 1; + final int minY = (int) Math.floor(src.getMinY()) - 1; + final int maxX = (int) Math.ceil(src.getMaxX()) + 1; + final int maxY = (int) Math.ceil(src.getMaxY()) + 1; return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); } - private Rectangle2D normalize(Rectangle2D src) { + private Rectangle2D normalize(final Rectangle2D src) { // Give ourselves a boundary around each entity on the backing // store in order to prevent bleeding of nearby Strings due to // the fact that we use linear filtering @@ -598,7 +601,7 @@ public class TextRenderer { // NOTE that this boundary is quite heuristic and is related // to how far away in 3D we may view the text -- // heuristically, 1.5% of the font's height - int boundary = (int) Math.max(1, 0.015 * font.getSize()); + final int boundary = (int) Math.max(1, 0.015 * font.getSize()); return new Rectangle2D.Double((int) Math.floor(src.getMinX() - boundary), (int) Math.floor(src.getMinY() - boundary), @@ -607,7 +610,7 @@ public class TextRenderer { } private TextureRenderer getBackingStore() { - TextureRenderer renderer = (TextureRenderer) packer.getBackingStore(); + final TextureRenderer renderer = (TextureRenderer) packer.getBackingStore(); if (renderer != cachedBackingStore) { // Backing store changed since last time; discard any cached Graphics2D @@ -624,7 +627,7 @@ public class TextRenderer { } private Graphics2D getGraphics2D() { - TextureRenderer renderer = getBackingStore(); + final TextureRenderer renderer = getBackingStore(); if (cachedGraphics == null) { cachedGraphics = renderer.createGraphics(); @@ -645,9 +648,9 @@ public class TextRenderer { return cachedGraphics; } - private void beginRendering(boolean ortho, int width, int height, - boolean disableDepthTestForOrtho) { - GL2 gl = GLContext.getCurrentGL().getGL2(); + private void beginRendering(final boolean ortho, final int width, final int height, + final boolean disableDepthTestForOrtho) { + final GL2 gl = GLContext.getCurrentGL().getGL2(); if (DEBUG && !debugged) { debug(gl); @@ -672,8 +675,8 @@ public class TextRenderer { if (!haveMaxSize) { // Query OpenGL for the maximum texture size and set it in the // RectanglePacker to keep it from expanding too large - int[] sz = new int[1]; - gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_SIZE, sz, 0); + final int[] sz = new int[1]; + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, sz, 0); packer.setMaxSize(sz[0], sz[0]); haveMaxSize = true; } @@ -702,16 +705,16 @@ public class TextRenderer { /** * emzic: here the call to glBindBuffer crashes on certain graphicscard/driver combinations * this is why the ugly try-catch block has been added, which falls back to the old textrenderer - * + * * @param ortho * @throws GLException */ - private void endRendering(boolean ortho) throws GLException { + private void endRendering(final boolean ortho) throws GLException { flushGlyphPipeline(); inBeginEndPair = false; - GL2 gl = GLContext.getCurrentGL().getGL2(); + final GL2 gl = GLContext.getCurrentGL().getGL2(); // Pop client attrib bits used by the pipelined quad renderer gl.glPopClientAttrib(); @@ -721,8 +724,8 @@ public class TextRenderer { // binding if (getUseVertexArrays() && is15Available(gl)) { try { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } catch (final Exception e) { isExtensionAvailable_GL_VERSION_1_5 = false; } } @@ -745,13 +748,14 @@ public class TextRenderer { } private void clearUnusedEntries() { - final java.util.List deadRects = new ArrayList /*<Rect>*/(); + final java.util.List<Rect> deadRects = new ArrayList<Rect>(); // Iterate through the contents of the backing store, removing // text strings that haven't been used recently packer.visit(new RectVisitor() { - public void visit(Rect rect) { - TextData data = (TextData) rect.getUserData(); + @Override + public void visit(final Rect rect) { + final TextData data = (TextData) rect.getUserData(); if (data.used()) { data.clearUsed(); @@ -761,12 +765,11 @@ public class TextRenderer { } }); - for (Iterator iter = deadRects.iterator(); iter.hasNext();) { - Rect r = (Rect) iter.next(); + for (final Rect r : deadRects) { packer.remove(r); stringLocations.remove(((TextData) r.getUserData()).string()); - int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID; + final int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID; if (unicodeToClearFromCache > 0) { mGlyphProducer.clearCacheEntry(unicodeToClearFromCache); @@ -781,7 +784,7 @@ public class TextRenderer { } // If we removed dead rectangles this cycle, try to do a compaction - float frag = packer.verticalFragmentationRatio(); + final float frag = packer.verticalFragmentationRatio(); if (!deadRects.isEmpty() && (frag > MAX_VERTICAL_FRAGMENTATION)) { if (DEBUG) { @@ -799,12 +802,10 @@ public class TextRenderer { } } - private void internal_draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { - List/*<Glyph>*/ glyphs = mGlyphProducer.getGlyphs(str); - for (Iterator iter = glyphs.iterator(); iter.hasNext(); ) { - Glyph glyph = (Glyph) iter.next(); - float advance = glyph.draw3D(x, y, z, scaleFactor); + private void internal_draw3D(final CharSequence str, float x, final float y, final float z, + final float scaleFactor) { + for (final Glyph glyph : mGlyphProducer.getGlyphs(str)) { + final float advance = glyph.draw3D(x, y, z, scaleFactor); x += advance * scaleFactor; } } @@ -815,8 +816,8 @@ public class TextRenderer { } } - private void draw3D_ROBUST(CharSequence str, float x, float y, float z, - float scaleFactor) { + private void draw3D_ROBUST(final CharSequence str, final float x, final float y, final float z, + final float scaleFactor) { String curStr; if (str instanceof String) { curStr = (String) str; @@ -825,14 +826,14 @@ public class TextRenderer { } // Look up the string on the backing store - Rect rect = (Rect) stringLocations.get(curStr); + Rect rect = stringLocations.get(curStr); if (rect == null) { // Rasterize this string and place it on the backing store Graphics2D g = getGraphics2D(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(curStr, font, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), + final Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(curStr, font, getFontRenderContext())); + final Rectangle2D bbox = normalize(origBBox); + final Point origin = new Point((int) -bbox.getMinX(), (int) -bbox.getMinY()); rect = new Rect(0, 0, (int) bbox.getWidth(), (int) bbox.getHeight(), @@ -847,8 +848,8 @@ public class TextRenderer { // OK, should now have an (x, y) for this rectangle; rasterize // the String - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; + final int strx = rect.x() + origin.x; + final int stry = rect.y() + origin.y; // Clear out the area we're going to draw into g.setComposite(AlphaComposite.Clear); @@ -859,7 +860,7 @@ public class TextRenderer { renderDelegate.draw(g, curStr, strx, stry); if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); + final TextData data = (TextData) rect.getUserData(); // Draw a bounding box on the backing store g.drawRect(strx - data.origOriginX(), stry - data.origOriginY(), @@ -877,16 +878,16 @@ public class TextRenderer { } // OK, now draw the portion of the backing store to the screen - TextureRenderer renderer = getBackingStore(); + final TextureRenderer renderer = getBackingStore(); // NOTE that the rectangles managed by the packer have their // origin at the upper-left but the TextureRenderer's origin is // at its lower left!!! - TextData data = (TextData) rect.getUserData(); + final TextData data = (TextData) rect.getUserData(); data.markUsed(); - Rectangle2D origRect = data.origRect(); - + final Rectangle2D origRect = data.origRect(); + // Align the leftmost point of the baseline to the (x, y, z) coordinate requested renderer.draw3DRect(x - (scaleFactor * data.origOriginX()), y - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())), z, @@ -899,21 +900,23 @@ public class TextRenderer { //---------------------------------------------------------------------- // Debugging functionality // - private void debug(GL gl) { + private void debug(final GL gl) { dbgFrame = new Frame("TextRenderer Debug Output"); - GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(gl.getGLProfile()), null, - GLContext.getCurrent(), null); + final GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(gl.getGLProfile())); + dbgCanvas.setSharedContext(GLContext.getCurrent()); dbgCanvas.addGLEventListener(new DebugListener(gl, dbgFrame)); dbgFrame.add(dbgCanvas); final FPSAnimator anim = new FPSAnimator(dbgCanvas, 10); dbgFrame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { + @Override + public void windowClosing(final WindowEvent e) { // Run this on another thread than the AWT event queue to // make sure the call to Animator.stop() completes before // exiting new Thread(new Runnable() { + @Override public void run() { anim.stop(); } @@ -994,22 +997,24 @@ public class TextRenderer { CharSequenceIterator() { } - CharSequenceIterator(CharSequence sequence) { + CharSequenceIterator(final CharSequence sequence) { initFromCharSequence(sequence); } - public void initFromCharSequence(CharSequence sequence) { + public void initFromCharSequence(final CharSequence sequence) { mSequence = sequence; mLength = mSequence.length(); mCurrentIndex = 0; } + @Override public char last() { mCurrentIndex = Math.max(0, mLength - 1); return current(); } + @Override public char current() { if ((mLength == 0) || (mCurrentIndex >= mLength)) { return CharacterIterator.DONE; @@ -1018,43 +1023,51 @@ public class TextRenderer { return mSequence.charAt(mCurrentIndex); } + @Override public char next() { mCurrentIndex++; return current(); } + @Override public char previous() { mCurrentIndex = Math.max(mCurrentIndex - 1, 0); return current(); } - public char setIndex(int position) { + @Override + public char setIndex(final int position) { mCurrentIndex = position; return current(); } + @Override public int getBeginIndex() { return 0; } + @Override public int getEndIndex() { return mLength; } + @Override public int getIndex() { return mCurrentIndex; } + @Override public Object clone() { - CharSequenceIterator iter = new CharSequenceIterator(mSequence); + final CharSequenceIterator iter = new CharSequenceIterator(mSequence); iter.mCurrentIndex = mCurrentIndex; return iter; } + @Override public char first() { if (mLength == 0) { return CharacterIterator.DONE; @@ -1070,7 +1083,7 @@ public class TextRenderer { static class TextData { // Back-pointer to String this TextData describes, if it // represents a String rather than a single glyph - private String str; + private final String str; // If this TextData represents a single glyph, this is its // unicode ID @@ -1081,7 +1094,7 @@ public class TextRenderer { // 2D coordinate system) at which the string must be rasterized in // order to fit within the rectangle -- the leftmost point of the // baseline. - private Point origin; + private final Point origin; // This represents the pre-normalized rectangle, which fits // within the rectangle on the backing store. We keep a @@ -1089,11 +1102,11 @@ public class TextRenderer { // prevent bleeding of adjacent letters when using GL_LINEAR // filtering for rendering. The origin of this rectangle is // equivalent to the origin above. - private Rectangle2D origRect; + private final Rectangle2D origRect; private boolean used; // Whether this text was used recently - TextData(String str, Point origin, Rectangle2D origRect, int unicodeID) { + TextData(final String str, final Point origin, final Rectangle2D origRect, final int unicodeID) { this.str = str; this.origin = origin; this.origRect = origRect; @@ -1138,7 +1151,8 @@ public class TextRenderer { class Manager implements BackingStoreManager { private Graphics2D g; - public Object allocateBackingStore(int w, int h) { + @Override + public Object allocateBackingStore(final int w, final int h) { // FIXME: should consider checking Font's attributes to see // whether we're likely to need to support a full RGBA backing // store (i.e., non-default Paint, foreground color, etc.), but @@ -1160,11 +1174,13 @@ public class TextRenderer { return renderer; } - public void deleteBackingStore(Object backingStore) { + @Override + public void deleteBackingStore(final Object backingStore) { ((TextureRenderer) backingStore).dispose(); } - public boolean preExpand(Rect cause, int attemptNumber) { + @Override + public boolean preExpand(final Rect cause, final int attemptNumber) { // Only try this one time; clear out potentially obsolete entries // NOTE: this heuristic and the fact that it clears the used bit // of all entries seems to cause cycling of entries in some @@ -1199,7 +1215,8 @@ public class TextRenderer { return false; } - public boolean additionFailed(Rect cause, int attemptNumber) { + @Override + public boolean additionFailed(final Rect cause, final int attemptNumber) { // Heavy hammer -- might consider doing something different packer.clear(); stringLocations.clear(); @@ -1217,17 +1234,19 @@ public class TextRenderer { return false; } + @Override public boolean canCompact() { return true; } - public void beginMovement(Object oldBackingStore, Object newBackingStore) { + @Override + public void beginMovement(final Object oldBackingStore, final Object newBackingStore) { // Exit the begin / end pair if necessary if (inBeginEndPair) { // Draw any outstanding glyphs flush(); - GL2 gl = GLContext.getCurrentGL().getGL2(); + final GL2 gl = GLContext.getCurrentGL().getGL2(); // Pop client attrib bits used by the pipelined quad renderer gl.glPopClientAttrib(); @@ -1237,8 +1256,8 @@ public class TextRenderer { // binding if (getUseVertexArrays() && is15Available(gl)) { try { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } catch (final Exception e) { isExtensionAvailable_GL_VERSION_1_5 = false; } } @@ -1250,14 +1269,15 @@ public class TextRenderer { } } - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; + final TextureRenderer newRenderer = (TextureRenderer) newBackingStore; g = newRenderer.createGraphics(); } - public void move(Object oldBackingStore, Rect oldLocation, - Object newBackingStore, Rect newLocation) { - TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore; - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; + @Override + public void move(final Object oldBackingStore, final Rect oldLocation, + final Object newBackingStore, final Rect newLocation) { + final TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore; + final TextureRenderer newRenderer = (TextureRenderer) newBackingStore; if (oldRenderer == newRenderer) { // Movement on the same backing store -- easy case @@ -1266,7 +1286,7 @@ public class TextRenderer { newLocation.y() - oldLocation.y()); } else { // Need to draw from the old renderer's image into the new one - Image img = oldRenderer.getImage(); + final Image img = oldRenderer.getImage(); g.drawImage(img, newLocation.x(), newLocation.y(), newLocation.x() + newLocation.w(), newLocation.y() + newLocation.h(), oldLocation.x(), @@ -1275,11 +1295,12 @@ public class TextRenderer { } } - public void endMovement(Object oldBackingStore, Object newBackingStore) { + @Override + public void endMovement(final Object oldBackingStore, final Object newBackingStore) { g.dispose(); // Sync the whole surface - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; + final TextureRenderer newRenderer = (TextureRenderer) newBackingStore; newRenderer.markDirty(0, 0, newRenderer.getWidth(), newRenderer.getHeight()); @@ -1293,7 +1314,7 @@ public class TextRenderer { } // Push client attrib bits used by the pipelined quad renderer - GL2 gl = GLContext.getCurrentGL().getGL2(); + final GL2 gl = GLContext.getCurrentGL().getGL2(); gl.glPushClientAttrib((int) GL2.GL_ALL_CLIENT_ATTRIB_BITS); if (haveCachedColor) { @@ -1311,32 +1332,38 @@ public class TextRenderer { } public static class DefaultRenderDelegate implements RenderDelegate { + @Override public boolean intensityOnly() { return true; } - public Rectangle2D getBounds(CharSequence str, Font font, - FontRenderContext frc) { + @Override + public Rectangle2D getBounds(final CharSequence str, final Font font, + final FontRenderContext frc) { return getBounds(font.createGlyphVector(frc, new CharSequenceIterator(str)), frc); } - public Rectangle2D getBounds(String str, Font font, - FontRenderContext frc) { + @Override + public Rectangle2D getBounds(final String str, final Font font, + final FontRenderContext frc) { return getBounds(font.createGlyphVector(frc, str), frc); } - public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { + @Override + public Rectangle2D getBounds(final GlyphVector gv, final FontRenderContext frc) { return gv.getVisualBounds(); } - public void drawGlyphVector(Graphics2D graphics, GlyphVector str, - int x, int y) { + @Override + public void drawGlyphVector(final Graphics2D graphics, final GlyphVector str, + final int x, final int y) { graphics.drawGlyphVector(str, x, y); } - public void draw(Graphics2D graphics, String str, int x, int y) { + @Override + public void draw(final Graphics2D graphics, final String str, final int x, final int y) { graphics.drawString(str, x, y); } } @@ -1346,7 +1373,7 @@ public class TextRenderer { // // A temporary to prevent excessive garbage creation - private char[] singleUnicode = new char[1]; + private final char[] singleUnicode = new char[1]; /** A Glyph represents either a single unicode glyph or a substring of characters to be drawn. The reason for the dual @@ -1386,11 +1413,11 @@ public class TextRenderer { private boolean needAdvance; // Creates a Glyph representing an individual Unicode character - public Glyph(int unicodeID, - int glyphCode, - float advance, - GlyphVector singleUnicodeGlyphVector, - GlyphProducer producer) { + public Glyph(final int unicodeID, + final int glyphCode, + final float advance, + final GlyphVector singleUnicodeGlyphVector, + final GlyphProducer producer) { this.unicodeID = unicodeID; this.glyphCode = glyphCode; this.advance = advance; @@ -1401,7 +1428,7 @@ public class TextRenderer { // Creates a Glyph representing a sequence of characters, with // an indication of whether additional single glyphs are being // rendered after it - public Glyph(String str, boolean needAdvance) { + public Glyph(final String str, final boolean needAdvance) { this.str = str; this.needAdvance = needAdvance; } @@ -1422,14 +1449,14 @@ public class TextRenderer { } /** Draws this glyph and returns the (x) advance for this glyph */ - public float draw3D(float inX, float inY, float z, float scaleFactor) { + public float draw3D(final float inX, final float inY, final float z, final float scaleFactor) { if (str != null) { draw3D_ROBUST(str, inX, inY, z, scaleFactor); if (!needAdvance) { return 0; } // Compute and return the advance for this string - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str); + final GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str); float totalAdvance = 0; for (int i = 0; i < gv.getNumGlyphs(); i++) { totalAdvance += gv.getGlyphMetrics(i).getAdvance(); @@ -1447,32 +1474,32 @@ public class TextRenderer { mPipelinedQuadRenderer = new Pipelined_QuadRenderer(); } - TextureRenderer renderer = getBackingStore(); + final TextureRenderer renderer = getBackingStore(); // Handles case where NPOT texture is used for backing store - TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords(); - float xScale = wholeImageTexCoords.right(); - float yScale = wholeImageTexCoords.bottom(); + final TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords(); + final float xScale = wholeImageTexCoords.right(); + final float yScale = wholeImageTexCoords.bottom(); - Rect rect = glyphRectForTextureMapping; - TextData data = (TextData) rect.getUserData(); + final Rect rect = glyphRectForTextureMapping; + final TextData data = (TextData) rect.getUserData(); data.markUsed(); - Rectangle2D origRect = data.origRect(); + final Rectangle2D origRect = data.origRect(); - float x = inX - (scaleFactor * data.origOriginX()); - float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())); + final float x = inX - (scaleFactor * data.origOriginX()); + final float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())); - int texturex = rect.x() + (data.origin().x - data.origOriginX()); - int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() - + final int texturex = rect.x() + (data.origin().x - data.origOriginX()); + final int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() - (data.origin().y - data.origOriginY()); - int width = (int) origRect.getWidth(); - int height = (int) origRect.getHeight(); + final int width = (int) origRect.getWidth(); + final int height = (int) origRect.getHeight(); - float tx1 = xScale * (float) texturex / (float) renderer.getWidth(); - float ty1 = yScale * (1.0f - + final float tx1 = xScale * texturex / renderer.getWidth(); + final float ty1 = yScale * (1.0f - ((float) texturey / (float) renderer.getHeight())); - float tx2 = xScale * (float) (texturex + width) / (float) renderer.getWidth(); - float ty2 = yScale * (1.0f - + final float tx2 = xScale * (texturex + width) / renderer.getWidth(); + final float ty2 = yScale * (1.0f - ((float) (texturey + height) / (float) renderer.getHeight())); mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1); @@ -1486,7 +1513,7 @@ public class TextRenderer { mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2); mPipelinedQuadRenderer.glVertex3f(x, y + (height * scaleFactor), z); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } return advance; @@ -1498,21 +1525,21 @@ public class TextRenderer { } private void upload() { - GlyphVector gv = getGlyphVector(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), + final GlyphVector gv = getGlyphVector(); + final Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext())); + final Rectangle2D bbox = normalize(origBBox); + final Point origin = new Point((int) -bbox.getMinX(), (int) -bbox.getMinY()); - Rect rect = new Rect(0, 0, (int) bbox.getWidth(), + final Rect rect = new Rect(0, 0, (int) bbox.getWidth(), (int) bbox.getHeight(), new TextData(null, origin, origBBox, unicodeID)); packer.add(rect); glyphRectForTextureMapping = rect; - Graphics2D g = getGraphics2D(); + final Graphics2D g = getGraphics2D(); // OK, should now have an (x, y) for this rectangle; rasterize // the glyph - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; + final int strx = rect.x() + origin.x; + final int stry = rect.y() + origin.y; // Clear out the area we're going to draw into g.setComposite(AlphaComposite.Clear); @@ -1523,7 +1550,7 @@ public class TextRenderer { renderDelegate.drawGlyphVector(g, gv, strx, stry); if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); + final TextData data = (TextData) rect.getUserData(); // Draw a bounding box on the backing store g.drawRect(strx - data.origOriginX(), stry - data.origOriginY(), @@ -1543,7 +1570,7 @@ public class TextRenderer { } private GlyphVector getGlyphVector() { - GlyphVector gv = singleUnicodeGlyphVector; + final GlyphVector gv = singleUnicodeGlyphVector; if (gv != null) { singleUnicodeGlyphVector = null; // Don't need this anymore return gv; @@ -1554,11 +1581,11 @@ public class TextRenderer { } class GlyphProducer { - final int undefined = -2; - FontRenderContext fontRenderContext; - List/*<Glyph>*/ glyphsOutput = new ArrayList/*<Glyph>*/(); - HashMap/*<String, GlyphVector>*/fullGlyphVectorCache = new HashMap/*<String, GlyphVector>*/(); - HashMap/*<Character, GlyphMetrics>*/glyphMetricsCache = new HashMap/*<Character, GlyphMetrics>*/(); + static final int undefined = -2; + final FontRenderContext fontRenderContext = null; // FIXME: Never initialized! + List<Glyph> glyphsOutput = new ArrayList<Glyph>(); + HashMap<String, GlyphVector> fullGlyphVectorCache = new HashMap<String, GlyphVector>(); + HashMap<Character, GlyphMetrics> glyphMetricsCache = new HashMap<Character, GlyphMetrics>(); // The mapping from unicode character to font-specific glyph ID int[] unicodes2Glyphs; // The mapping from glyph ID to Glyph @@ -1566,45 +1593,45 @@ public class TextRenderer { // We re-use this for each incoming string CharSequenceIterator iter = new CharSequenceIterator(); - GlyphProducer(int fontLengthInGlyphs) { + GlyphProducer(final int fontLengthInGlyphs) { unicodes2Glyphs = new int[512]; glyphCache = new Glyph[fontLengthInGlyphs]; clearAllCacheEntries(); } - public List/*<Glyph>*/ getGlyphs(CharSequence inString) { + public List<Glyph> getGlyphs(final CharSequence inString) { glyphsOutput.clear(); GlyphVector fullRunGlyphVector; - fullRunGlyphVector = (GlyphVector) fullGlyphVectorCache.get(inString.toString()); + fullRunGlyphVector = fullGlyphVectorCache.get(inString.toString()); if (fullRunGlyphVector == null) { iter.initFromCharSequence(inString); fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), iter); fullGlyphVectorCache.put(inString.toString(), fullRunGlyphVector); } - boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0); + final boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0); if (complex || DISABLE_GLYPH_CACHE) { // Punt to the robust version of the renderer glyphsOutput.add(new Glyph(inString.toString(), false)); return glyphsOutput; } - int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs(); + final int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs(); int i = 0; while (i < lengthInGlyphs) { - Character letter = CharacterCache.valueOf(inString.charAt(i)); - GlyphMetrics metrics = (GlyphMetrics) glyphMetricsCache.get(letter); + final Character letter = CharacterCache.valueOf(inString.charAt(i)); + GlyphMetrics metrics = glyphMetricsCache.get(letter); if (metrics == null) { metrics = fullRunGlyphVector.getGlyphMetrics(i); glyphMetricsCache.put(letter, metrics); } - Glyph glyph = getGlyph(inString, metrics, i); + final Glyph glyph = getGlyph(inString, metrics, i); if (glyph != null) { glyphsOutput.add(glyph); i++; } else { // Assemble a run of characters that don't fit in // the cache - StringBuilder buf = new StringBuilder(); + final StringBuilder buf = new StringBuilder(); while (i < lengthInGlyphs && getGlyph(inString, fullRunGlyphVector.getGlyphMetrics(i), i) == null) { buf.append(inString.charAt(i++)); @@ -1617,10 +1644,10 @@ public class TextRenderer { return glyphsOutput; } - public void clearCacheEntry(int unicodeID) { - int glyphID = unicodes2Glyphs[unicodeID]; + public void clearCacheEntry(final int unicodeID) { + final int glyphID = unicodes2Glyphs[unicodeID]; if (glyphID != undefined) { - Glyph glyph = glyphCache[glyphID]; + final Glyph glyph = glyphCache[glyphID]; if (glyph != null) { glyph.clear(); } @@ -1635,20 +1662,23 @@ public class TextRenderer { } } - public void register(Glyph glyph) { + public void register(final Glyph glyph) { unicodes2Glyphs[glyph.getUnicodeID()] = glyph.getGlyphCode(); glyphCache[glyph.getGlyphCode()] = glyph; } - public float getGlyphPixelWidth(char unicodeID) { - Glyph glyph = getGlyph(unicodeID); + public float getGlyphPixelWidth(final char unicodeID) { + final Glyph glyph = getGlyph(unicodeID); if (glyph != null) { return glyph.getAdvance(); } // Have to do this the hard / uncached way singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(fontRenderContext, + if( null == fontRenderContext ) { // FIXME: Never initialized! + throw new InternalError("fontRenderContext never initialized!"); + } + final GlyphVector gv = font.createGlyphVector(fontRenderContext, singleUnicode); return gv.getGlyphMetrics(0).getAdvance(); } @@ -1656,52 +1686,52 @@ public class TextRenderer { // Returns a glyph object for this single glyph. Returns null // if the unicode or glyph ID would be out of bounds of the // glyph cache. - private Glyph getGlyph(CharSequence inString, - GlyphMetrics glyphMetrics, - int index) { - char unicodeID = inString.charAt(index); + private Glyph getGlyph(final CharSequence inString, + final GlyphMetrics glyphMetrics, + final int index) { + final char unicodeID = inString.charAt(index); if (unicodeID >= unicodes2Glyphs.length) { return null; } - int glyphID = unicodes2Glyphs[unicodeID]; + final int glyphID = unicodes2Glyphs[unicodeID]; if (glyphID != undefined) { return glyphCache[glyphID]; } // Must fabricate the glyph singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); + final GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); return getGlyph(unicodeID, gv, glyphMetrics); } // It's unclear whether this variant might produce less // optimal results than if we can see the entire GlyphVector // for the incoming string - private Glyph getGlyph(int unicodeID) { + private Glyph getGlyph(final int unicodeID) { if (unicodeID >= unicodes2Glyphs.length) { return null; } - int glyphID = unicodes2Glyphs[unicodeID]; + final int glyphID = unicodes2Glyphs[unicodeID]; if (glyphID != undefined) { return glyphCache[glyphID]; } singleUnicode[0] = (char) unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); + final GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); return getGlyph(unicodeID, gv, gv.getGlyphMetrics(0)); } - private Glyph getGlyph(int unicodeID, - GlyphVector singleUnicodeGlyphVector, - GlyphMetrics metrics) { - int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0); + private Glyph getGlyph(final int unicodeID, + final GlyphVector singleUnicodeGlyphVector, + final GlyphMetrics metrics) { + final int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0); // Have seen huge glyph codes (65536) coming out of some fonts in some Unicode situations if (glyphCode >= glyphCache.length) { return null; } - Glyph glyph = new Glyph(unicodeID, + final Glyph glyph = new Glyph(unicodeID, glyphCode, metrics.getAdvance(), singleUnicodeGlyphVector, @@ -1710,7 +1740,7 @@ public class TextRenderer { return glyph; } } - + private static class CharacterCache { private CharacterCache() { } @@ -1719,15 +1749,15 @@ public class TextRenderer { static { for (int i = 0; i < cache.length; i++) { - cache[i] = new Character((char) i); + cache[i] = Character.valueOf((char) i); } } - public static Character valueOf(char c) { + public static Character valueOf(final char c) { if (c <= 127) { // must cache return CharacterCache.cache[c]; } - return new Character(c); + return Character.valueOf(c); } } @@ -1740,7 +1770,7 @@ public class TextRenderer { int mVBO_For_ResuableTileTexCoords; Pipelined_QuadRenderer() { - GL2 gl = GLContext.getCurrentGL().getGL2(); + final GL2 gl = GLContext.getCurrentGL().getGL2(); mVertCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsVerts); mTexCoords = Buffers.newDirectFloatBuffer(kTotalBufferSizeCoordsTex); @@ -1748,34 +1778,34 @@ public class TextRenderer { if (usingVBOs) { try { - int[] vbos = new int[2]; + final int[] vbos = new int[2]; gl.glGenBuffers(2, IntBuffer.wrap(vbos)); mVBO_For_ResuableTileVertices = vbos[0]; mVBO_For_ResuableTileTexCoords = vbos[1]; - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, mVBO_For_ResuableTileVertices); - gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesVerts, - null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline + gl.glBufferData(GL.GL_ARRAY_BUFFER, kTotalBufferSizeBytesVerts, + null, GL2ES2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, mVBO_For_ResuableTileTexCoords); - gl.glBufferData(GL2.GL_ARRAY_BUFFER, kTotalBufferSizeBytesTex, - null, GL2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - } catch (Exception e) { + gl.glBufferData(GL.GL_ARRAY_BUFFER, kTotalBufferSizeBytesTex, + null, GL2ES2.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline + } catch (final Exception e) { isExtensionAvailable_GL_VERSION_1_5 = false; usingVBOs = false; } } } - public void glTexCoord2f(float v, float v1) { + public void glTexCoord2f(final float v, final float v1) { mTexCoords.put(v); mTexCoords.put(v1); } - public void glVertex3f(float inX, float inY, float inZ) { + public void glVertex3f(final float inX, final float inY, final float inZ) { mVertCoords.put(inX); mVertCoords.put(inY); mVertCoords.put(inZ); @@ -1797,41 +1827,41 @@ public class TextRenderer { private void drawVertexArrays() { if (mOutstandingGlyphsVerticesPipeline > 0) { - GL2 gl = GLContext.getCurrentGL().getGL2(); + final GL2 gl = GLContext.getCurrentGL().getGL2(); - TextureRenderer renderer = getBackingStore(); - Texture texture = renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? + final TextureRenderer renderer = getBackingStore(); + renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? mVertCoords.rewind(); mTexCoords.rewind(); - gl.glEnableClientState(GL2.GL_VERTEX_ARRAY); + gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY); if (usingVBOs) { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, mVBO_For_ResuableTileVertices); - gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0, + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_VertexData, mVertCoords); // upload only the new stuff - gl.glVertexPointer(3, GL2.GL_FLOAT, 0, 0); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); } else { - gl.glVertexPointer(3, GL2.GL_FLOAT, 0, mVertCoords); + gl.glVertexPointer(3, GL.GL_FLOAT, 0, mVertCoords); } - gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY); + gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY); if (usingVBOs) { - gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, mVBO_For_ResuableTileTexCoords); - gl.glBufferSubData(GL2.GL_ARRAY_BUFFER, 0, + gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_TexData, mTexCoords); // upload only the new stuff - gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, 0); + gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0); } else { - gl.glTexCoordPointer(2, GL2.GL_FLOAT, 0, mTexCoords); + gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, mTexCoords); } - gl.glDrawArrays(GL2.GL_QUADS, 0, + gl.glDrawArrays(GL2GL3.GL_QUADS, 0, mOutstandingGlyphsVerticesPipeline); mVertCoords.rewind(); @@ -1842,14 +1872,14 @@ public class TextRenderer { private void drawIMMEDIATE() { if (mOutstandingGlyphsVerticesPipeline > 0) { - TextureRenderer renderer = getBackingStore(); - Texture texture = renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? + final TextureRenderer renderer = getBackingStore(); + renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? - GL2 gl = GLContext.getCurrentGL().getGL2(); - gl.glBegin(GL2.GL_QUADS); + final GL2 gl = GLContext.getCurrentGL().getGL2(); + gl.glBegin(GL2GL3.GL_QUADS); try { - int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4; + final int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4; mVertCoords.rewind(); mTexCoords.rewind(); @@ -1870,7 +1900,7 @@ public class TextRenderer { gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), mVertCoords.get()); } - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } finally { gl.glEnd(); @@ -1886,20 +1916,21 @@ public class TextRenderer { private GLU glu; private Frame frame; - DebugListener(GL gl, Frame frame) { + DebugListener(final GL gl, final Frame frame) { this.glu = GLU.createGLU(gl); this.frame = frame; } - public void display(GLAutoDrawable drawable) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - gl.glClear(GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_COLOR_BUFFER_BIT); + @Override + public void display(final GLAutoDrawable drawable) { + final GL2 gl = GLContext.getCurrentGL().getGL2(); + gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT); if (packer == null) { return; } - TextureRenderer rend = getBackingStore(); + final TextureRenderer rend = getBackingStore(); final int w = rend.getWidth(); final int h = rend.getHeight(); rend.beginOrthoRendering(w, h); @@ -1908,6 +1939,7 @@ public class TextRenderer { if ((frame.getWidth() != w) || (frame.getHeight() != h)) { EventQueue.invokeLater(new Runnable() { + @Override public void run() { frame.setSize(w, h); } @@ -1915,22 +1947,24 @@ public class TextRenderer { } } - public void dispose(GLAutoDrawable drawable) { - glu.destroy(); + @Override + public void dispose(final GLAutoDrawable drawable) { glu=null; frame=null; } // Unused methods - public void init(GLAutoDrawable drawable) { + @Override + public void init(final GLAutoDrawable drawable) { } - public void reshape(GLAutoDrawable drawable, int x, int y, int width, - int height) { + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, + final int height) { } - public void displayChanged(GLAutoDrawable drawable, - boolean modeChanged, boolean deviceChanged) { + public void displayChanged(final GLAutoDrawable drawable, + final boolean modeChanged, final boolean deviceChanged) { } } @@ -1941,7 +1975,7 @@ public class TextRenderer { * certain graphics cards which have poor vertex array * performance. Defaults to true. */ - public void setUseVertexArrays(boolean useVertexArrays) { + public void setUseVertexArrays(final boolean useVertexArrays) { this.useVertexArrays = useVertexArrays; } @@ -1960,7 +1994,7 @@ public class TextRenderer { * graphics cards do not behave well when this is enabled, * resulting in fuzzy text. Defaults to true. */ - public void setSmoothing(boolean smoothing) { + public void setSmoothing(final boolean smoothing) { this.smoothing = smoothing; getBackingStore().setSmoothing(smoothing); } @@ -1975,7 +2009,7 @@ public class TextRenderer { return smoothing; } - private final boolean is15Available(GL gl) { + private final boolean is15Available(final GL gl) { if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) { isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable(GLExtensions.VERSION_1_5); checkFor_isExtensionAvailable_GL_VERSION_1_5 = true; diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/TextureRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/awt/TextureRenderer.java index 922fc69c1..ef59f9fa0 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/TextureRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/TextureRenderer.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -48,7 +48,10 @@ import java.awt.Rectangle; import java.awt.image.*; import javax.media.opengl.*; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.fixedfunc.GLMatrixFunc; import javax.media.opengl.glu.gl2.*; + import com.jogamp.opengl.util.texture.*; import com.jogamp.opengl.util.texture.awt.*; @@ -70,10 +73,10 @@ public class TextureRenderer { // appropriate threads, which would be somewhat unfortunate. // Whether we have an alpha channel in the (RGB/A) backing store - private boolean alpha; + private final boolean alpha; // Whether we're using only a GL_INTENSITY backing store - private boolean intensity; + private final boolean intensity; // Whether we're attempting to use automatic mipmap generation support private boolean mipmap; @@ -91,7 +94,7 @@ public class TextureRenderer { private boolean mustReallocateTexture; private Rectangle dirtyRegion; - private GLUgl2 glu = new GLUgl2(); + private final GLUgl2 glu = new GLUgl2(); // Current color private float r = 1.0f; @@ -108,7 +111,7 @@ public class TextureRenderer { @param height the height of the texture to render into @param alpha whether to allocate an alpha channel for the texture */ - public TextureRenderer(int width, int height, boolean alpha) { + public TextureRenderer(final int width, final int height, final boolean alpha) { this(width, height, alpha, false); } @@ -123,13 +126,13 @@ public class TextureRenderer { @param alpha whether to allocate an alpha channel for the texture @param mipmap whether to attempt use of automatic mipmap generation */ - public TextureRenderer(int width, int height, boolean alpha, boolean mipmap) { + public TextureRenderer(final int width, final int height, final boolean alpha, final boolean mipmap) { this(width, height, alpha, false, mipmap); } // Internal constructor to avoid confusion since alpha only makes // sense when intensity is not set - private TextureRenderer(int width, int height, boolean alpha, boolean intensity, boolean mipmap) { + private TextureRenderer(final int width, final int height, final boolean alpha, final boolean intensity, final boolean mipmap) { this.alpha = alpha; this.intensity = intensity; this.mipmap = mipmap; @@ -140,7 +143,7 @@ public class TextureRenderer { which acts only as an alpha channel. No mipmap support is requested. Internally, this associates a GL_INTENSITY OpenGL texture with the backing store. */ - public static TextureRenderer createAlphaOnlyRenderer(int width, int height) { + public static TextureRenderer createAlphaOnlyRenderer(final int width, final int height) { return createAlphaOnlyRenderer(width, height, false); } @@ -150,7 +153,7 @@ public class TextureRenderer { better smoothing when rendering the TextureRenderer's contents at a distance. Internally, this associates a GL_INTENSITY OpenGL texture with the backing store. */ - public static TextureRenderer createAlphaOnlyRenderer(int width, int height, boolean mipmap) { + public static TextureRenderer createAlphaOnlyRenderer(final int width, final int height, final boolean mipmap) { return new TextureRenderer(width, height, false, true, mipmap); } @@ -205,7 +208,7 @@ public class TextureRenderer { @param height the new height of the backing store of this renderer @throws GLException If an OpenGL context is not current when this method is called */ - public void setSize(int width, int height) throws GLException { + public void setSize(final int width, final int height) throws GLException { init(width, height); } @@ -216,7 +219,7 @@ public class TextureRenderer { @param d the new size of the backing store of this renderer @throws GLException If an OpenGL context is not current when this method is called */ - public void setSize(Dimension d) throws GLException { + public void setSize(final Dimension d) throws GLException { setSize(d.width, d.height); } @@ -228,7 +231,7 @@ public class TextureRenderer { @param smoothing whether smoothing is enabled for the OpenGL texture */ - public void setSmoothing(boolean smoothing) { + public void setSmoothing(final boolean smoothing) { this.smoothing = smoothing; smoothingChanged = true; } @@ -275,8 +278,8 @@ public class TextureRenderer { @param width the width of the region to update @param height the height of the region to update */ - public void markDirty(int x, int y, int width, int height) { - Rectangle curRegion = new Rectangle(x, y, width, height); + public void markDirty(final int x, final int y, final int width, final int height) { + final Rectangle curRegion = new Rectangle(x, y, width, height); if (dirtyRegion == null) { dirtyRegion = curRegion; } else { @@ -333,7 +336,7 @@ public class TextureRenderer { @throws GLException If an OpenGL context is not current when this method is called */ - public void beginOrthoRendering(int width, int height) throws GLException { + public void beginOrthoRendering(final int width, final int height) throws GLException { beginOrthoRendering(width, height, true); } @@ -355,7 +358,7 @@ public class TextureRenderer { @throws GLException If an OpenGL context is not current when this method is called */ - public void beginOrthoRendering(int width, int height, boolean disableDepthTest) throws GLException { + public void beginOrthoRendering(final int width, final int height, final boolean disableDepthTest) throws GLException { beginRendering(true, width, height, disableDepthTest); } @@ -399,15 +402,15 @@ public class TextureRenderer { transparent, 1.0f = completely opaque @throws GLException If an OpenGL context is not current when this method is called */ - public void setColor(float r, float g, float b, float a) throws GLException { - GL2 gl = GLContext.getCurrentGL().getGL2(); + public void setColor(final float r, final float g, final float b, final float a) throws GLException { + final GL2 gl = GLContext.getCurrentGL().getGL2(); this.r = r * a; this.g = g * a; this.b = b * a; this.a = a; gl.glColor4f(this.r, this.g, this.b, this.a); - } + } private float[] compArray; /** Changes the current color of this TextureRenderer to the @@ -417,7 +420,7 @@ public class TextureRenderer { @param color the new color to use for rendering @throws GLException If an OpenGL context is not current when this method is called */ - public void setColor(Color color) throws GLException { + public void setColor(final Color color) throws GLException { // Get color's RGBA components as floats in the range [0,1]. if (compArray == null) { compArray = new float[4]; @@ -437,10 +440,10 @@ public class TextureRenderer { @param screenx the on-screen x coordinate at which to draw the rectangle @param screeny the on-screen y coordinate (relative to lower left) at which to draw the rectangle - + @throws GLException If an OpenGL context is not current when this method is called */ - public void drawOrthoRect(int screenx, int screeny) throws GLException { + public void drawOrthoRect(final int screenx, final int screeny) throws GLException { drawOrthoRect(screenx, screeny, 0, 0, getWidth(), getHeight()); } @@ -459,12 +462,12 @@ public class TextureRenderer { rectangle to draw @param width the width of the rectangle to draw @param height the height of the rectangle to draw - + @throws GLException If an OpenGL context is not current when this method is called */ - public void drawOrthoRect(int screenx, int screeny, - int texturex, int texturey, - int width, int height) throws GLException { + public void drawOrthoRect(final int screenx, final int screeny, + final int texturex, final int texturey, + final int width, final int height) throws GLException { draw3DRect(screenx, screeny, 0, texturex, texturey, width, height, 1); } @@ -490,19 +493,19 @@ public class TextureRenderer { @param height the height in texels of the rectangle to draw @param scaleFactor the scale factor to apply (multiplicatively) to the size of the drawn rectangle - + @throws GLException If an OpenGL context is not current when this method is called */ - public void draw3DRect(float x, float y, float z, - int texturex, int texturey, - int width, int height, - float scaleFactor) throws GLException { - GL2 gl = GLContext.getCurrentGL().getGL2(); - Texture texture = getTexture(); - TextureCoords coords = texture.getSubImageTexCoords(texturex, texturey, + public void draw3DRect(final float x, final float y, final float z, + final int texturex, final int texturey, + final int width, final int height, + final float scaleFactor) throws GLException { + final GL2 gl = GLContext.getCurrentGL().getGL2(); + final Texture texture = getTexture(); + final TextureCoords coords = texture.getSubImageTexCoords(texturex, texturey, texturex + width, texturey + height); - gl.glBegin(GL2.GL_QUADS); + gl.glBegin(GL2GL3.GL_QUADS); gl.glTexCoord2f(coords.left(), coords.bottom()); gl.glVertex3f(x, y, z); gl.glTexCoord2f(coords.right(), coords.bottom()); @@ -518,7 +521,7 @@ public class TextureRenderer { OpenGL texture to the screen, if the application intends to draw them as a flat overlay on to the screen. Must be used if {@link #beginOrthoRendering} is used to set up the rendering stage for - this overlay. + this overlay. @throws GLException If an OpenGL context is not current when this method is called */ @@ -550,70 +553,70 @@ public class TextureRenderer { // Internals only below this point // - private void beginRendering(boolean ortho, int width, int height, boolean disableDepthTestForOrtho) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - int attribBits = - GL2.GL_ENABLE_BIT | GL2.GL_TEXTURE_BIT | GL2.GL_COLOR_BUFFER_BIT | - (ortho ? (GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_TRANSFORM_BIT) : 0); + private void beginRendering(final boolean ortho, final int width, final int height, final boolean disableDepthTestForOrtho) { + final GL2 gl = GLContext.getCurrentGL().getGL2(); + final int attribBits = + GL2.GL_ENABLE_BIT | GL2.GL_TEXTURE_BIT | GL.GL_COLOR_BUFFER_BIT | + (ortho ? (GL.GL_DEPTH_BUFFER_BIT | GL2.GL_TRANSFORM_BIT) : 0); gl.glPushAttrib(attribBits); - gl.glDisable(GL2.GL_LIGHTING); + gl.glDisable(GLLightingFunc.GL_LIGHTING); if (ortho) { if (disableDepthTestForOrtho) { - gl.glDisable(GL2.GL_DEPTH_TEST); + gl.glDisable(GL.GL_DEPTH_TEST); } - gl.glDisable(GL2.GL_CULL_FACE); - gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glDisable(GL.GL_CULL_FACE); + gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl.glPushMatrix(); gl.glLoadIdentity(); glu.gluOrtho2D(0, width, 0, height); - gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); gl.glPushMatrix(); gl.glLoadIdentity(); - gl.glMatrixMode(GL2.GL_TEXTURE); + gl.glMatrixMode(GL.GL_TEXTURE); gl.glPushMatrix(); gl.glLoadIdentity(); } - gl.glEnable(GL2.GL_BLEND); - gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA); - Texture texture = getTexture(); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + final Texture texture = getTexture(); texture.enable(gl); texture.bind(gl); - gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE); + gl.glTexEnvi(GL2ES1.GL_TEXTURE_ENV, GL2ES1.GL_TEXTURE_ENV_MODE, GL2ES1.GL_MODULATE); // Change polygon color to last saved gl.glColor4f(r, g, b, a); if (smoothingChanged) { smoothingChanged = false; if (smoothing) { - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); if (mipmap) { - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR_MIPMAP_LINEAR); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR); } else { - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); } } else { - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST); - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); } } } - private void endRendering(boolean ortho) { - GL2 gl = GLContext.getCurrentGL().getGL2(); - Texture texture = getTexture(); + private void endRendering(final boolean ortho) { + final GL2 gl = GLContext.getCurrentGL().getGL2(); + final Texture texture = getTexture(); texture.disable(gl); if (ortho) { - gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl.glPopMatrix(); - gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); gl.glPopMatrix(); - gl.glMatrixMode(GL2.GL_TEXTURE); + gl.glMatrixMode(GL.GL_TEXTURE); gl.glPopMatrix(); } gl.glPopAttrib(); } - private void init(int width, int height) { - GL2 gl = GLContext.getCurrentGL().getGL2(); + private void init(final int width, final int height) { + final GL2 gl = GLContext.getCurrentGL().getGL2(); // Discard previous BufferedImage if any if (image != null) { image.flush(); @@ -621,8 +624,8 @@ public class TextureRenderer { } // Infer the internal format if not an intensity texture - int internalFormat = (intensity ? GL2.GL_INTENSITY : 0); - int imageType = + final int internalFormat = (intensity ? GL2.GL_INTENSITY : 0); + final int imageType = (intensity ? BufferedImage.TYPE_BYTE_GRAY : (alpha ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_RGB)); image = new BufferedImage(width, height, imageType); @@ -650,9 +653,9 @@ public class TextureRenderer { @throws GLException If an OpenGL context is not current when this method is called */ - private void sync(int x, int y, int width, int height) throws GLException { + private void sync(final int x, final int y, final int width, final int height) throws GLException { // Force allocation if necessary - boolean canSkipUpdate = ensureTexture(); + final boolean canSkipUpdate = ensureTexture(); if (!canSkipUpdate) { // Update specified region. @@ -667,7 +670,7 @@ public class TextureRenderer { // Returns true if the texture was newly allocated, false if not private boolean ensureTexture() { - GL gl = GLContext.getCurrentGL(); + final GL gl = GLContext.getCurrentGL(); if (mustReallocateTexture) { if (texture != null) { texture.destroy(gl); @@ -688,8 +691,8 @@ public class TextureRenderer { if (!smoothing) { // The TextureIO classes default to GL_LINEAR filtering - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST); - texture.setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + texture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); } return true; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapCharRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapCharRec.java index 34685e1b2..1f055211d 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapCharRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapCharRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,8 +41,8 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class BitmapCharRec { @@ -53,12 +53,12 @@ class BitmapCharRec { public float advance; public byte[] bitmap; - public BitmapCharRec(int width, - int height, - float xorig, - float yorig, - float advance, - byte[] bitmap) { + public BitmapCharRec(final int width, + final int height, + final float xorig, + final float yorig, + final float advance, + final byte[] bitmap) { this.width = width; this.height = height; this.xorig = xorig; diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapFontRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapFontRec.java index 18f7d3b28..7b8418704 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapFontRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/BitmapFontRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,8 +41,8 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class BitmapFontRec { @@ -51,10 +51,10 @@ class BitmapFontRec { public int first; public BitmapCharRec[] ch; - public BitmapFontRec(String name, - int num_chars, - int first, - BitmapCharRec[] ch) { + public BitmapFontRec(final String name, + final int num_chars, + final int first, + final BitmapCharRec[] ch) { this.name = name; this.num_chars = num_chars; this.first = first; diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/CoordRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/CoordRec.java index 9ad95ec03..d79f16638 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/CoordRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/CoordRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,15 +41,15 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class CoordRec { public float x; public float y; - public CoordRec(float x, float y) { + public CoordRec(final float x, final float y) { this.x = x; this.y = y; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUT.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUT.java index 010ce6699..92cbd66da 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUT.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUT.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -40,6 +40,7 @@ package com.jogamp.opengl.util.gl2; import javax.media.opengl.*; +import javax.media.opengl.fixedfunc.GLLightingFunc; import javax.media.opengl.glu.*; import javax.media.opengl.glu.gl2.*; @@ -103,13 +104,13 @@ public class GLUT { public static final int BITMAP_HELVETICA_12 = 7; public static final int BITMAP_HELVETICA_18 = 8; - private GLUgl2 glu = new GLUgl2(); + private final GLUgl2 glu = new GLUgl2(); //---------------------------------------------------------------------- // Shapes // - public void glutWireSphere(double radius, int slices, int stacks) { + public void glutWireSphere(final double radius, final int slices, final int stacks) { quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_LINE); glu.gluQuadricNormals(quadObj, GLU.GLU_SMOOTH); @@ -119,7 +120,7 @@ public class GLUT { glu.gluSphere(quadObj, radius, slices, stacks); } - public void glutSolidSphere(double radius, int slices, int stacks) { + public void glutSolidSphere(final double radius, final int slices, final int stacks) { quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_FILL); glu.gluQuadricNormals(quadObj, GLU.GLU_SMOOTH); @@ -129,8 +130,8 @@ public class GLUT { glu.gluSphere(quadObj, radius, slices, stacks); } - public void glutWireCone(double base, double height, - int slices, int stacks) { + public void glutWireCone(final double base, final double height, + final int slices, final int stacks) { quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_LINE); glu.gluQuadricNormals(quadObj, GLU.GLU_SMOOTH); @@ -140,8 +141,8 @@ public class GLUT { glu.gluCylinder(quadObj, base, 0.0, height, slices, stacks); } - public void glutSolidCone(double base, double height, - int slices, int stacks) { + public void glutSolidCone(final double base, final double height, + final int slices, final int stacks) { quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_FILL); glu.gluQuadricNormals(quadObj, GLU.GLU_SMOOTH); @@ -151,7 +152,7 @@ public class GLUT { glu.gluCylinder(quadObj, base, 0.0, height, slices, stacks); } - public void glutWireCylinder(double radius, double height, int slices, int stacks) { + public void glutWireCylinder(final double radius, final double height, final int slices, final int stacks) { quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_LINE); glu.gluQuadricNormals(quadObj, GLU.GLU_SMOOTH); @@ -161,22 +162,22 @@ public class GLUT { glu.gluCylinder(quadObj, radius, radius, height, slices, stacks); } - public void glutSolidCylinder(double radius, double height, int slices, int stacks) { - GL2 gl = GLUgl2.getCurrentGL2(); - + public void glutSolidCylinder(final double radius, final double height, final int slices, final int stacks) { + final GL2 gl = GLUgl2.getCurrentGL2(); + // Prepare table of points for drawing end caps - double [] x = new double[slices]; - double [] y = new double[slices]; - double angleDelta = Math.PI * 2 / slices; + final double [] x = new double[slices]; + final double [] y = new double[slices]; + final double angleDelta = Math.PI * 2 / slices; double angle = 0; for (int i = 0 ; i < slices ; i ++) { angle = i * angleDelta; x[i] = Math.cos(angle) * radius; y[i] = Math.sin(angle) * radius; } - + // Draw bottom cap - gl.glBegin(GL2.GL_TRIANGLE_FAN); + gl.glBegin(GL.GL_TRIANGLE_FAN); gl.glNormal3d(0,0,-1); gl.glVertex3d(0,0,0); for (int i = 0 ; i < slices ; i ++) { @@ -184,9 +185,9 @@ public class GLUT { } gl.glVertex3d(x[0], y[0], 0); gl.glEnd(); - + // Draw top cap - gl.glBegin(GL2.GL_TRIANGLE_FAN); + gl.glBegin(GL.GL_TRIANGLE_FAN); gl.glNormal3d(0,0,1); gl.glVertex3d(0,0,height); for (int i = 0 ; i < slices ; i ++) { @@ -194,7 +195,7 @@ public class GLUT { } gl.glVertex3d(x[0], y[0], height); gl.glEnd(); - + // Draw walls quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_FILL); @@ -205,68 +206,68 @@ public class GLUT { glu.gluCylinder(quadObj, radius, radius, height, slices, stacks); } - public void glutWireCube(float size) { - drawBox(GLUgl2.getCurrentGL2(), size, GL2.GL_LINE_LOOP); + public void glutWireCube(final float size) { + drawBox(GLUgl2.getCurrentGL2(), size, GL.GL_LINE_LOOP); } - public void glutSolidCube(float size) { - drawBox(GLUgl2.getCurrentGL2(), size, GL2.GL_QUADS); + public void glutSolidCube(final float size) { + drawBox(GLUgl2.getCurrentGL2(), size, GL2GL3.GL_QUADS); } - public void glutWireTorus(double innerRadius, double outerRadius, - int nsides, int rings) { - GL2 gl = GLUgl2.getCurrentGL2(); + public void glutWireTorus(final double innerRadius, final double outerRadius, + final int nsides, final int rings) { + final GL2 gl = GLUgl2.getCurrentGL2(); gl.glPushAttrib(GL2.GL_POLYGON_BIT); - gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, GL2.GL_LINE); + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); doughnut(gl, innerRadius, outerRadius, nsides, rings); gl.glPopAttrib(); } - public void glutSolidTorus(double innerRadius, double outerRadius, - int nsides, int rings) { + public void glutSolidTorus(final double innerRadius, final double outerRadius, + final int nsides, final int rings) { doughnut(GLUgl2.getCurrentGL2(), innerRadius, outerRadius, nsides, rings); } public void glutWireDodecahedron() { - dodecahedron(GLUgl2.getCurrentGL2(), GL2.GL_LINE_LOOP); + dodecahedron(GLUgl2.getCurrentGL2(), GL.GL_LINE_LOOP); } public void glutSolidDodecahedron() { - dodecahedron(GLUgl2.getCurrentGL2(), GL2.GL_TRIANGLE_FAN); + dodecahedron(GLUgl2.getCurrentGL2(), GL.GL_TRIANGLE_FAN); } public void glutWireOctahedron() { - octahedron(GLUgl2.getCurrentGL2(), GL2.GL_LINE_LOOP); + octahedron(GLUgl2.getCurrentGL2(), GL.GL_LINE_LOOP); } public void glutSolidOctahedron() { - octahedron(GLUgl2.getCurrentGL2(), GL2.GL_TRIANGLES); + octahedron(GLUgl2.getCurrentGL2(), GL.GL_TRIANGLES); } public void glutWireIcosahedron() { - icosahedron(GLUgl2.getCurrentGL2(), GL2.GL_LINE_LOOP); + icosahedron(GLUgl2.getCurrentGL2(), GL.GL_LINE_LOOP); } public void glutSolidIcosahedron() { - icosahedron(GLUgl2.getCurrentGL2(), GL2.GL_TRIANGLES); + icosahedron(GLUgl2.getCurrentGL2(), GL.GL_TRIANGLES); } public void glutWireTetrahedron() { - tetrahedron(GLUgl2.getCurrentGL2(), GL2.GL_LINE_LOOP); + tetrahedron(GLUgl2.getCurrentGL2(), GL.GL_LINE_LOOP); } public void glutSolidTetrahedron() { - tetrahedron(GLUgl2.getCurrentGL2(), GL2.GL_TRIANGLES); + tetrahedron(GLUgl2.getCurrentGL2(), GL.GL_TRIANGLES); } /** * Renders the teapot as a solid shape of the specified size. The teapot is * created in a way that replicates the C GLUT implementation. - * + * * @param scale * the factor by which to scale the teapot */ - public void glutSolidTeapot(double scale) { + public void glutSolidTeapot(final double scale) { glutSolidTeapot(scale, true); } @@ -278,28 +279,28 @@ public class GLUT { * instead of the y=-1 plane). Both surface normals and texture coordinates * for the teapot are generated. The teapot is generated with OpenGL * evaluators. - * + * * @param scale * the factor by which to scale the teapot * @param cStyle * whether to create the teapot in exactly the same way as in the C * implementation of GLUT */ - public void glutSolidTeapot(double scale, boolean cStyle) { - teapot(GLUgl2.getCurrentGL2(), 14, scale, GL2.GL_FILL, cStyle); + public void glutSolidTeapot(final double scale, final boolean cStyle) { + teapot(GLUgl2.getCurrentGL2(), 14, scale, GL2GL3.GL_FILL, cStyle); } /** * Renders the teapot as a wireframe shape of the specified size. The teapot * is created in a way that replicates the C GLUT implementation. - * + * * @param scale * the factor by which to scale the teapot */ - public void glutWireTeapot(double scale) { + public void glutWireTeapot(final double scale) { glutWireTeapot(scale, true); } - + /** * Renders the teapot as a wireframe shape of the specified size. The teapot * can either be created in a way that is backward-compatible with the @@ -308,29 +309,29 @@ public class GLUT { * plane, instead of the y=-1 plane). Both surface normals and texture * coordinates for the teapot are generated. The teapot is generated with * OpenGL evaluators. - * + * * @param scale * the factor by which to scale the teapot * @param cStyle * whether to create the teapot in exactly the same way as in the C * implementation of GLUT */ - public void glutWireTeapot(double scale, boolean cStyle) { - teapot(GLUgl2.getCurrentGL2(), 10, scale, GL2.GL_LINE, cStyle); + public void glutWireTeapot(final double scale, final boolean cStyle) { + teapot(GLUgl2.getCurrentGL2(), 10, scale, GL2GL3.GL_LINE, cStyle); } //---------------------------------------------------------------------- // Fonts // - public void glutBitmapCharacter(int font, char character) { - GL2 gl = GLUgl2.getCurrentGL2(); - int[] swapbytes = new int[1]; - int[] lsbfirst = new int[1]; - int[] rowlength = new int[1]; - int[] skiprows = new int[1]; - int[] skippixels = new int[1]; - int[] alignment = new int[1]; + public void glutBitmapCharacter(final int font, final char character) { + final GL2 gl = GLUgl2.getCurrentGL2(); + final int[] swapbytes = new int[1]; + final int[] lsbfirst = new int[1]; + final int[] rowlength = new int[1]; + final int[] skiprows = new int[1]; + final int[] skippixels = new int[1]; + final int[] alignment = new int[1]; beginBitmap(gl, swapbytes, lsbfirst, @@ -348,26 +349,26 @@ public class GLUT { alignment); } - public void glutBitmapString (int font, String string) { - GL2 gl = GLUgl2.getCurrentGL2(); - int[] swapbytes = new int[1]; - int[] lsbfirst = new int[1]; - int[] rowlength = new int[1]; - int[] skiprows = new int[1]; - int[] skippixels = new int[1]; - int[] alignment = new int[1]; - beginBitmap(gl, + public void glutBitmapString (final int font, final String string) { + final GL2 gl = GLUgl2.getCurrentGL2(); + final int[] swapbytes = new int[1]; + final int[] lsbfirst = new int[1]; + final int[] rowlength = new int[1]; + final int[] skiprows = new int[1]; + final int[] skippixels = new int[1]; + final int[] alignment = new int[1]; + beginBitmap(gl, swapbytes, lsbfirst, rowlength, skiprows, skippixels, alignment); - int len = string.length(); + final int len = string.length(); for (int i = 0; i < len; i++) { bitmapCharacterImpl(gl, font, string.charAt(i)); } - endBitmap(gl, + endBitmap(gl, swapbytes, lsbfirst, rowlength, @@ -376,31 +377,31 @@ public class GLUT { alignment); } - public int glutBitmapWidth (int font, char character) { - BitmapFontRec fontinfo = getBitmapFont(font); - int c = character & 0xFFFF; + public int glutBitmapWidth (final int font, final char character) { + final BitmapFontRec fontinfo = getBitmapFont(font); + final int c = character & 0xFFFF; if (c < fontinfo.first || c >= fontinfo.first + fontinfo.num_chars) return 0; - BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; + final BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; if (ch != null) return (int) ch.advance; else return 0; } - public void glutStrokeCharacter(int font, char character) { - GL2 gl = GLUgl2.getCurrentGL2(); - StrokeFontRec fontinfo = getStrokeFont(font); - int c = character & 0xFFFF; + public void glutStrokeCharacter(final int font, final char character) { + final GL2 gl = GLUgl2.getCurrentGL2(); + final StrokeFontRec fontinfo = getStrokeFont(font); + final int c = character & 0xFFFF; if (c < 0 || c >= fontinfo.num_chars) return; - StrokeCharRec ch = fontinfo.ch[c]; + final StrokeCharRec ch = fontinfo.ch[c]; if (ch != null) { for (int i = 0; i < ch.num_strokes; i++) { - StrokeRec stroke = ch.stroke[i]; - gl.glBegin(GL2.GL_LINE_STRIP); + final StrokeRec stroke = ch.stroke[i]; + gl.glBegin(GL.GL_LINE_STRIP); for (int j = 0; j < stroke.num_coords; j++) { - CoordRec coord = stroke.coord[j]; + final CoordRec coord = stroke.coord[j]; gl.glVertex2f(coord.x, coord.y); } gl.glEnd(); @@ -409,21 +410,21 @@ public class GLUT { } } - public void glutStrokeString(int font, String string) { - GL2 gl = GLUgl2.getCurrentGL2(); - StrokeFontRec fontinfo = getStrokeFont(font); - int len = string.length(); + public void glutStrokeString(final int font, final String string) { + final GL2 gl = GLUgl2.getCurrentGL2(); + final StrokeFontRec fontinfo = getStrokeFont(font); + final int len = string.length(); for (int pos = 0; pos < len; pos++) { - int c = string.charAt(pos) & 0xFFFF; + final int c = string.charAt(pos) & 0xFFFF; if (c < 0 || c >= fontinfo.num_chars) continue; - StrokeCharRec ch = fontinfo.ch[c]; + final StrokeCharRec ch = fontinfo.ch[c]; if (ch != null) { for (int i = 0; i < ch.num_strokes; i++) { - StrokeRec stroke = ch.stroke[i]; - gl.glBegin(GL2.GL_LINE_STRIP); + final StrokeRec stroke = ch.stroke[i]; + gl.glBegin(GL.GL_LINE_STRIP); for (int j = 0; j < stroke.num_coords; j++) { - CoordRec coord = stroke.coord[j]; + final CoordRec coord = stroke.coord[j]; gl.glVertex2f(coord.x, coord.y); } gl.glEnd(); @@ -433,30 +434,30 @@ public class GLUT { } } - public int glutStrokeWidth (int font, char character) { + public int glutStrokeWidth (final int font, final char character) { return (int) glutStrokeWidthf(font, character); } - public float glutStrokeWidthf (int font, char character) { - StrokeFontRec fontinfo = getStrokeFont(font); - int c = character & 0xFFFF; + public float glutStrokeWidthf (final int font, final char character) { + final StrokeFontRec fontinfo = getStrokeFont(font); + final int c = character & 0xFFFF; if (c < 0 || c >= fontinfo.num_chars) return 0; - StrokeCharRec ch = fontinfo.ch[c]; + final StrokeCharRec ch = fontinfo.ch[c]; if (ch != null) return ch.right; else return 0; } - public int glutBitmapLength (int font, String string) { - BitmapFontRec fontinfo = getBitmapFont(font); + public int glutBitmapLength (final int font, final String string) { + final BitmapFontRec fontinfo = getBitmapFont(font); int length = 0; - int len = string.length(); + final int len = string.length(); for (int pos = 0; pos < len; pos++) { - int c = string.charAt(pos) & 0xFFFF; + final int c = string.charAt(pos) & 0xFFFF; if (c >= fontinfo.first && c < fontinfo.first + fontinfo.num_chars) { - BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; + final BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; if (ch != null) length += ch.advance; } @@ -464,18 +465,18 @@ public class GLUT { return length; } - public int glutStrokeLength (int font, String string) { + public int glutStrokeLength (final int font, final String string) { return (int) glutStrokeLengthf(font, string); } - public float glutStrokeLengthf (int font, String string) { - StrokeFontRec fontinfo = getStrokeFont(font); + public float glutStrokeLengthf (final int font, final String string) { + final StrokeFontRec fontinfo = getStrokeFont(font); float length = 0; - int len = string.length(); + final int len = string.length(); for (int i = 0; i < len; i++) { - char c = string.charAt(i); + final char c = string.charAt(i); if (c >= 0 && c < fontinfo.num_chars) { - StrokeCharRec ch = fontinfo.ch[c]; + final StrokeCharRec ch = fontinfo.ch[c]; if (ch != null) length += ch.right; } @@ -491,9 +492,9 @@ public class GLUT { The polyhedron is centered at the origin. */ public void glutWireRhombicDodecahedron() { - GL2 gl = GLUgl2.getCurrentGL2(); + final GL2 gl = GLUgl2.getCurrentGL2(); for( int i = 0; i < 12; i++ ) { - gl.glBegin( GL2.GL_LINE_LOOP ); + gl.glBegin( GL.GL_LINE_LOOP ); gl.glNormal3dv( rdod_n[ i ],0 ); gl.glVertex3dv( rdod_r[ rdod_v[ i ][ 0 ] ],0 ); gl.glVertex3dv( rdod_r[ rdod_v[ i ][ 1 ] ],0 ); @@ -502,7 +503,7 @@ public class GLUT { gl.glEnd( ); } } - + /** This function draws a solid-shaded dodecahedron whose facets are rhombic and @@ -511,8 +512,8 @@ public class GLUT { The polyhedron is centered at the origin. */ public void glutSolidRhombicDodecahedron() { - GL2 gl = GLUgl2.getCurrentGL2(); - gl.glBegin( GL2.GL_QUADS ); + final GL2 gl = GLUgl2.getCurrentGL2(); + gl.glBegin( GL2GL3.GL_QUADS ); for( int i = 0; i < 12; i++ ) { gl.glNormal3dv( rdod_n[ i ],0 ); gl.glVertex3dv( rdod_r[ rdod_v[ i ][ 0 ] ],0 ); @@ -522,7 +523,7 @@ public class GLUT { } gl.glEnd( ); } - + //---------------------------------------------------------------------- // Internals only below this point // @@ -532,7 +533,7 @@ public class GLUT { // private GLUquadric quadObj; - private void quadObjInit(GLUgl2 glu) { + private void quadObjInit(final GLUgl2 glu) { if (quadObj == null) { quadObj = glu.gluNewQuadric(); } @@ -541,7 +542,7 @@ public class GLUT { } } - private static void doughnut(GL2 gl, double r, double R, int nsides, int rings) { + private static void doughnut(final GL2 gl, final double r, final double R, final int nsides, final int rings) { int i, j; float theta, phi, theta1; float cosTheta, sinTheta; @@ -597,9 +598,9 @@ public class GLUT { {5, 6, 2, 1}, {7, 4, 0, 3} }; - private void drawBox(GL2 gl, float size, int type) { + private void drawBox(final GL2 gl, final float size, final int type) { if (boxVertices == null) { - float[][] v = new float[8][]; + final float[][] v = new float[8][]; for (int i = 0; i < 8; i++) { v[i] = new float[3]; } @@ -611,9 +612,9 @@ public class GLUT { v[1][2] = v[2][2] = v[5][2] = v[6][2] = 0.5f; boxVertices = v; } - float[][] v = boxVertices; - float[][] n = boxNormals; - int[][] faces = boxFaces; + final float[][] v = boxVertices; + final float[][] n = boxNormals; + final int[][] faces = boxFaces; for (int i = 5; i >= 0; i--) { gl.glBegin(type); gl.glNormal3fv(n[i], 0); @@ -664,14 +665,14 @@ public class GLUT { dodec[19][0] = 0; dodec[19][1] = -beta; dodec[19][2] = -alpha; } - private static void diff3(float[] a, float[] b, float[] c) { + private static void diff3(final float[] a, final float[] b, final float[] c) { c[0] = a[0] - b[0]; c[1] = a[1] - b[1]; c[2] = a[2] - b[2]; } - private static void crossprod(float[] v1, float[] v2, float[] prod) { - float[] p = new float[3]; /* in case prod == v1 or v2 */ + private static void crossprod(final float[] v1, final float[] v2, final float[] prod) { + final float[] p = new float[3]; /* in case prod == v1 or v2 */ p[0] = v1[1] * v2[2] - v2[1] * v1[2]; p[1] = v1[2] * v2[0] - v2[2] * v1[0]; @@ -681,7 +682,7 @@ public class GLUT { prod[2] = p[2]; } - private static void normalize(float[] v) { + private static void normalize(final float[] v) { float d; d = (float) Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); @@ -694,10 +695,10 @@ public class GLUT { v[2] *= d; } - private void pentagon(GL2 gl, int a, int b, int c, int d, int e, int shadeType) { - float[] n0 = new float[3]; - float[] d1 = new float[3]; - float[] d2 = new float[3]; + private void pentagon(final GL2 gl, final int a, final int b, final int c, final int d, final int e, final int shadeType) { + final float[] n0 = new float[3]; + final float[] d1 = new float[3]; + final float[] d2 = new float[3]; diff3(dodec[a], dodec[b], d1); diff3(dodec[b], dodec[c], d2); @@ -714,7 +715,7 @@ public class GLUT { gl.glEnd(); } - private void dodecahedron(GL2 gl, int type) { + private void dodecahedron(final GL2 gl, final int type) { if (dodec == null) { initDodecahedron(); } @@ -732,9 +733,9 @@ public class GLUT { pentagon(gl, 4, 12, 5, 16, 17, type); } - private static void recorditem(GL2 gl, float[] n1, float[] n2, float[] n3, int shadeType) { - float[] q0 = new float[3]; - float[] q1 = new float[3]; + private static void recorditem(final GL2 gl, final float[] n1, final float[] n2, final float[] n3, final int shadeType) { + final float[] q0 = new float[3]; + final float[] q1 = new float[3]; diff3(n1, n2, q0); diff3(n2, n3, q1); @@ -749,11 +750,11 @@ public class GLUT { gl.glEnd(); } - private static void subdivide(GL2 gl, float[] v0, float[] v1, float[] v2, int shadeType) { + private static void subdivide(final GL2 gl, final float[] v0, final float[] v1, final float[] v2, final int shadeType) { int depth; - float[] w0 = new float[3]; - float[] w1 = new float[3]; - float[] w2 = new float[3]; + final float[] w0 = new float[3]; + final float[] w1 = new float[3]; + final float[] w2 = new float[3]; float l; int i, j, k, n; @@ -785,10 +786,10 @@ public class GLUT { } } - private static void drawtriangle(GL2 gl, int i, float[][] data, int[][] ndx, int shadeType) { - float[] x0 = data[ndx[i][0]]; - float[] x1 = data[ndx[i][1]]; - float[] x2 = data[ndx[i][2]]; + private static void drawtriangle(final GL2 gl, final int i, final float[][] data, final int[][] ndx, final int shadeType) { + final float[] x0 = data[ndx[i][0]]; + final float[] x1 = data[ndx[i][1]]; + final float[] x2 = data[ndx[i][2]]; subdivide(gl, x0, x1, x2, shadeType); } @@ -816,7 +817,7 @@ public class GLUT { {1, 3, 5} }; - private static void octahedron(GL2 gl, int shadeType) { + private static void octahedron(final GL2 gl, final int shadeType) { int i; for (i = 7; i >= 0; i--) { @@ -870,7 +871,7 @@ public class GLUT { {7, 2, 11}, }; - private static void icosahedron(GL2 gl, int shadeType) { + private static void icosahedron(final GL2 gl, final int shadeType) { int i; for (i = 19; i >= 0; i--) { @@ -879,7 +880,7 @@ public class GLUT { } /* rhombic dodecahedron data: */ - + private static final double rdod_r[][] = { { 0.0, 0.0, 1.0 }, @@ -897,7 +898,7 @@ public class GLUT { { 0.000000000000, -0.707106781187, -0.5 }, { 0.0, 0.0, -1.0 } }; - + private static final int rdod_v[][] = { { 0, 1, 5, 2 }, @@ -913,7 +914,7 @@ public class GLUT { { 7, 11, 13, 12 }, { 8, 12, 13, 9 } }; - + private static final double rdod_n[][] = { { 0.353553390594, 0.353553390594, 0.5 }, @@ -929,7 +930,7 @@ public class GLUT { { -0.353553390594, -0.353553390594, -0.5 }, { 0.353553390594, -0.353553390594, -0.5 } }; - + /* tetrahedron data: */ private static final float T = 1.73205080756887729f; @@ -950,7 +951,7 @@ public class GLUT { {1, 2, 3} }; - private static final void tetrahedron(GL2 gl, int shadeType) { + private static final void tetrahedron(final GL2 gl, final int shadeType) { for (int i = 3; i >= 0; i--) drawtriangle(gl, i, tdata, tndex, shadeType); } @@ -1112,28 +1113,28 @@ public class GLUT { 0, 0, 1, 0, 0, 1, 1, 1 }; - private static void teapot(GL2 gl, - int grid, - double scale, - int type, - boolean backCompatible) + private static void teapot(final GL2 gl, + final int grid, + final double scale, + final int type, + final boolean backCompatible) { // As mentioned above, GL2.glMap2f expects a packed array of floats - float[] p = new float[4*4*3]; - float[] q = new float[4*4*3]; - float[] r = new float[4*4*3]; - float[] s = new float[4*4*3]; + final float[] p = new float[4*4*3]; + final float[] q = new float[4*4*3]; + final float[] r = new float[4*4*3]; + final float[] s = new float[4*4*3]; int i, j, k, l; - + gl.glPushAttrib(GL2.GL_ENABLE_BIT | GL2.GL_EVAL_BIT | GL2.GL_POLYGON_BIT); gl.glEnable(GL2.GL_AUTO_NORMAL); - gl.glEnable(GL2.GL_NORMALIZE); + gl.glEnable(GLLightingFunc.GL_NORMALIZE); gl.glEnable(GL2.GL_MAP2_VERTEX_3); gl.glEnable(GL2.GL_MAP2_TEXTURE_COORD_2); gl.glPushMatrix(); if (!backCompatible) { // The time has come to have the teapot no longer be inside out - gl.glFrontFace(GL2.GL_CW); + gl.glFrontFace(GL.GL_CW); gl.glScaled(0.5*scale, 0.5*scale, 0.5*scale); } else { // We want the teapot in it's backward compatible position and @@ -1183,30 +1184,30 @@ public class GLUT { gl.glPopMatrix(); gl.glPopAttrib(); } - - private static void evaluateTeapotMesh(GL2 gl, - int grid, - int type, - int partNum, - boolean repairSingularities) + + private static void evaluateTeapotMesh(final GL2 gl, + final int grid, + final int type, + final int partNum, + final boolean repairSingularities) { if (repairSingularities && (partNum == 5 || partNum == 3)) { // Instead of using evaluators that give bad results at singularities, // evaluate by hand - gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, type); + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, type); for (int nv = 0; nv < grid; nv++) { if (nv == 0) { // Draw a small triangle-fan to fill the hole gl.glDisable(GL2.GL_AUTO_NORMAL); gl.glNormal3f(0, 0, partNum == 3 ? 1 : -1); - gl.glBegin(GL2.GL_TRIANGLE_FAN); + gl.glBegin(GL.GL_TRIANGLE_FAN); { gl.glEvalCoord2f(0, 0); // Note that we draw in clock-wise order to match the evaluator // method for (int nu = 0; nu <= grid; nu++) { - gl.glEvalCoord2f(nu / (float)grid, (1f / grid) / (float)grid); + gl.glEvalCoord2f(nu / (float)grid, (1f / grid) / grid); } } gl.glEnd(); @@ -1219,7 +1220,7 @@ public class GLUT { for (int nu = grid; nu >= 0; nu--) { gl.glEvalCoord2f(nu / (float)grid, (nv + 1) / (float)grid); gl.glEvalCoord2f(nu / (float)grid, Math.max(nv, 1f / grid) - / (float)grid); + / grid); } } gl.glEnd(); @@ -1233,13 +1234,13 @@ public class GLUT { // Font implementation // - private static void bitmapCharacterImpl(GL2 gl, int font, char cin) { - BitmapFontRec fontinfo = getBitmapFont(font); - int c = cin & 0xFFFF; + private static void bitmapCharacterImpl(final GL2 gl, final int font, final char cin) { + final BitmapFontRec fontinfo = getBitmapFont(font); + final int c = cin & 0xFFFF; if (c < fontinfo.first || c >= fontinfo.first + fontinfo.num_chars) return; - BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; + final BitmapCharRec ch = fontinfo.ch[c - fontinfo.first]; if (ch != null) { gl.glBitmap(ch.width, ch.height, ch.xorig, ch.yorig, ch.advance, 0, ch.bitmap, 0); @@ -1249,7 +1250,7 @@ public class GLUT { private static final BitmapFontRec[] bitmapFonts = new BitmapFontRec[9]; private static final StrokeFontRec[] strokeFonts = new StrokeFontRec[9]; - private static BitmapFontRec getBitmapFont(int font) { + private static BitmapFontRec getBitmapFont(final int font) { BitmapFontRec rec = bitmapFonts[font]; if (rec == null) { switch (font) { @@ -1282,7 +1283,7 @@ public class GLUT { return rec; } - private static StrokeFontRec getStrokeFont(int font) { + private static StrokeFontRec getStrokeFont(final int font) { StrokeFontRec rec = strokeFonts[font]; if (rec == null) { switch (font) { @@ -1299,44 +1300,44 @@ public class GLUT { return rec; } - private static void beginBitmap(GL2 gl, - int[] swapbytes, - int[] lsbfirst, - int[] rowlength, - int[] skiprows, - int[] skippixels, - int[] alignment) { - gl.glGetIntegerv(GL2.GL_UNPACK_SWAP_BYTES, swapbytes, 0); - gl.glGetIntegerv(GL2.GL_UNPACK_LSB_FIRST, lsbfirst, 0); - gl.glGetIntegerv(GL2.GL_UNPACK_ROW_LENGTH, rowlength, 0); - gl.glGetIntegerv(GL2.GL_UNPACK_SKIP_ROWS, skiprows, 0); - gl.glGetIntegerv(GL2.GL_UNPACK_SKIP_PIXELS, skippixels, 0); - gl.glGetIntegerv(GL2.GL_UNPACK_ALIGNMENT, alignment, 0); + private static void beginBitmap(final GL2 gl, + final int[] swapbytes, + final int[] lsbfirst, + final int[] rowlength, + final int[] skiprows, + final int[] skippixels, + final int[] alignment) { + gl.glGetIntegerv(GL2GL3.GL_UNPACK_SWAP_BYTES, swapbytes, 0); + gl.glGetIntegerv(GL2GL3.GL_UNPACK_LSB_FIRST, lsbfirst, 0); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_ROW_LENGTH, rowlength, 0); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_ROWS, skiprows, 0); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_PIXELS, skippixels, 0); + gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, alignment, 0); /* Little endian machines (DEC Alpha for example) could benefit from setting GL_UNPACK_LSB_FIRST to GL_TRUE instead of GL_FALSE, but this would require changing the generated bitmaps too. */ - gl.glPixelStorei(GL2.GL_UNPACK_SWAP_BYTES, GL2.GL_FALSE); - gl.glPixelStorei(GL2.GL_UNPACK_LSB_FIRST, GL2.GL_FALSE); - gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1); + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, GL.GL_FALSE); + gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, GL.GL_FALSE); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); } - private static void endBitmap(GL2 gl, - int[] swapbytes, - int[] lsbfirst, - int[] rowlength, - int[] skiprows, - int[] skippixels, - int[] alignment) { + private static void endBitmap(final GL2 gl, + final int[] swapbytes, + final int[] lsbfirst, + final int[] rowlength, + final int[] skiprows, + final int[] skippixels, + final int[] alignment) { /* Restore saved modes. */ - gl.glPixelStorei(GL2.GL_UNPACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL2.GL_UNPACK_LSB_FIRST, lsbfirst[0]); - gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, alignment[0]); + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, swapbytes[0]); + gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, lsbfirst[0]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, rowlength[0]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, skiprows[0]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, skippixels[0]); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, alignment[0]); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap8x13.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap8x13.java index 07ded652a..c24483777 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap8x13.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap8x13.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap9x15.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap9x15.java index 5d357f3f7..62af3b631 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap9x15.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmap9x15.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica10.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica10.java index b9c7e6e50..5f06d697e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica10.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica10.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica12.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica12.java index bc86f6216..8326d6461 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica12.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica12.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica18.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica18.java index 1b2e69ba4..cb11f6bec 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica18.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapHelvetica18.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman10.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman10.java index f753b56f7..17cbd0796 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman10.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman10.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman24.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman24.java index 073e6e673..9cc2bdc3a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman24.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTBitmapTimesRoman24.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeMonoRoman.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeMonoRoman.java index b8296924e..a191efdb4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeMonoRoman.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeMonoRoman.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -46,14 +46,14 @@ class GLUTStrokeMonoRoman { /* char: 33 '!' */ static final CoordRec char33_stroke0[] = { - new CoordRec((float) 52.381, (float) 100 ), + new CoordRec((float) 52.381, 100 ), new CoordRec((float) 52.381, (float) 33.3333 ), }; static final CoordRec char33_stroke1[] = { new CoordRec((float) 52.381, (float) 9.5238 ), new CoordRec((float) 47.6191, (float) 4.7619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), new CoordRec((float) 57.1429, (float) 4.7619 ), new CoordRec((float) 52.381, (float) 9.5238 ), }; @@ -66,12 +66,12 @@ static final StrokeRec char33[] = { /* char: 34 '"' */ static final CoordRec char34_stroke0[] = { - new CoordRec((float) 33.3334, (float) 100 ), + new CoordRec((float) 33.3334, 100 ), new CoordRec((float) 33.3334, (float) 66.6667 ), }; static final CoordRec char34_stroke1[] = { - new CoordRec((float) 71.4286, (float) 100 ), + new CoordRec((float) 71.4286, 100 ), new CoordRec((float) 71.4286, (float) 66.6667 ), }; @@ -89,7 +89,7 @@ static final CoordRec char35_stroke0[] = { static final CoordRec char35_stroke1[] = { new CoordRec((float) 83.3334, (float) 119.048 ), - new CoordRec((float) 50, (float) -33.3333 ), + new CoordRec(50, (float) -33.3333 ), }; static final CoordRec char35_stroke2[] = { @@ -124,8 +124,8 @@ static final CoordRec char36_stroke1[] = { static final CoordRec char36_stroke2[] = { new CoordRec((float) 85.7143, (float) 85.7143 ), new CoordRec((float) 76.1905, (float) 95.2381 ), - new CoordRec((float) 61.9047, (float) 100 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 61.9047, 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 28.5714, (float) 95.2381 ), new CoordRec((float) 19.0476, (float) 85.7143 ), new CoordRec((float) 19.0476, (float) 76.1905 ), @@ -138,8 +138,8 @@ static final CoordRec char36_stroke2[] = { new CoordRec((float) 85.7143, (float) 28.5714 ), new CoordRec((float) 85.7143, (float) 14.2857 ), new CoordRec((float) 76.1905, (float) 4.7619 ), - new CoordRec((float) 61.9047, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 61.9047, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 28.5714, (float) 4.7619 ), new CoordRec((float) 19.0476, (float) 14.2857 ), }; @@ -153,12 +153,12 @@ static final StrokeRec char36[] = { /* char: 37 '%' */ static final CoordRec char37_stroke0[] = { - new CoordRec((float) 95.2381, (float) 100 ), - new CoordRec((float) 9.5238, (float) 0 ), + new CoordRec((float) 95.2381, 100 ), + new CoordRec((float) 9.5238, 0 ), }; static final CoordRec char37_stroke1[] = { - new CoordRec((float) 33.3333, (float) 100 ), + new CoordRec((float) 33.3333, 100 ), new CoordRec((float) 42.8571, (float) 90.4762 ), new CoordRec((float) 42.8571, (float) 80.9524 ), new CoordRec((float) 38.0952, (float) 71.4286 ), @@ -167,13 +167,13 @@ static final CoordRec char37_stroke1[] = { new CoordRec((float) 9.5238, (float) 76.1905 ), new CoordRec((float) 9.5238, (float) 85.7143 ), new CoordRec((float) 14.2857, (float) 95.2381 ), - new CoordRec((float) 23.8095, (float) 100 ), - new CoordRec((float) 33.3333, (float) 100 ), + new CoordRec((float) 23.8095, 100 ), + new CoordRec((float) 33.3333, 100 ), new CoordRec((float) 42.8571, (float) 95.2381 ), new CoordRec((float) 57.1428, (float) 90.4762 ), new CoordRec((float) 71.4286, (float) 90.4762 ), new CoordRec((float) 85.7143, (float) 95.2381 ), - new CoordRec((float) 95.2381, (float) 100 ), + new CoordRec((float) 95.2381, 100 ), }; static final CoordRec char37_stroke2[] = { @@ -181,8 +181,8 @@ static final CoordRec char37_stroke2[] = { new CoordRec((float) 66.6667, (float) 28.5714 ), new CoordRec((float) 61.9048, (float) 19.0476 ), new CoordRec((float) 61.9048, (float) 9.5238 ), - new CoordRec((float) 71.4286, (float) 0 ), - new CoordRec((float) 80.9524, (float) 0 ), + new CoordRec((float) 71.4286, 0 ), + new CoordRec((float) 80.9524, 0 ), new CoordRec((float) 90.4762, (float) 4.7619 ), new CoordRec((float) 95.2381, (float) 14.2857 ), new CoordRec((float) 95.2381, (float) 23.8095 ), @@ -199,8 +199,8 @@ static final StrokeRec char37[] = { /* char: 38 '&' */ static final CoordRec char38_stroke0[] = { - new CoordRec((float) 100, (float) 57.1429 ), - new CoordRec((float) 100, (float) 61.9048 ), + new CoordRec(100, (float) 57.1429 ), + new CoordRec(100, (float) 61.9048 ), new CoordRec((float) 95.2381, (float) 66.6667 ), new CoordRec((float) 90.4762, (float) 66.6667 ), new CoordRec((float) 85.7143, (float) 61.9048 ), @@ -208,8 +208,8 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 71.4286, (float) 28.5714 ), new CoordRec((float) 61.9048, (float) 14.2857 ), new CoordRec((float) 52.3809, (float) 4.7619 ), - new CoordRec((float) 42.8571, (float) 0 ), - new CoordRec((float) 23.8095, (float) 0 ), + new CoordRec((float) 42.8571, 0 ), + new CoordRec((float) 23.8095, 0 ), new CoordRec((float) 14.2857, (float) 4.7619 ), new CoordRec((float) 9.5238, (float) 9.5238 ), new CoordRec((float) 4.7619, (float) 19.0476 ), @@ -221,7 +221,7 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 57.1429, (float) 76.1905 ), new CoordRec((float) 57.1429, (float) 85.7143 ), new CoordRec((float) 52.3809, (float) 95.2381 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 33.3333, (float) 95.2381 ), new CoordRec((float) 28.5714, (float) 85.7143 ), new CoordRec((float) 28.5714, (float) 76.1905 ), @@ -229,10 +229,10 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 42.8571, (float) 47.619 ), new CoordRec((float) 66.6667, (float) 14.2857 ), new CoordRec((float) 76.1905, (float) 4.7619 ), - new CoordRec((float) 85.7143, (float) 0 ), - new CoordRec((float) 95.2381, (float) 0 ), - new CoordRec((float) 100, (float) 4.7619 ), - new CoordRec((float) 100, (float) 9.5238 ), + new CoordRec((float) 85.7143, 0 ), + new CoordRec((float) 95.2381, 0 ), + new CoordRec(100, (float) 4.7619 ), + new CoordRec(100, (float) 9.5238 ), }; static final StrokeRec char38[] = { @@ -242,7 +242,7 @@ static final StrokeRec char38[] = { /* char: 39 ''' */ static final CoordRec char39_stroke0[] = { - new CoordRec((float) 52.381, (float) 100 ), + new CoordRec((float) 52.381, 100 ), new CoordRec((float) 52.381, (float) 66.6667 ), }; @@ -255,12 +255,12 @@ static final StrokeRec char39[] = { static final CoordRec char40_stroke0[] = { new CoordRec((float) 69.0476, (float) 119.048 ), new CoordRec((float) 59.5238, (float) 109.524 ), - new CoordRec((float) 50, (float) 95.2381 ), + new CoordRec(50, (float) 95.2381 ), new CoordRec((float) 40.4762, (float) 76.1905 ), new CoordRec((float) 35.7143, (float) 52.381 ), new CoordRec((float) 35.7143, (float) 33.3333 ), new CoordRec((float) 40.4762, (float) 9.5238 ), - new CoordRec((float) 50, (float) -9.5238 ), + new CoordRec(50, (float) -9.5238 ), new CoordRec((float) 59.5238, (float) -23.8095 ), new CoordRec((float) 69.0476, (float) -33.3333 ), }; @@ -315,7 +315,7 @@ static final StrokeRec char42[] = { static final CoordRec char43_stroke0[] = { new CoordRec((float) 52.3809, (float) 85.7143 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char43_stroke1[] = { @@ -332,7 +332,7 @@ static final StrokeRec char43[] = { static final CoordRec char44_stroke0[] = { new CoordRec((float) 57.1429, (float) 4.7619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), new CoordRec((float) 47.6191, (float) 4.7619 ), new CoordRec((float) 52.381, (float) 9.5238 ), new CoordRec((float) 57.1429, (float) 4.7619 ), @@ -361,7 +361,7 @@ static final StrokeRec char45[] = { static final CoordRec char46_stroke0[] = { new CoordRec((float) 52.381, (float) 9.5238 ), new CoordRec((float) 47.6191, (float) 4.7619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), new CoordRec((float) 57.1429, (float) 4.7619 ), new CoordRec((float) 52.381, (float) 9.5238 ), }; @@ -374,7 +374,7 @@ static final StrokeRec char46[] = { static final CoordRec char47_stroke0[] = { new CoordRec((float) 19.0476, (float) -14.2857 ), - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 85.7143, 100 ), }; static final StrokeRec char47[] = { @@ -384,23 +384,23 @@ static final StrokeRec char47[] = { /* char: 48 '0' */ static final CoordRec char48_stroke0[] = { - new CoordRec((float) 47.619, (float) 100 ), + new CoordRec((float) 47.619, 100 ), new CoordRec((float) 33.3333, (float) 95.2381 ), new CoordRec((float) 23.8095, (float) 80.9524 ), new CoordRec((float) 19.0476, (float) 57.1429 ), new CoordRec((float) 19.0476, (float) 42.8571 ), new CoordRec((float) 23.8095, (float) 19.0476 ), new CoordRec((float) 33.3333, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 57.1428, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 57.1428, 0 ), new CoordRec((float) 71.4286, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 19.0476 ), new CoordRec((float) 85.7143, (float) 42.8571 ), new CoordRec((float) 85.7143, (float) 57.1429 ), new CoordRec((float) 80.9524, (float) 80.9524 ), new CoordRec((float) 71.4286, (float) 95.2381 ), - new CoordRec((float) 57.1428, (float) 100 ), - new CoordRec((float) 47.619, (float) 100 ), + new CoordRec((float) 57.1428, 100 ), + new CoordRec((float) 47.619, 100 ), }; static final StrokeRec char48[] = { @@ -411,9 +411,9 @@ static final StrokeRec char48[] = { static final CoordRec char49_stroke0[] = { new CoordRec((float) 40.4762, (float) 80.9524 ), - new CoordRec((float) 50, (float) 85.7143 ), - new CoordRec((float) 64.2857, (float) 100 ), - new CoordRec((float) 64.2857, (float) 0 ), + new CoordRec(50, (float) 85.7143 ), + new CoordRec((float) 64.2857, 100 ), + new CoordRec((float) 64.2857, 0 ), }; static final StrokeRec char49[] = { @@ -427,16 +427,16 @@ static final CoordRec char50_stroke0[] = { new CoordRec((float) 23.8095, (float) 80.9524 ), new CoordRec((float) 28.5714, (float) 90.4762 ), new CoordRec((float) 33.3333, (float) 95.2381 ), - new CoordRec((float) 42.8571, (float) 100 ), - new CoordRec((float) 61.9047, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), + new CoordRec((float) 61.9047, 100 ), new CoordRec((float) 71.4286, (float) 95.2381 ), new CoordRec((float) 76.1905, (float) 90.4762 ), new CoordRec((float) 80.9524, (float) 80.9524 ), new CoordRec((float) 80.9524, (float) 71.4286 ), new CoordRec((float) 76.1905, (float) 61.9048 ), new CoordRec((float) 66.6666, (float) 47.619 ), - new CoordRec((float) 19.0476, (float) 0 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 19.0476, 0 ), + new CoordRec((float) 85.7143, 0 ), }; static final StrokeRec char50[] = { @@ -446,8 +446,8 @@ static final StrokeRec char50[] = { /* char: 51 '3' */ static final CoordRec char51_stroke0[] = { - new CoordRec((float) 28.5714, (float) 100 ), - new CoordRec((float) 80.9524, (float) 100 ), + new CoordRec((float) 28.5714, 100 ), + new CoordRec((float) 80.9524, 100 ), new CoordRec((float) 52.3809, (float) 61.9048 ), new CoordRec((float) 66.6666, (float) 61.9048 ), new CoordRec((float) 76.1905, (float) 57.1429 ), @@ -456,8 +456,8 @@ static final CoordRec char51_stroke0[] = { new CoordRec((float) 85.7143, (float) 28.5714 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 71.4286, (float) 4.7619 ), - new CoordRec((float) 57.1428, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 57.1428, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 28.5714, (float) 4.7619 ), new CoordRec((float) 23.8095, (float) 9.5238 ), new CoordRec((float) 19.0476, (float) 19.0476 ), @@ -470,14 +470,14 @@ static final StrokeRec char51[] = { /* char: 52 '4' */ static final CoordRec char52_stroke0[] = { - new CoordRec((float) 64.2857, (float) 100 ), + new CoordRec((float) 64.2857, 100 ), new CoordRec((float) 16.6667, (float) 33.3333 ), new CoordRec((float) 88.0952, (float) 33.3333 ), }; static final CoordRec char52_stroke1[] = { - new CoordRec((float) 64.2857, (float) 100 ), - new CoordRec((float) 64.2857, (float) 0 ), + new CoordRec((float) 64.2857, 100 ), + new CoordRec((float) 64.2857, 0 ), }; static final StrokeRec char52[] = { @@ -488,8 +488,8 @@ static final StrokeRec char52[] = { /* char: 53 '5' */ static final CoordRec char53_stroke0[] = { - new CoordRec((float) 76.1905, (float) 100 ), - new CoordRec((float) 28.5714, (float) 100 ), + new CoordRec((float) 76.1905, 100 ), + new CoordRec((float) 28.5714, 100 ), new CoordRec((float) 23.8095, (float) 57.1429 ), new CoordRec((float) 28.5714, (float) 61.9048 ), new CoordRec((float) 42.8571, (float) 66.6667 ), @@ -500,8 +500,8 @@ static final CoordRec char53_stroke0[] = { new CoordRec((float) 85.7143, (float) 28.5714 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 71.4286, (float) 4.7619 ), - new CoordRec((float) 57.1428, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 57.1428, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 28.5714, (float) 4.7619 ), new CoordRec((float) 23.8095, (float) 9.5238 ), new CoordRec((float) 19.0476, (float) 19.0476 ), @@ -516,16 +516,16 @@ static final StrokeRec char53[] = { static final CoordRec char54_stroke0[] = { new CoordRec((float) 78.5714, (float) 85.7143 ), new CoordRec((float) 73.8096, (float) 95.2381 ), - new CoordRec((float) 59.5238, (float) 100 ), - new CoordRec((float) 50, (float) 100 ), + new CoordRec((float) 59.5238, 100 ), + new CoordRec(50, 100 ), new CoordRec((float) 35.7143, (float) 95.2381 ), new CoordRec((float) 26.1905, (float) 80.9524 ), new CoordRec((float) 21.4286, (float) 57.1429 ), new CoordRec((float) 21.4286, (float) 33.3333 ), new CoordRec((float) 26.1905, (float) 14.2857 ), new CoordRec((float) 35.7143, (float) 4.7619 ), - new CoordRec((float) 50, (float) 0 ), - new CoordRec((float) 54.7619, (float) 0 ), + new CoordRec(50, 0 ), + new CoordRec((float) 54.7619, 0 ), new CoordRec((float) 69.0476, (float) 4.7619 ), new CoordRec((float) 78.5714, (float) 14.2857 ), new CoordRec((float) 83.3334, (float) 28.5714 ), @@ -533,7 +533,7 @@ static final CoordRec char54_stroke0[] = { new CoordRec((float) 78.5714, (float) 47.619 ), new CoordRec((float) 69.0476, (float) 57.1429 ), new CoordRec((float) 54.7619, (float) 61.9048 ), - new CoordRec((float) 50, (float) 61.9048 ), + new CoordRec(50, (float) 61.9048 ), new CoordRec((float) 35.7143, (float) 57.1429 ), new CoordRec((float) 26.1905, (float) 47.619 ), new CoordRec((float) 21.4286, (float) 33.3333 ), @@ -546,13 +546,13 @@ static final StrokeRec char54[] = { /* char: 55 '7' */ static final CoordRec char55_stroke0[] = { - new CoordRec((float) 85.7143, (float) 100 ), - new CoordRec((float) 38.0952, (float) 0 ), + new CoordRec((float) 85.7143, 100 ), + new CoordRec((float) 38.0952, 0 ), }; static final CoordRec char55_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 85.7143, 100 ), }; static final StrokeRec char55[] = { @@ -563,7 +563,7 @@ static final StrokeRec char55[] = { /* char: 56 '8' */ static final CoordRec char56_stroke0[] = { - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 28.5714, (float) 95.2381 ), new CoordRec((float) 23.8095, (float) 85.7143 ), new CoordRec((float) 23.8095, (float) 76.1905 ), @@ -576,8 +576,8 @@ static final CoordRec char56_stroke0[] = { new CoordRec((float) 85.7143, (float) 19.0476 ), new CoordRec((float) 80.9524, (float) 9.5238 ), new CoordRec((float) 76.1905, (float) 4.7619 ), - new CoordRec((float) 61.9047, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 61.9047, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 28.5714, (float) 4.7619 ), new CoordRec((float) 23.8095, (float) 9.5238 ), new CoordRec((float) 19.0476, (float) 19.0476 ), @@ -590,8 +590,8 @@ static final CoordRec char56_stroke0[] = { new CoordRec((float) 80.9524, (float) 76.1905 ), new CoordRec((float) 80.9524, (float) 85.7143 ), new CoordRec((float) 76.1905, (float) 95.2381 ), - new CoordRec((float) 61.9047, (float) 100 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 61.9047, 100 ), + new CoordRec((float) 42.8571, 100 ), }; static final StrokeRec char56[] = { @@ -605,23 +605,23 @@ static final CoordRec char57_stroke0[] = { new CoordRec((float) 78.5714, (float) 52.381 ), new CoordRec((float) 69.0476, (float) 42.8571 ), new CoordRec((float) 54.7619, (float) 38.0952 ), - new CoordRec((float) 50, (float) 38.0952 ), + new CoordRec(50, (float) 38.0952 ), new CoordRec((float) 35.7143, (float) 42.8571 ), new CoordRec((float) 26.1905, (float) 52.381 ), new CoordRec((float) 21.4286, (float) 66.6667 ), new CoordRec((float) 21.4286, (float) 71.4286 ), new CoordRec((float) 26.1905, (float) 85.7143 ), new CoordRec((float) 35.7143, (float) 95.2381 ), - new CoordRec((float) 50, (float) 100 ), - new CoordRec((float) 54.7619, (float) 100 ), + new CoordRec(50, 100 ), + new CoordRec((float) 54.7619, 100 ), new CoordRec((float) 69.0476, (float) 95.2381 ), new CoordRec((float) 78.5714, (float) 85.7143 ), new CoordRec((float) 83.3334, (float) 66.6667 ), new CoordRec((float) 83.3334, (float) 42.8571 ), new CoordRec((float) 78.5714, (float) 19.0476 ), new CoordRec((float) 69.0476, (float) 4.7619 ), - new CoordRec((float) 54.7619, (float) 0 ), - new CoordRec((float) 45.2381, (float) 0 ), + new CoordRec((float) 54.7619, 0 ), + new CoordRec((float) 45.2381, 0 ), new CoordRec((float) 30.9524, (float) 4.7619 ), new CoordRec((float) 26.1905, (float) 14.2857 ), }; @@ -643,7 +643,7 @@ static final CoordRec char58_stroke0[] = { static final CoordRec char58_stroke1[] = { new CoordRec((float) 52.381, (float) 9.5238 ), new CoordRec((float) 47.6191, (float) 4.7619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), new CoordRec((float) 57.1429, (float) 4.7619 ), new CoordRec((float) 52.381, (float) 9.5238 ), }; @@ -665,7 +665,7 @@ static final CoordRec char59_stroke0[] = { static final CoordRec char59_stroke1[] = { new CoordRec((float) 57.1429, (float) 4.7619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), new CoordRec((float) 47.6191, (float) 4.7619 ), new CoordRec((float) 52.381, (float) 9.5238 ), new CoordRec((float) 57.1429, (float) 4.7619 ), @@ -684,7 +684,7 @@ static final StrokeRec char59[] = { static final CoordRec char60_stroke0[] = { new CoordRec((float) 90.4762, (float) 85.7143 ), new CoordRec((float) 14.2857, (float) 42.8571 ), - new CoordRec((float) 90.4762, (float) 0 ), + new CoordRec((float) 90.4762, 0 ), }; static final StrokeRec char60[] = { @@ -713,7 +713,7 @@ static final StrokeRec char61[] = { static final CoordRec char62_stroke0[] = { new CoordRec((float) 14.2857, (float) 85.7143 ), new CoordRec((float) 90.4762, (float) 42.8571 ), - new CoordRec((float) 14.2857, (float) 0 ), + new CoordRec((float) 14.2857, 0 ), }; static final StrokeRec char62[] = { @@ -727,8 +727,8 @@ static final CoordRec char63_stroke0[] = { new CoordRec((float) 23.8095, (float) 80.9524 ), new CoordRec((float) 28.5714, (float) 90.4762 ), new CoordRec((float) 33.3333, (float) 95.2381 ), - new CoordRec((float) 42.8571, (float) 100 ), - new CoordRec((float) 61.9047, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), + new CoordRec((float) 61.9047, 100 ), new CoordRec((float) 71.4285, (float) 95.2381 ), new CoordRec((float) 76.1905, (float) 90.4762 ), new CoordRec((float) 80.9524, (float) 80.9524 ), @@ -742,7 +742,7 @@ static final CoordRec char63_stroke0[] = { static final CoordRec char63_stroke1[] = { new CoordRec((float) 52.3809, (float) 9.5238 ), new CoordRec((float) 47.619, (float) 4.7619 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), new CoordRec((float) 57.1428, (float) 4.7619 ), new CoordRec((float) 52.3809, (float) 9.5238 ), }; @@ -775,14 +775,14 @@ static final CoordRec char64_stroke1[] = { new CoordRec((float) 78.5714, (float) 61.9048 ), new CoordRec((float) 69.0476, (float) 71.4286 ), new CoordRec((float) 54.7619, (float) 76.1905 ), - new CoordRec((float) 50, (float) 76.1905 ), + new CoordRec(50, (float) 76.1905 ), new CoordRec((float) 35.7143, (float) 71.4286 ), new CoordRec((float) 26.1905, (float) 61.9048 ), new CoordRec((float) 21.4286, (float) 47.619 ), new CoordRec((float) 21.4286, (float) 42.8571 ), new CoordRec((float) 26.1905, (float) 28.5714 ), new CoordRec((float) 35.7143, (float) 19.0476 ), - new CoordRec((float) 50, (float) 14.2857 ), + new CoordRec(50, (float) 14.2857 ), new CoordRec((float) 54.7619, (float) 14.2857 ), new CoordRec((float) 69.0476, (float) 19.0476 ), }; @@ -795,13 +795,13 @@ static final StrokeRec char64[] = { /* char: 65 'A' */ static final CoordRec char65_stroke0[] = { - new CoordRec((float) 52.3809, (float) 100 ), - new CoordRec((float) 14.2857, (float) 0 ), + new CoordRec((float) 52.3809, 100 ), + new CoordRec((float) 14.2857, 0 ), }; static final CoordRec char65_stroke1[] = { - new CoordRec((float) 52.3809, (float) 100 ), - new CoordRec((float) 90.4762, (float) 0 ), + new CoordRec((float) 52.3809, 100 ), + new CoordRec((float) 90.4762, 0 ), }; static final CoordRec char65_stroke2[] = { @@ -818,13 +818,13 @@ static final StrokeRec char65[] = { /* char: 66 'B' */ static final CoordRec char66_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char66_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 61.9047, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 61.9047, 100 ), new CoordRec((float) 76.1905, (float) 95.2381 ), new CoordRec((float) 80.9524, (float) 90.4762 ), new CoordRec((float) 85.7143, (float) 80.9524 ), @@ -843,8 +843,8 @@ static final CoordRec char66_stroke2[] = { new CoordRec((float) 85.7143, (float) 19.0476 ), new CoordRec((float) 80.9524, (float) 9.5238 ), new CoordRec((float) 76.1905, (float) 4.7619 ), - new CoordRec((float) 61.9047, (float) 0 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 61.9047, 0 ), + new CoordRec((float) 19.0476, 0 ), }; static final StrokeRec char66[] = { @@ -859,8 +859,8 @@ static final CoordRec char67_stroke0[] = { new CoordRec((float) 88.0952, (float) 76.1905 ), new CoordRec((float) 83.3334, (float) 85.7143 ), new CoordRec((float) 73.8096, (float) 95.2381 ), - new CoordRec((float) 64.2857, (float) 100 ), - new CoordRec((float) 45.2381, (float) 100 ), + new CoordRec((float) 64.2857, 100 ), + new CoordRec((float) 45.2381, 100 ), new CoordRec((float) 35.7143, (float) 95.2381 ), new CoordRec((float) 26.1905, (float) 85.7143 ), new CoordRec((float) 21.4286, (float) 76.1905 ), @@ -869,8 +869,8 @@ static final CoordRec char67_stroke0[] = { new CoordRec((float) 21.4286, (float) 23.8095 ), new CoordRec((float) 26.1905, (float) 14.2857 ), new CoordRec((float) 35.7143, (float) 4.7619 ), - new CoordRec((float) 45.2381, (float) 0 ), - new CoordRec((float) 64.2857, (float) 0 ), + new CoordRec((float) 45.2381, 0 ), + new CoordRec((float) 64.2857, 0 ), new CoordRec((float) 73.8096, (float) 4.7619 ), new CoordRec((float) 83.3334, (float) 14.2857 ), new CoordRec((float) 88.0952, (float) 23.8095 ), @@ -883,13 +883,13 @@ static final StrokeRec char67[] = { /* char: 68 'D' */ static final CoordRec char68_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char68_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 52.3809, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 52.3809, 100 ), new CoordRec((float) 66.6666, (float) 95.2381 ), new CoordRec((float) 76.1905, (float) 85.7143 ), new CoordRec((float) 80.9524, (float) 76.1905 ), @@ -898,8 +898,8 @@ static final CoordRec char68_stroke1[] = { new CoordRec((float) 80.9524, (float) 23.8095 ), new CoordRec((float) 76.1905, (float) 14.2857 ), new CoordRec((float) 66.6666, (float) 4.7619 ), - new CoordRec((float) 52.3809, (float) 0 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), + new CoordRec((float) 19.0476, 0 ), }; static final StrokeRec char68[] = { @@ -910,13 +910,13 @@ static final StrokeRec char68[] = { /* char: 69 'E' */ static final CoordRec char69_stroke0[] = { - new CoordRec((float) 21.4286, (float) 100 ), - new CoordRec((float) 21.4286, (float) 0 ), + new CoordRec((float) 21.4286, 100 ), + new CoordRec((float) 21.4286, 0 ), }; static final CoordRec char69_stroke1[] = { - new CoordRec((float) 21.4286, (float) 100 ), - new CoordRec((float) 83.3334, (float) 100 ), + new CoordRec((float) 21.4286, 100 ), + new CoordRec((float) 83.3334, 100 ), }; static final CoordRec char69_stroke2[] = { @@ -925,8 +925,8 @@ static final CoordRec char69_stroke2[] = { }; static final CoordRec char69_stroke3[] = { - new CoordRec((float) 21.4286, (float) 0 ), - new CoordRec((float) 83.3334, (float) 0 ), + new CoordRec((float) 21.4286, 0 ), + new CoordRec((float) 83.3334, 0 ), }; static final StrokeRec char69[] = { @@ -939,13 +939,13 @@ static final StrokeRec char69[] = { /* char: 70 'F' */ static final CoordRec char70_stroke0[] = { - new CoordRec((float) 21.4286, (float) 100 ), - new CoordRec((float) 21.4286, (float) 0 ), + new CoordRec((float) 21.4286, 100 ), + new CoordRec((float) 21.4286, 0 ), }; static final CoordRec char70_stroke1[] = { - new CoordRec((float) 21.4286, (float) 100 ), - new CoordRec((float) 83.3334, (float) 100 ), + new CoordRec((float) 21.4286, 100 ), + new CoordRec((float) 83.3334, 100 ), }; static final CoordRec char70_stroke2[] = { @@ -965,8 +965,8 @@ static final CoordRec char71_stroke0[] = { new CoordRec((float) 88.0952, (float) 76.1905 ), new CoordRec((float) 83.3334, (float) 85.7143 ), new CoordRec((float) 73.8096, (float) 95.2381 ), - new CoordRec((float) 64.2857, (float) 100 ), - new CoordRec((float) 45.2381, (float) 100 ), + new CoordRec((float) 64.2857, 100 ), + new CoordRec((float) 45.2381, 100 ), new CoordRec((float) 35.7143, (float) 95.2381 ), new CoordRec((float) 26.1905, (float) 85.7143 ), new CoordRec((float) 21.4286, (float) 76.1905 ), @@ -975,8 +975,8 @@ static final CoordRec char71_stroke0[] = { new CoordRec((float) 21.4286, (float) 23.8095 ), new CoordRec((float) 26.1905, (float) 14.2857 ), new CoordRec((float) 35.7143, (float) 4.7619 ), - new CoordRec((float) 45.2381, (float) 0 ), - new CoordRec((float) 64.2857, (float) 0 ), + new CoordRec((float) 45.2381, 0 ), + new CoordRec((float) 64.2857, 0 ), new CoordRec((float) 73.8096, (float) 4.7619 ), new CoordRec((float) 83.3334, (float) 14.2857 ), new CoordRec((float) 88.0952, (float) 23.8095 ), @@ -996,13 +996,13 @@ static final StrokeRec char71[] = { /* char: 72 'H' */ static final CoordRec char72_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char72_stroke1[] = { - new CoordRec((float) 85.7143, (float) 100 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 85.7143, 100 ), + new CoordRec((float) 85.7143, 0 ), }; static final CoordRec char72_stroke2[] = { @@ -1019,8 +1019,8 @@ static final StrokeRec char72[] = { /* char: 73 'I' */ static final CoordRec char73_stroke0[] = { - new CoordRec((float) 52.381, (float) 100 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 100 ), + new CoordRec((float) 52.381, 0 ), }; static final StrokeRec char73[] = { @@ -1030,12 +1030,12 @@ static final StrokeRec char73[] = { /* char: 74 'J' */ static final CoordRec char74_stroke0[] = { - new CoordRec((float) 76.1905, (float) 100 ), + new CoordRec((float) 76.1905, 100 ), new CoordRec((float) 76.1905, (float) 23.8095 ), new CoordRec((float) 71.4286, (float) 9.5238 ), new CoordRec((float) 66.6667, (float) 4.7619 ), - new CoordRec((float) 57.1429, (float) 0 ), - new CoordRec((float) 47.6191, (float) 0 ), + new CoordRec((float) 57.1429, 0 ), + new CoordRec((float) 47.6191, 0 ), new CoordRec((float) 38.0953, (float) 4.7619 ), new CoordRec((float) 33.3334, (float) 9.5238 ), new CoordRec((float) 28.5715, (float) 23.8095 ), @@ -1049,18 +1049,18 @@ static final StrokeRec char74[] = { /* char: 75 'K' */ static final CoordRec char75_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char75_stroke1[] = { - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 85.7143, 100 ), new CoordRec((float) 19.0476, (float) 33.3333 ), }; static final CoordRec char75_stroke2[] = { new CoordRec((float) 42.8571, (float) 57.1429 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 85.7143, 0 ), }; static final StrokeRec char75[] = { @@ -1072,13 +1072,13 @@ static final StrokeRec char75[] = { /* char: 76 'L' */ static final CoordRec char76_stroke0[] = { - new CoordRec((float) 23.8095, (float) 100 ), - new CoordRec((float) 23.8095, (float) 0 ), + new CoordRec((float) 23.8095, 100 ), + new CoordRec((float) 23.8095, 0 ), }; static final CoordRec char76_stroke1[] = { - new CoordRec((float) 23.8095, (float) 0 ), - new CoordRec((float) 80.9524, (float) 0 ), + new CoordRec((float) 23.8095, 0 ), + new CoordRec((float) 80.9524, 0 ), }; static final StrokeRec char76[] = { @@ -1089,23 +1089,23 @@ static final StrokeRec char76[] = { /* char: 77 'M' */ static final CoordRec char77_stroke0[] = { - new CoordRec((float) 14.2857, (float) 100 ), - new CoordRec((float) 14.2857, (float) 0 ), + new CoordRec((float) 14.2857, 100 ), + new CoordRec((float) 14.2857, 0 ), }; static final CoordRec char77_stroke1[] = { - new CoordRec((float) 14.2857, (float) 100 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 14.2857, 100 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char77_stroke2[] = { - new CoordRec((float) 90.4762, (float) 100 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 90.4762, 100 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char77_stroke3[] = { - new CoordRec((float) 90.4762, (float) 100 ), - new CoordRec((float) 90.4762, (float) 0 ), + new CoordRec((float) 90.4762, 100 ), + new CoordRec((float) 90.4762, 0 ), }; static final StrokeRec char77[] = { @@ -1118,18 +1118,18 @@ static final StrokeRec char77[] = { /* char: 78 'N' */ static final CoordRec char78_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char78_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 85.7143, 0 ), }; static final CoordRec char78_stroke2[] = { - new CoordRec((float) 85.7143, (float) 100 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 85.7143, 100 ), + new CoordRec((float) 85.7143, 0 ), }; static final StrokeRec char78[] = { @@ -1141,7 +1141,7 @@ static final StrokeRec char78[] = { /* char: 79 'O' */ static final CoordRec char79_stroke0[] = { - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 33.3333, (float) 95.2381 ), new CoordRec((float) 23.8095, (float) 85.7143 ), new CoordRec((float) 19.0476, (float) 76.1905 ), @@ -1150,8 +1150,8 @@ static final CoordRec char79_stroke0[] = { new CoordRec((float) 19.0476, (float) 23.8095 ), new CoordRec((float) 23.8095, (float) 14.2857 ), new CoordRec((float) 33.3333, (float) 4.7619 ), - new CoordRec((float) 42.8571, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 42.8571, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4286, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 85.7143, (float) 23.8095 ), @@ -1160,8 +1160,8 @@ static final CoordRec char79_stroke0[] = { new CoordRec((float) 85.7143, (float) 76.1905 ), new CoordRec((float) 80.9524, (float) 85.7143 ), new CoordRec((float) 71.4286, (float) 95.2381 ), - new CoordRec((float) 61.9047, (float) 100 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 61.9047, 100 ), + new CoordRec((float) 42.8571, 100 ), }; static final StrokeRec char79[] = { @@ -1171,13 +1171,13 @@ static final StrokeRec char79[] = { /* char: 80 'P' */ static final CoordRec char80_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char80_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 61.9047, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 61.9047, 100 ), new CoordRec((float) 76.1905, (float) 95.2381 ), new CoordRec((float) 80.9524, (float) 90.4762 ), new CoordRec((float) 85.7143, (float) 80.9524 ), @@ -1196,7 +1196,7 @@ static final StrokeRec char80[] = { /* char: 81 'Q' */ static final CoordRec char81_stroke0[] = { - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 33.3333, (float) 95.2381 ), new CoordRec((float) 23.8095, (float) 85.7143 ), new CoordRec((float) 19.0476, (float) 76.1905 ), @@ -1205,8 +1205,8 @@ static final CoordRec char81_stroke0[] = { new CoordRec((float) 19.0476, (float) 23.8095 ), new CoordRec((float) 23.8095, (float) 14.2857 ), new CoordRec((float) 33.3333, (float) 4.7619 ), - new CoordRec((float) 42.8571, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 42.8571, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4286, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 85.7143, (float) 23.8095 ), @@ -1215,8 +1215,8 @@ static final CoordRec char81_stroke0[] = { new CoordRec((float) 85.7143, (float) 76.1905 ), new CoordRec((float) 80.9524, (float) 85.7143 ), new CoordRec((float) 71.4286, (float) 95.2381 ), - new CoordRec((float) 61.9047, (float) 100 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 61.9047, 100 ), + new CoordRec((float) 42.8571, 100 ), }; static final CoordRec char81_stroke1[] = { @@ -1232,13 +1232,13 @@ static final StrokeRec char81[] = { /* char: 82 'R' */ static final CoordRec char82_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char82_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 61.9047, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 61.9047, 100 ), new CoordRec((float) 76.1905, (float) 95.2381 ), new CoordRec((float) 80.9524, (float) 90.4762 ), new CoordRec((float) 85.7143, (float) 80.9524 ), @@ -1251,7 +1251,7 @@ static final CoordRec char82_stroke1[] = { static final CoordRec char82_stroke2[] = { new CoordRec((float) 52.3809, (float) 52.381 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 85.7143, 0 ), }; static final StrokeRec char82[] = { @@ -1265,8 +1265,8 @@ static final StrokeRec char82[] = { static final CoordRec char83_stroke0[] = { new CoordRec((float) 85.7143, (float) 85.7143 ), new CoordRec((float) 76.1905, (float) 95.2381 ), - new CoordRec((float) 61.9047, (float) 100 ), - new CoordRec((float) 42.8571, (float) 100 ), + new CoordRec((float) 61.9047, 100 ), + new CoordRec((float) 42.8571, 100 ), new CoordRec((float) 28.5714, (float) 95.2381 ), new CoordRec((float) 19.0476, (float) 85.7143 ), new CoordRec((float) 19.0476, (float) 76.1905 ), @@ -1279,8 +1279,8 @@ static final CoordRec char83_stroke0[] = { new CoordRec((float) 85.7143, (float) 28.5714 ), new CoordRec((float) 85.7143, (float) 14.2857 ), new CoordRec((float) 76.1905, (float) 4.7619 ), - new CoordRec((float) 61.9047, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 61.9047, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 28.5714, (float) 4.7619 ), new CoordRec((float) 19.0476, (float) 14.2857 ), }; @@ -1292,13 +1292,13 @@ static final StrokeRec char83[] = { /* char: 84 'T' */ static final CoordRec char84_stroke0[] = { - new CoordRec((float) 52.3809, (float) 100 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 100 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char84_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 85.7143, 100 ), }; static final StrokeRec char84[] = { @@ -1309,16 +1309,16 @@ static final StrokeRec char84[] = { /* char: 85 'U' */ static final CoordRec char85_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), new CoordRec((float) 19.0476, (float) 28.5714 ), new CoordRec((float) 23.8095, (float) 14.2857 ), new CoordRec((float) 33.3333, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 57.1428, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 57.1428, 0 ), new CoordRec((float) 71.4286, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 85.7143, (float) 28.5714 ), - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 85.7143, 100 ), }; static final StrokeRec char85[] = { @@ -1328,13 +1328,13 @@ static final StrokeRec char85[] = { /* char: 86 'V' */ static final CoordRec char86_stroke0[] = { - new CoordRec((float) 14.2857, (float) 100 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 14.2857, 100 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char86_stroke1[] = { - new CoordRec((float) 90.4762, (float) 100 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 90.4762, 100 ), + new CoordRec((float) 52.3809, 0 ), }; static final StrokeRec char86[] = { @@ -1345,23 +1345,23 @@ static final StrokeRec char86[] = { /* char: 87 'W' */ static final CoordRec char87_stroke0[] = { - new CoordRec((float) 4.7619, (float) 100 ), - new CoordRec((float) 28.5714, (float) 0 ), + new CoordRec((float) 4.7619, 100 ), + new CoordRec((float) 28.5714, 0 ), }; static final CoordRec char87_stroke1[] = { - new CoordRec((float) 52.3809, (float) 100 ), - new CoordRec((float) 28.5714, (float) 0 ), + new CoordRec((float) 52.3809, 100 ), + new CoordRec((float) 28.5714, 0 ), }; static final CoordRec char87_stroke2[] = { - new CoordRec((float) 52.3809, (float) 100 ), - new CoordRec((float) 76.1905, (float) 0 ), + new CoordRec((float) 52.3809, 100 ), + new CoordRec((float) 76.1905, 0 ), }; static final CoordRec char87_stroke3[] = { - new CoordRec((float) 100, (float) 100 ), - new CoordRec((float) 76.1905, (float) 0 ), + new CoordRec(100, 100 ), + new CoordRec((float) 76.1905, 0 ), }; static final StrokeRec char87[] = { @@ -1374,13 +1374,13 @@ static final StrokeRec char87[] = { /* char: 88 'X' */ static final CoordRec char88_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 85.7143, 0 ), }; static final CoordRec char88_stroke1[] = { - new CoordRec((float) 85.7143, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 85.7143, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final StrokeRec char88[] = { @@ -1391,13 +1391,13 @@ static final StrokeRec char88[] = { /* char: 89 'Y' */ static final CoordRec char89_stroke0[] = { - new CoordRec((float) 14.2857, (float) 100 ), + new CoordRec((float) 14.2857, 100 ), new CoordRec((float) 52.3809, (float) 52.381 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char89_stroke1[] = { - new CoordRec((float) 90.4762, (float) 100 ), + new CoordRec((float) 90.4762, 100 ), new CoordRec((float) 52.3809, (float) 52.381 ), }; @@ -1409,18 +1409,18 @@ static final StrokeRec char89[] = { /* char: 90 'Z' */ static final CoordRec char90_stroke0[] = { - new CoordRec((float) 85.7143, (float) 100 ), - new CoordRec((float) 19.0476, (float) 0 ), + new CoordRec((float) 85.7143, 100 ), + new CoordRec((float) 19.0476, 0 ), }; static final CoordRec char90_stroke1[] = { - new CoordRec((float) 19.0476, (float) 100 ), - new CoordRec((float) 85.7143, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), + new CoordRec((float) 85.7143, 100 ), }; static final CoordRec char90_stroke2[] = { - new CoordRec((float) 19.0476, (float) 0 ), - new CoordRec((float) 85.7143, (float) 0 ), + new CoordRec((float) 19.0476, 0 ), + new CoordRec((float) 85.7143, 0 ), }; static final StrokeRec char90[] = { @@ -1461,7 +1461,7 @@ static final StrokeRec char91[] = { /* char: 92 '\' */ static final CoordRec char92_stroke0[] = { - new CoordRec((float) 19.0476, (float) 100 ), + new CoordRec((float) 19.0476, 100 ), new CoordRec((float) 85.7143, (float) -14.2857 ), }; @@ -1518,11 +1518,11 @@ static final StrokeRec char94[] = { /* char: 95 '_' */ static final CoordRec char95_stroke0[] = { - new CoordRec((float) 0, (float) -33.3333 ), + new CoordRec(0, (float) -33.3333 ), new CoordRec((float) 104.762, (float) -33.3333 ), new CoordRec((float) 104.762, (float) -28.5714 ), - new CoordRec((float) 0, (float) -28.5714 ), - new CoordRec((float) 0, (float) -33.3333 ), + new CoordRec(0, (float) -28.5714 ), + new CoordRec(0, (float) -33.3333 ), }; static final StrokeRec char95[] = { @@ -1532,12 +1532,12 @@ static final StrokeRec char95[] = { /* char: 96 '`' */ static final CoordRec char96_stroke0[] = { - new CoordRec((float) 42.8572, (float) 100 ), + new CoordRec((float) 42.8572, 100 ), new CoordRec((float) 66.6667, (float) 71.4286 ), }; static final CoordRec char96_stroke1[] = { - new CoordRec((float) 42.8572, (float) 100 ), + new CoordRec((float) 42.8572, 100 ), new CoordRec((float) 38.0953, (float) 95.2381 ), new CoordRec((float) 66.6667, (float) 71.4286 ), }; @@ -1551,7 +1551,7 @@ static final StrokeRec char96[] = { static final CoordRec char97_stroke0[] = { new CoordRec((float) 80.9524, (float) 66.6667 ), - new CoordRec((float) 80.9524, (float) 0 ), + new CoordRec((float) 80.9524, 0 ), }; static final CoordRec char97_stroke1[] = { @@ -1565,8 +1565,8 @@ static final CoordRec char97_stroke1[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1579,8 +1579,8 @@ static final StrokeRec char97[] = { /* char: 98 'b' */ static final CoordRec char98_stroke0[] = { - new CoordRec((float) 23.8095, (float) 100 ), - new CoordRec((float) 23.8095, (float) 0 ), + new CoordRec((float) 23.8095, 100 ), + new CoordRec((float) 23.8095, 0 ), }; static final CoordRec char98_stroke1[] = { @@ -1594,8 +1594,8 @@ static final CoordRec char98_stroke1[] = { new CoordRec((float) 80.9524, (float) 28.5714 ), new CoordRec((float) 76.1905, (float) 14.2857 ), new CoordRec((float) 66.6666, (float) 4.7619 ), - new CoordRec((float) 57.1428, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 57.1428, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 33.3333, (float) 4.7619 ), new CoordRec((float) 23.8095, (float) 14.2857 ), }; @@ -1618,8 +1618,8 @@ static final CoordRec char99_stroke0[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1631,8 +1631,8 @@ static final StrokeRec char99[] = { /* char: 100 'd' */ static final CoordRec char100_stroke0[] = { - new CoordRec((float) 80.9524, (float) 100 ), - new CoordRec((float) 80.9524, (float) 0 ), + new CoordRec((float) 80.9524, 100 ), + new CoordRec((float) 80.9524, 0 ), }; static final CoordRec char100_stroke1[] = { @@ -1646,8 +1646,8 @@ static final CoordRec char100_stroke1[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1673,8 +1673,8 @@ static final CoordRec char101_stroke0[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1686,11 +1686,11 @@ static final StrokeRec char101[] = { /* char: 102 'f' */ static final CoordRec char102_stroke0[] = { - new CoordRec((float) 71.4286, (float) 100 ), - new CoordRec((float) 61.9048, (float) 100 ), + new CoordRec((float) 71.4286, 100 ), + new CoordRec((float) 61.9048, 100 ), new CoordRec((float) 52.381, (float) 95.2381 ), new CoordRec((float) 47.6191, (float) 80.9524 ), - new CoordRec((float) 47.6191, (float) 0 ), + new CoordRec((float) 47.6191, 0 ), }; static final CoordRec char102_stroke1[] = { @@ -1726,8 +1726,8 @@ static final CoordRec char103_stroke1[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1740,18 +1740,18 @@ static final StrokeRec char103[] = { /* char: 104 'h' */ static final CoordRec char104_stroke0[] = { - new CoordRec((float) 26.1905, (float) 100 ), - new CoordRec((float) 26.1905, (float) 0 ), + new CoordRec((float) 26.1905, 100 ), + new CoordRec((float) 26.1905, 0 ), }; static final CoordRec char104_stroke1[] = { new CoordRec((float) 26.1905, (float) 47.619 ), new CoordRec((float) 40.4762, (float) 61.9048 ), - new CoordRec((float) 50, (float) 66.6667 ), + new CoordRec(50, (float) 66.6667 ), new CoordRec((float) 64.2857, (float) 66.6667 ), new CoordRec((float) 73.8095, (float) 61.9048 ), new CoordRec((float) 78.5715, (float) 47.619 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final StrokeRec char104[] = { @@ -1762,16 +1762,16 @@ static final StrokeRec char104[] = { /* char: 105 'i' */ static final CoordRec char105_stroke0[] = { - new CoordRec((float) 47.6191, (float) 100 ), + new CoordRec((float) 47.6191, 100 ), new CoordRec((float) 52.381, (float) 95.2381 ), - new CoordRec((float) 57.1429, (float) 100 ), + new CoordRec((float) 57.1429, 100 ), new CoordRec((float) 52.381, (float) 104.762 ), - new CoordRec((float) 47.6191, (float) 100 ), + new CoordRec((float) 47.6191, 100 ), }; static final CoordRec char105_stroke1[] = { new CoordRec((float) 52.381, (float) 66.6667 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), }; static final StrokeRec char105[] = { @@ -1782,11 +1782,11 @@ static final StrokeRec char105[] = { /* char: 106 'j' */ static final CoordRec char106_stroke0[] = { - new CoordRec((float) 57.1429, (float) 100 ), + new CoordRec((float) 57.1429, 100 ), new CoordRec((float) 61.9048, (float) 95.2381 ), - new CoordRec((float) 66.6667, (float) 100 ), + new CoordRec((float) 66.6667, 100 ), new CoordRec((float) 61.9048, (float) 104.762 ), - new CoordRec((float) 57.1429, (float) 100 ), + new CoordRec((float) 57.1429, 100 ), }; static final CoordRec char106_stroke1[] = { @@ -1805,8 +1805,8 @@ static final StrokeRec char106[] = { /* char: 107 'k' */ static final CoordRec char107_stroke0[] = { - new CoordRec((float) 26.1905, (float) 100 ), - new CoordRec((float) 26.1905, (float) 0 ), + new CoordRec((float) 26.1905, 100 ), + new CoordRec((float) 26.1905, 0 ), }; static final CoordRec char107_stroke1[] = { @@ -1816,7 +1816,7 @@ static final CoordRec char107_stroke1[] = { static final CoordRec char107_stroke2[] = { new CoordRec((float) 45.2381, (float) 38.0952 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final StrokeRec char107[] = { @@ -1828,8 +1828,8 @@ static final StrokeRec char107[] = { /* char: 108 'l' */ static final CoordRec char108_stroke0[] = { - new CoordRec((float) 52.381, (float) 100 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 100 ), + new CoordRec((float) 52.381, 0 ), }; static final StrokeRec char108[] = { @@ -1839,18 +1839,18 @@ static final StrokeRec char108[] = { /* char: 109 'm' */ static final CoordRec char109_stroke0[] = { - new CoordRec((float) 0, (float) 66.6667 ), - new CoordRec((float) 0, (float) 0 ), + new CoordRec(0, (float) 66.6667 ), + new CoordRec(0, 0 ), }; static final CoordRec char109_stroke1[] = { - new CoordRec((float) 0, (float) 47.619 ), + new CoordRec(0, (float) 47.619 ), new CoordRec((float) 14.2857, (float) 61.9048 ), new CoordRec((float) 23.8095, (float) 66.6667 ), new CoordRec((float) 38.0952, (float) 66.6667 ), new CoordRec((float) 47.619, (float) 61.9048 ), new CoordRec((float) 52.381, (float) 47.619 ), - new CoordRec((float) 52.381, (float) 0 ), + new CoordRec((float) 52.381, 0 ), }; static final CoordRec char109_stroke2[] = { @@ -1858,9 +1858,9 @@ static final CoordRec char109_stroke2[] = { new CoordRec((float) 66.6667, (float) 61.9048 ), new CoordRec((float) 76.1905, (float) 66.6667 ), new CoordRec((float) 90.4762, (float) 66.6667 ), - new CoordRec((float) 100, (float) 61.9048 ), + new CoordRec(100, (float) 61.9048 ), new CoordRec((float) 104.762, (float) 47.619 ), - new CoordRec((float) 104.762, (float) 0 ), + new CoordRec((float) 104.762, 0 ), }; static final StrokeRec char109[] = { @@ -1873,17 +1873,17 @@ static final StrokeRec char109[] = { static final CoordRec char110_stroke0[] = { new CoordRec((float) 26.1905, (float) 66.6667 ), - new CoordRec((float) 26.1905, (float) 0 ), + new CoordRec((float) 26.1905, 0 ), }; static final CoordRec char110_stroke1[] = { new CoordRec((float) 26.1905, (float) 47.619 ), new CoordRec((float) 40.4762, (float) 61.9048 ), - new CoordRec((float) 50, (float) 66.6667 ), + new CoordRec(50, (float) 66.6667 ), new CoordRec((float) 64.2857, (float) 66.6667 ), new CoordRec((float) 73.8095, (float) 61.9048 ), new CoordRec((float) 78.5715, (float) 47.619 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final StrokeRec char110[] = { @@ -1901,8 +1901,8 @@ static final CoordRec char111_stroke0[] = { new CoordRec((float) 21.4286, (float) 28.5714 ), new CoordRec((float) 26.1905, (float) 14.2857 ), new CoordRec((float) 35.7143, (float) 4.7619 ), - new CoordRec((float) 45.2381, (float) 0 ), - new CoordRec((float) 59.5238, (float) 0 ), + new CoordRec((float) 45.2381, 0 ), + new CoordRec((float) 59.5238, 0 ), new CoordRec((float) 69.0476, (float) 4.7619 ), new CoordRec((float) 78.5714, (float) 14.2857 ), new CoordRec((float) 83.3334, (float) 28.5714 ), @@ -1935,8 +1935,8 @@ static final CoordRec char112_stroke1[] = { new CoordRec((float) 80.9524, (float) 28.5714 ), new CoordRec((float) 76.1905, (float) 14.2857 ), new CoordRec((float) 66.6666, (float) 4.7619 ), - new CoordRec((float) 57.1428, (float) 0 ), - new CoordRec((float) 42.8571, (float) 0 ), + new CoordRec((float) 57.1428, 0 ), + new CoordRec((float) 42.8571, 0 ), new CoordRec((float) 33.3333, (float) 4.7619 ), new CoordRec((float) 23.8095, (float) 14.2857 ), }; @@ -1964,8 +1964,8 @@ static final CoordRec char113_stroke1[] = { new CoordRec((float) 23.8095, (float) 28.5714 ), new CoordRec((float) 28.5714, (float) 14.2857 ), new CoordRec((float) 38.0952, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 61.9047, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 61.9047, 0 ), new CoordRec((float) 71.4285, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), }; @@ -1979,7 +1979,7 @@ static final StrokeRec char113[] = { static final CoordRec char114_stroke0[] = { new CoordRec((float) 33.3334, (float) 66.6667 ), - new CoordRec((float) 33.3334, (float) 0 ), + new CoordRec((float) 33.3334, 0 ), }; static final CoordRec char114_stroke1[] = { @@ -2011,8 +2011,8 @@ static final CoordRec char115_stroke0[] = { new CoordRec((float) 78.5715, (float) 19.0476 ), new CoordRec((float) 78.5715, (float) 14.2857 ), new CoordRec((float) 73.8095, (float) 4.7619 ), - new CoordRec((float) 59.5238, (float) 0 ), - new CoordRec((float) 45.2381, (float) 0 ), + new CoordRec((float) 59.5238, 0 ), + new CoordRec((float) 45.2381, 0 ), new CoordRec((float) 30.9524, (float) 4.7619 ), new CoordRec((float) 26.1905, (float) 14.2857 ), }; @@ -2024,11 +2024,11 @@ static final StrokeRec char115[] = { /* char: 116 't' */ static final CoordRec char116_stroke0[] = { - new CoordRec((float) 47.6191, (float) 100 ), + new CoordRec((float) 47.6191, 100 ), new CoordRec((float) 47.6191, (float) 19.0476 ), new CoordRec((float) 52.381, (float) 4.7619 ), - new CoordRec((float) 61.9048, (float) 0 ), - new CoordRec((float) 71.4286, (float) 0 ), + new CoordRec((float) 61.9048, 0 ), + new CoordRec((float) 71.4286, 0 ), }; static final CoordRec char116_stroke1[] = { @@ -2047,15 +2047,15 @@ static final CoordRec char117_stroke0[] = { new CoordRec((float) 26.1905, (float) 66.6667 ), new CoordRec((float) 26.1905, (float) 19.0476 ), new CoordRec((float) 30.9524, (float) 4.7619 ), - new CoordRec((float) 40.4762, (float) 0 ), - new CoordRec((float) 54.7619, (float) 0 ), + new CoordRec((float) 40.4762, 0 ), + new CoordRec((float) 54.7619, 0 ), new CoordRec((float) 64.2857, (float) 4.7619 ), new CoordRec((float) 78.5715, (float) 19.0476 ), }; static final CoordRec char117_stroke1[] = { new CoordRec((float) 78.5715, (float) 66.6667 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final StrokeRec char117[] = { @@ -2067,12 +2067,12 @@ static final StrokeRec char117[] = { static final CoordRec char118_stroke0[] = { new CoordRec((float) 23.8095, (float) 66.6667 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), }; static final CoordRec char118_stroke1[] = { new CoordRec((float) 80.9524, (float) 66.6667 ), - new CoordRec((float) 52.3809, (float) 0 ), + new CoordRec((float) 52.3809, 0 ), }; static final StrokeRec char118[] = { @@ -2084,22 +2084,22 @@ static final StrokeRec char118[] = { static final CoordRec char119_stroke0[] = { new CoordRec((float) 14.2857, (float) 66.6667 ), - new CoordRec((float) 33.3333, (float) 0 ), + new CoordRec((float) 33.3333, 0 ), }; static final CoordRec char119_stroke1[] = { new CoordRec((float) 52.3809, (float) 66.6667 ), - new CoordRec((float) 33.3333, (float) 0 ), + new CoordRec((float) 33.3333, 0 ), }; static final CoordRec char119_stroke2[] = { new CoordRec((float) 52.3809, (float) 66.6667 ), - new CoordRec((float) 71.4286, (float) 0 ), + new CoordRec((float) 71.4286, 0 ), }; static final CoordRec char119_stroke3[] = { new CoordRec((float) 90.4762, (float) 66.6667 ), - new CoordRec((float) 71.4286, (float) 0 ), + new CoordRec((float) 71.4286, 0 ), }; static final StrokeRec char119[] = { @@ -2113,12 +2113,12 @@ static final StrokeRec char119[] = { static final CoordRec char120_stroke0[] = { new CoordRec((float) 26.1905, (float) 66.6667 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final CoordRec char120_stroke1[] = { new CoordRec((float) 78.5715, (float) 66.6667 ), - new CoordRec((float) 26.1905, (float) 0 ), + new CoordRec((float) 26.1905, 0 ), }; static final StrokeRec char120[] = { @@ -2130,12 +2130,12 @@ static final StrokeRec char120[] = { static final CoordRec char121_stroke0[] = { new CoordRec((float) 26.1905, (float) 66.6667 ), - new CoordRec((float) 54.7619, (float) 0 ), + new CoordRec((float) 54.7619, 0 ), }; static final CoordRec char121_stroke1[] = { new CoordRec((float) 83.3334, (float) 66.6667 ), - new CoordRec((float) 54.7619, (float) 0 ), + new CoordRec((float) 54.7619, 0 ), new CoordRec((float) 45.2381, (float) -19.0476 ), new CoordRec((float) 35.7143, (float) -28.5714 ), new CoordRec((float) 26.1905, (float) -33.3333 ), @@ -2151,7 +2151,7 @@ static final StrokeRec char121[] = { static final CoordRec char122_stroke0[] = { new CoordRec((float) 78.5715, (float) 66.6667 ), - new CoordRec((float) 26.1905, (float) 0 ), + new CoordRec((float) 26.1905, 0 ), }; static final CoordRec char122_stroke1[] = { @@ -2160,8 +2160,8 @@ static final CoordRec char122_stroke1[] = { }; static final CoordRec char122_stroke2[] = { - new CoordRec((float) 26.1905, (float) 0 ), - new CoordRec((float) 78.5715, (float) 0 ), + new CoordRec((float) 26.1905, 0 ), + new CoordRec((float) 78.5715, 0 ), }; static final StrokeRec char122[] = { @@ -2175,20 +2175,20 @@ static final StrokeRec char122[] = { static final CoordRec char123_stroke0[] = { new CoordRec((float) 64.2857, (float) 119.048 ), new CoordRec((float) 54.7619, (float) 114.286 ), - new CoordRec((float) 50, (float) 109.524 ), - new CoordRec((float) 45.2381, (float) 100 ), + new CoordRec(50, (float) 109.524 ), + new CoordRec((float) 45.2381, 100 ), new CoordRec((float) 45.2381, (float) 90.4762 ), - new CoordRec((float) 50, (float) 80.9524 ), + new CoordRec(50, (float) 80.9524 ), new CoordRec((float) 54.7619, (float) 76.1905 ), new CoordRec((float) 59.5238, (float) 66.6667 ), new CoordRec((float) 59.5238, (float) 57.1429 ), - new CoordRec((float) 50, (float) 47.619 ), + new CoordRec(50, (float) 47.619 ), }; static final CoordRec char123_stroke1[] = { new CoordRec((float) 54.7619, (float) 114.286 ), - new CoordRec((float) 50, (float) 104.762 ), - new CoordRec((float) 50, (float) 95.2381 ), + new CoordRec(50, (float) 104.762 ), + new CoordRec(50, (float) 95.2381 ), new CoordRec((float) 54.7619, (float) 85.7143 ), new CoordRec((float) 59.5238, (float) 80.9524 ), new CoordRec((float) 64.2857, (float) 71.4286 ), @@ -2199,21 +2199,21 @@ static final CoordRec char123_stroke1[] = { new CoordRec((float) 64.2857, (float) 23.8095 ), new CoordRec((float) 64.2857, (float) 14.2857 ), new CoordRec((float) 59.5238, (float) 4.7619 ), - new CoordRec((float) 54.7619, (float) 0 ), - new CoordRec((float) 50, (float) -9.5238 ), - new CoordRec((float) 50, (float) -19.0476 ), + new CoordRec((float) 54.7619, 0 ), + new CoordRec(50, (float) -9.5238 ), + new CoordRec(50, (float) -19.0476 ), new CoordRec((float) 54.7619, (float) -28.5714 ), }; static final CoordRec char123_stroke2[] = { - new CoordRec((float) 50, (float) 38.0952 ), + new CoordRec(50, (float) 38.0952 ), new CoordRec((float) 59.5238, (float) 28.5714 ), new CoordRec((float) 59.5238, (float) 19.0476 ), new CoordRec((float) 54.7619, (float) 9.5238 ), - new CoordRec((float) 50, (float) 4.7619 ), + new CoordRec(50, (float) 4.7619 ), new CoordRec((float) 45.2381, (float) -4.7619 ), new CoordRec((float) 45.2381, (float) -14.2857 ), - new CoordRec((float) 50, (float) -23.8095 ), + new CoordRec(50, (float) -23.8095 ), new CoordRec((float) 54.7619, (float) -28.5714 ), new CoordRec((float) 64.2857, (float) -33.3333 ), }; @@ -2239,22 +2239,22 @@ static final StrokeRec char124[] = { static final CoordRec char125_stroke0[] = { new CoordRec((float) 40.4762, (float) 119.048 ), - new CoordRec((float) 50, (float) 114.286 ), + new CoordRec(50, (float) 114.286 ), new CoordRec((float) 54.7619, (float) 109.524 ), - new CoordRec((float) 59.5238, (float) 100 ), + new CoordRec((float) 59.5238, 100 ), new CoordRec((float) 59.5238, (float) 90.4762 ), new CoordRec((float) 54.7619, (float) 80.9524 ), - new CoordRec((float) 50, (float) 76.1905 ), + new CoordRec(50, (float) 76.1905 ), new CoordRec((float) 45.2381, (float) 66.6667 ), new CoordRec((float) 45.2381, (float) 57.1429 ), new CoordRec((float) 54.7619, (float) 47.619 ), }; static final CoordRec char125_stroke1[] = { - new CoordRec((float) 50, (float) 114.286 ), + new CoordRec(50, (float) 114.286 ), new CoordRec((float) 54.7619, (float) 104.762 ), new CoordRec((float) 54.7619, (float) 95.2381 ), - new CoordRec((float) 50, (float) 85.7143 ), + new CoordRec(50, (float) 85.7143 ), new CoordRec((float) 45.2381, (float) 80.9524 ), new CoordRec((float) 40.4762, (float) 71.4286 ), new CoordRec((float) 40.4762, (float) 61.9048 ), @@ -2264,22 +2264,22 @@ static final CoordRec char125_stroke1[] = { new CoordRec((float) 40.4762, (float) 23.8095 ), new CoordRec((float) 40.4762, (float) 14.2857 ), new CoordRec((float) 45.2381, (float) 4.7619 ), - new CoordRec((float) 50, (float) 0 ), + new CoordRec(50, 0 ), new CoordRec((float) 54.7619, (float) -9.5238 ), new CoordRec((float) 54.7619, (float) -19.0476 ), - new CoordRec((float) 50, (float) -28.5714 ), + new CoordRec(50, (float) -28.5714 ), }; static final CoordRec char125_stroke2[] = { new CoordRec((float) 54.7619, (float) 38.0952 ), new CoordRec((float) 45.2381, (float) 28.5714 ), new CoordRec((float) 45.2381, (float) 19.0476 ), - new CoordRec((float) 50, (float) 9.5238 ), + new CoordRec(50, (float) 9.5238 ), new CoordRec((float) 54.7619, (float) 4.7619 ), new CoordRec((float) 59.5238, (float) -4.7619 ), new CoordRec((float) 59.5238, (float) -14.2857 ), new CoordRec((float) 54.7619, (float) -23.8095 ), - new CoordRec((float) 50, (float) -28.5714 ), + new CoordRec(50, (float) -28.5714 ), new CoordRec((float) 40.4762, (float) -33.3333 ), }; @@ -2327,7 +2327,7 @@ static final StrokeRec char126[] = { /* char: 127 */ static final CoordRec char127_stroke0[] = { - new CoordRec((float) 71.4286, (float) 100 ), + new CoordRec((float) 71.4286, 100 ), new CoordRec((float) 33.3333, (float) -33.3333 ), }; @@ -2339,8 +2339,8 @@ static final CoordRec char127_stroke1[] = { new CoordRec((float) 19.0476, (float) 23.8095 ), new CoordRec((float) 23.8095, (float) 14.2857 ), new CoordRec((float) 33.3333, (float) 4.7619 ), - new CoordRec((float) 47.619, (float) 0 ), - new CoordRec((float) 57.1428, (float) 0 ), + new CoordRec((float) 47.619, 0 ), + new CoordRec((float) 57.1428, 0 ), new CoordRec((float) 71.4286, (float) 4.7619 ), new CoordRec((float) 80.9524, (float) 14.2857 ), new CoordRec((float) 85.7143, (float) 28.5714 ), @@ -2357,38 +2357,38 @@ static final StrokeRec char127[] = { }; static final StrokeCharRec chars[] = { - new StrokeCharRec(0, /* char0 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char1 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char2 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char3 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char4 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char5 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char6 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char7 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char8 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char9 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char10 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char11 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char12 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char13 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char14 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char15 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char16 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char17 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char18 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char19 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char20 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char21 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char22 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char23 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char24 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char25 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char26 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char27 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char28 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char29 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char30 */ null, (float) 0, (float) 0 ), - new StrokeCharRec(0, /* char31 */ null, (float) 0, (float) 0 ), + new StrokeCharRec(0, /* char0 */ null, 0, 0 ), + new StrokeCharRec(0, /* char1 */ null, 0, 0 ), + new StrokeCharRec(0, /* char2 */ null, 0, 0 ), + new StrokeCharRec(0, /* char3 */ null, 0, 0 ), + new StrokeCharRec(0, /* char4 */ null, 0, 0 ), + new StrokeCharRec(0, /* char5 */ null, 0, 0 ), + new StrokeCharRec(0, /* char6 */ null, 0, 0 ), + new StrokeCharRec(0, /* char7 */ null, 0, 0 ), + new StrokeCharRec(0, /* char8 */ null, 0, 0 ), + new StrokeCharRec(0, /* char9 */ null, 0, 0 ), + new StrokeCharRec(0, /* char10 */ null, 0, 0 ), + new StrokeCharRec(0, /* char11 */ null, 0, 0 ), + new StrokeCharRec(0, /* char12 */ null, 0, 0 ), + new StrokeCharRec(0, /* char13 */ null, 0, 0 ), + new StrokeCharRec(0, /* char14 */ null, 0, 0 ), + new StrokeCharRec(0, /* char15 */ null, 0, 0 ), + new StrokeCharRec(0, /* char16 */ null, 0, 0 ), + new StrokeCharRec(0, /* char17 */ null, 0, 0 ), + new StrokeCharRec(0, /* char18 */ null, 0, 0 ), + new StrokeCharRec(0, /* char19 */ null, 0, 0 ), + new StrokeCharRec(0, /* char20 */ null, 0, 0 ), + new StrokeCharRec(0, /* char21 */ null, 0, 0 ), + new StrokeCharRec(0, /* char22 */ null, 0, 0 ), + new StrokeCharRec(0, /* char23 */ null, 0, 0 ), + new StrokeCharRec(0, /* char24 */ null, 0, 0 ), + new StrokeCharRec(0, /* char25 */ null, 0, 0 ), + new StrokeCharRec(0, /* char26 */ null, 0, 0 ), + new StrokeCharRec(0, /* char27 */ null, 0, 0 ), + new StrokeCharRec(0, /* char28 */ null, 0, 0 ), + new StrokeCharRec(0, /* char29 */ null, 0, 0 ), + new StrokeCharRec(0, /* char30 */ null, 0, 0 ), + new StrokeCharRec(0, /* char31 */ null, 0, 0 ), new StrokeCharRec(0, /* char32 */ null, (float) 52.381, (float) 104.762 ), new StrokeCharRec(2, char33, (float) 52.381, (float) 104.762 ), new StrokeCharRec(2, char34, (float) 52.381, (float) 104.762 ), diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeRoman.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeRoman.java index 94fa1c4fd..5b614c64e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeRoman.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/GLUTStrokeRoman.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -46,14 +46,14 @@ class GLUTStrokeRoman { /* char: 33 '!' */ static final CoordRec char33_stroke0[] = { - new CoordRec((float) 13.3819, (float) 100), + new CoordRec((float) 13.3819, 100), new CoordRec((float) 13.3819, (float) 33.3333), }; static final CoordRec char33_stroke1[] = { new CoordRec((float) 13.3819, (float) 9.5238), new CoordRec((float) 8.62, (float) 4.7619), - new CoordRec((float) 13.3819, (float) 0), + new CoordRec((float) 13.3819, 0), new CoordRec((float) 18.1438, (float) 4.7619), new CoordRec((float) 13.3819, (float) 9.5238), }; @@ -66,12 +66,12 @@ static final StrokeRec char33[] = { /* char: 34 '"' */ static final CoordRec char34_stroke0[] = { - new CoordRec((float) 4.02, (float) 100), + new CoordRec((float) 4.02, 100), new CoordRec((float) 4.02, (float) 66.6667), }; static final CoordRec char34_stroke1[] = { - new CoordRec((float) 42.1152, (float) 100), + new CoordRec((float) 42.1152, 100), new CoordRec((float) 42.1152, (float) 66.6667), }; @@ -124,8 +124,8 @@ static final CoordRec char36_stroke1[] = { static final CoordRec char36_stroke2[] = { new CoordRec((float) 71.4867, (float) 85.7143), new CoordRec((float) 61.9629, (float) 95.2381), - new CoordRec((float) 47.6771, (float) 100), - new CoordRec((float) 28.6295, (float) 100), + new CoordRec((float) 47.6771, 100), + new CoordRec((float) 28.6295, 100), new CoordRec((float) 14.3438, (float) 95.2381), new CoordRec((float) 4.82, (float) 85.7143), new CoordRec((float) 4.82, (float) 76.1905), @@ -138,8 +138,8 @@ static final CoordRec char36_stroke2[] = { new CoordRec((float) 71.4867, (float) 28.5714), new CoordRec((float) 71.4867, (float) 14.2857), new CoordRec((float) 61.9629, (float) 4.7619), - new CoordRec((float) 47.6771, (float) 0), - new CoordRec((float) 28.6295, (float) 0), + new CoordRec((float) 47.6771, 0), + new CoordRec((float) 28.6295, 0), new CoordRec((float) 14.3438, (float) 4.7619), new CoordRec((float) 4.82, (float) 14.2857), }; @@ -153,12 +153,12 @@ static final StrokeRec char36[] = { /* char: 37 '%' */ static final CoordRec char37_stroke0[] = { - new CoordRec((float) 92.0743, (float) 100), - new CoordRec((float) 6.36, (float) 0), + new CoordRec((float) 92.0743, 100), + new CoordRec((float) 6.36, 0), }; static final CoordRec char37_stroke1[] = { - new CoordRec((float) 30.1695, (float) 100), + new CoordRec((float) 30.1695, 100), new CoordRec((float) 39.6933, (float) 90.4762), new CoordRec((float) 39.6933, (float) 80.9524), new CoordRec((float) 34.9314, (float) 71.4286), @@ -167,13 +167,13 @@ static final CoordRec char37_stroke1[] = { new CoordRec((float) 6.36, (float) 76.1905), new CoordRec((float) 6.36, (float) 85.7143), new CoordRec((float) 11.1219, (float) 95.2381), - new CoordRec((float) 20.6457, (float) 100), - new CoordRec((float) 30.1695, (float) 100), + new CoordRec((float) 20.6457, 100), + new CoordRec((float) 30.1695, 100), new CoordRec((float) 39.6933, (float) 95.2381), new CoordRec((float) 53.979, (float) 90.4762), new CoordRec((float) 68.2648, (float) 90.4762), new CoordRec((float) 82.5505, (float) 95.2381), - new CoordRec((float) 92.0743, (float) 100), + new CoordRec((float) 92.0743, 100), }; static final CoordRec char37_stroke2[] = { @@ -181,8 +181,8 @@ static final CoordRec char37_stroke2[] = { new CoordRec((float) 63.5029, (float) 28.5714), new CoordRec((float) 58.741, (float) 19.0476), new CoordRec((float) 58.741, (float) 9.5238), - new CoordRec((float) 68.2648, (float) 0), - new CoordRec((float) 77.7886, (float) 0), + new CoordRec((float) 68.2648, 0), + new CoordRec((float) 77.7886, 0), new CoordRec((float) 87.3124, (float) 4.7619), new CoordRec((float) 92.0743, (float) 14.2857), new CoordRec((float) 92.0743, (float) 23.8095), @@ -208,8 +208,8 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 72.6467, (float) 28.5714), new CoordRec((float) 63.1229, (float) 14.2857), new CoordRec((float) 53.599, (float) 4.7619), - new CoordRec((float) 44.0752, (float) 0), - new CoordRec((float) 25.0276, (float) 0), + new CoordRec((float) 44.0752, 0), + new CoordRec((float) 25.0276, 0), new CoordRec((float) 15.5038, (float) 4.7619), new CoordRec((float) 10.7419, (float) 9.5238), new CoordRec((float) 5.98, (float) 19.0476), @@ -221,7 +221,7 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 58.361, (float) 76.1905), new CoordRec((float) 58.361, (float) 85.7143), new CoordRec((float) 53.599, (float) 95.2381), - new CoordRec((float) 44.0752, (float) 100), + new CoordRec((float) 44.0752, 100), new CoordRec((float) 34.5514, (float) 95.2381), new CoordRec((float) 29.7895, (float) 85.7143), new CoordRec((float) 29.7895, (float) 76.1905), @@ -229,8 +229,8 @@ static final CoordRec char38_stroke0[] = { new CoordRec((float) 44.0752, (float) 47.619), new CoordRec((float) 67.8848, (float) 14.2857), new CoordRec((float) 77.4086, (float) 4.7619), - new CoordRec((float) 86.9324, (float) 0), - new CoordRec((float) 96.4562, (float) 0), + new CoordRec((float) 86.9324, 0), + new CoordRec((float) 96.4562, 0), new CoordRec((float) 101.218, (float) 4.7619), new CoordRec((float) 101.218, (float) 9.5238), }; @@ -242,7 +242,7 @@ static final StrokeRec char38[] = { /* char: 39 ''' */ static final CoordRec char39_stroke0[] = { - new CoordRec((float) 4.44, (float) 100), + new CoordRec((float) 4.44, 100), new CoordRec((float) 4.44, (float) 66.6667), }; @@ -315,7 +315,7 @@ static final StrokeRec char42[] = { static final CoordRec char43_stroke0[] = { new CoordRec((float) 48.8371, (float) 85.7143), - new CoordRec((float) 48.8371, (float) 0), + new CoordRec((float) 48.8371, 0), }; static final CoordRec char43_stroke1[] = { @@ -332,7 +332,7 @@ static final StrokeRec char43[] = { static final CoordRec char44_stroke0[] = { new CoordRec((float) 18.2838, (float) 4.7619), - new CoordRec((float) 13.5219, (float) 0), + new CoordRec((float) 13.5219, 0), new CoordRec((float) 8.76, (float) 4.7619), new CoordRec((float) 13.5219, (float) 9.5238), new CoordRec((float) 18.2838, (float) 4.7619), @@ -361,7 +361,7 @@ static final StrokeRec char45[] = { static final CoordRec char46_stroke0[] = { new CoordRec((float) 13.1019, (float) 9.5238), new CoordRec((float) 8.34, (float) 4.7619), - new CoordRec((float) 13.1019, (float) 0), + new CoordRec((float) 13.1019, 0), new CoordRec((float) 17.8638, (float) 4.7619), new CoordRec((float) 13.1019, (float) 9.5238), }; @@ -374,7 +374,7 @@ static final StrokeRec char46[] = { static final CoordRec char47_stroke0[] = { new CoordRec((float) 7.24, (float) -14.2857), - new CoordRec((float) 73.9067, (float) 100), + new CoordRec((float) 73.9067, 100), }; static final StrokeRec char47[] = { @@ -384,23 +384,23 @@ static final StrokeRec char47[] = { /* char: 48 '0' */ static final CoordRec char48_stroke0[] = { - new CoordRec((float) 33.5514, (float) 100), + new CoordRec((float) 33.5514, 100), new CoordRec((float) 19.2657, (float) 95.2381), new CoordRec((float) 9.7419, (float) 80.9524), new CoordRec((float) 4.98, (float) 57.1429), new CoordRec((float) 4.98, (float) 42.8571), new CoordRec((float) 9.7419, (float) 19.0476), new CoordRec((float) 19.2657, (float) 4.7619), - new CoordRec((float) 33.5514, (float) 0), - new CoordRec((float) 43.0752, (float) 0), + new CoordRec((float) 33.5514, 0), + new CoordRec((float) 43.0752, 0), new CoordRec((float) 57.361, (float) 4.7619), new CoordRec((float) 66.8848, (float) 19.0476), new CoordRec((float) 71.6467, (float) 42.8571), new CoordRec((float) 71.6467, (float) 57.1429), new CoordRec((float) 66.8848, (float) 80.9524), new CoordRec((float) 57.361, (float) 95.2381), - new CoordRec((float) 43.0752, (float) 100), - new CoordRec((float) 33.5514, (float) 100), + new CoordRec((float) 43.0752, 100), + new CoordRec((float) 33.5514, 100), }; static final StrokeRec char48[] = { @@ -412,8 +412,8 @@ static final StrokeRec char48[] = { static final CoordRec char49_stroke0[] = { new CoordRec((float) 11.82, (float) 80.9524), new CoordRec((float) 21.3438, (float) 85.7143), - new CoordRec((float) 35.6295, (float) 100), - new CoordRec((float) 35.6295, (float) 0), + new CoordRec((float) 35.6295, 100), + new CoordRec((float) 35.6295, 0), }; static final StrokeRec char49[] = { @@ -427,16 +427,16 @@ static final CoordRec char50_stroke0[] = { new CoordRec((float) 10.1819, (float) 80.9524), new CoordRec((float) 14.9438, (float) 90.4762), new CoordRec((float) 19.7057, (float) 95.2381), - new CoordRec((float) 29.2295, (float) 100), - new CoordRec((float) 48.2771, (float) 100), + new CoordRec((float) 29.2295, 100), + new CoordRec((float) 48.2771, 100), new CoordRec((float) 57.801, (float) 95.2381), new CoordRec((float) 62.5629, (float) 90.4762), new CoordRec((float) 67.3248, (float) 80.9524), new CoordRec((float) 67.3248, (float) 71.4286), new CoordRec((float) 62.5629, (float) 61.9048), new CoordRec((float) 53.039, (float) 47.619), - new CoordRec((float) 5.42, (float) 0), - new CoordRec((float) 72.0867, (float) 0), + new CoordRec((float) 5.42, 0), + new CoordRec((float) 72.0867, 0), }; static final StrokeRec char50[] = { @@ -446,8 +446,8 @@ static final StrokeRec char50[] = { /* char: 51 '3' */ static final CoordRec char51_stroke0[] = { - new CoordRec((float) 14.5238, (float) 100), - new CoordRec((float) 66.9048, (float) 100), + new CoordRec((float) 14.5238, 100), + new CoordRec((float) 66.9048, 100), new CoordRec((float) 38.3333, (float) 61.9048), new CoordRec((float) 52.619, (float) 61.9048), new CoordRec((float) 62.1429, (float) 57.1429), @@ -456,11 +456,11 @@ static final CoordRec char51_stroke0[] = { new CoordRec((float) 71.6667, (float) 28.5714), new CoordRec((float) 66.9048, (float) 14.2857), new CoordRec((float) 57.381, (float) 4.7619), - new CoordRec((float) 43.0952, (float) 0), - new CoordRec((float) 28.8095, (float) 0), + new CoordRec((float) 43.0952, 0), + new CoordRec((float) 28.8095, 0), new CoordRec((float) 14.5238, (float) 4.7619), new CoordRec((float) 9.7619, (float) 9.5238), - new CoordRec((float) 5, (float) 19.0476), + new CoordRec(5, (float) 19.0476), }; static final StrokeRec char51[] = { @@ -470,14 +470,14 @@ static final StrokeRec char51[] = { /* char: 52 '4' */ static final CoordRec char52_stroke0[] = { - new CoordRec((float) 51.499, (float) 100), + new CoordRec((float) 51.499, 100), new CoordRec((float) 3.88, (float) 33.3333), new CoordRec((float) 75.3086, (float) 33.3333), }; static final CoordRec char52_stroke1[] = { - new CoordRec((float) 51.499, (float) 100), - new CoordRec((float) 51.499, (float) 0), + new CoordRec((float) 51.499, 100), + new CoordRec((float) 51.499, 0), }; static final StrokeRec char52[] = { @@ -488,8 +488,8 @@ static final StrokeRec char52[] = { /* char: 53 '5' */ static final CoordRec char53_stroke0[] = { - new CoordRec((float) 62.0029, (float) 100), - new CoordRec((float) 14.3838, (float) 100), + new CoordRec((float) 62.0029, 100), + new CoordRec((float) 14.3838, 100), new CoordRec((float) 9.6219, (float) 57.1429), new CoordRec((float) 14.3838, (float) 61.9048), new CoordRec((float) 28.6695, (float) 66.6667), @@ -500,8 +500,8 @@ static final CoordRec char53_stroke0[] = { new CoordRec((float) 71.5267, (float) 28.5714), new CoordRec((float) 66.7648, (float) 14.2857), new CoordRec((float) 57.241, (float) 4.7619), - new CoordRec((float) 42.9552, (float) 0), - new CoordRec((float) 28.6695, (float) 0), + new CoordRec((float) 42.9552, 0), + new CoordRec((float) 28.6695, 0), new CoordRec((float) 14.3838, (float) 4.7619), new CoordRec((float) 9.6219, (float) 9.5238), new CoordRec((float) 4.86, (float) 19.0476), @@ -516,16 +516,16 @@ static final StrokeRec char53[] = { static final CoordRec char54_stroke0[] = { new CoordRec((float) 62.7229, (float) 85.7143), new CoordRec((float) 57.961, (float) 95.2381), - new CoordRec((float) 43.6752, (float) 100), - new CoordRec((float) 34.1514, (float) 100), + new CoordRec((float) 43.6752, 100), + new CoordRec((float) 34.1514, 100), new CoordRec((float) 19.8657, (float) 95.2381), new CoordRec((float) 10.3419, (float) 80.9524), new CoordRec((float) 5.58, (float) 57.1429), new CoordRec((float) 5.58, (float) 33.3333), new CoordRec((float) 10.3419, (float) 14.2857), new CoordRec((float) 19.8657, (float) 4.7619), - new CoordRec((float) 34.1514, (float) 0), - new CoordRec((float) 38.9133, (float) 0), + new CoordRec((float) 34.1514, 0), + new CoordRec((float) 38.9133, 0), new CoordRec((float) 53.199, (float) 4.7619), new CoordRec((float) 62.7229, (float) 14.2857), new CoordRec((float) 67.4848, (float) 28.5714), @@ -546,13 +546,13 @@ static final StrokeRec char54[] = { /* char: 55 '7' */ static final CoordRec char55_stroke0[] = { - new CoordRec((float) 72.2267, (float) 100), - new CoordRec((float) 24.6076, (float) 0), + new CoordRec((float) 72.2267, 100), + new CoordRec((float) 24.6076, 0), }; static final CoordRec char55_stroke1[] = { - new CoordRec((float) 5.56, (float) 100), - new CoordRec((float) 72.2267, (float) 100), + new CoordRec((float) 5.56, 100), + new CoordRec((float) 72.2267, 100), }; static final StrokeRec char55[] = { @@ -563,7 +563,7 @@ static final StrokeRec char55[] = { /* char: 56 '8' */ static final CoordRec char56_stroke0[] = { - new CoordRec((float) 29.4095, (float) 100), + new CoordRec((float) 29.4095, 100), new CoordRec((float) 15.1238, (float) 95.2381), new CoordRec((float) 10.3619, (float) 85.7143), new CoordRec((float) 10.3619, (float) 76.1905), @@ -576,8 +576,8 @@ static final CoordRec char56_stroke0[] = { new CoordRec((float) 72.2667, (float) 19.0476), new CoordRec((float) 67.5048, (float) 9.5238), new CoordRec((float) 62.7429, (float) 4.7619), - new CoordRec((float) 48.4571, (float) 0), - new CoordRec((float) 29.4095, (float) 0), + new CoordRec((float) 48.4571, 0), + new CoordRec((float) 29.4095, 0), new CoordRec((float) 15.1238, (float) 4.7619), new CoordRec((float) 10.3619, (float) 9.5238), new CoordRec((float) 5.6, (float) 19.0476), @@ -590,8 +590,8 @@ static final CoordRec char56_stroke0[] = { new CoordRec((float) 67.5048, (float) 76.1905), new CoordRec((float) 67.5048, (float) 85.7143), new CoordRec((float) 62.7429, (float) 95.2381), - new CoordRec((float) 48.4571, (float) 100), - new CoordRec((float) 29.4095, (float) 100), + new CoordRec((float) 48.4571, 100), + new CoordRec((float) 29.4095, 100), }; static final StrokeRec char56[] = { @@ -612,16 +612,16 @@ static final CoordRec char57_stroke0[] = { new CoordRec((float) 6.6, (float) 71.4286), new CoordRec((float) 11.3619, (float) 85.7143), new CoordRec((float) 20.8857, (float) 95.2381), - new CoordRec((float) 35.1714, (float) 100), - new CoordRec((float) 39.9333, (float) 100), + new CoordRec((float) 35.1714, 100), + new CoordRec((float) 39.9333, 100), new CoordRec((float) 54.219, (float) 95.2381), new CoordRec((float) 63.7429, (float) 85.7143), new CoordRec((float) 68.5048, (float) 66.6667), new CoordRec((float) 68.5048, (float) 42.8571), new CoordRec((float) 63.7429, (float) 19.0476), new CoordRec((float) 54.219, (float) 4.7619), - new CoordRec((float) 39.9333, (float) 0), - new CoordRec((float) 30.4095, (float) 0), + new CoordRec((float) 39.9333, 0), + new CoordRec((float) 30.4095, 0), new CoordRec((float) 16.1238, (float) 4.7619), new CoordRec((float) 11.3619, (float) 14.2857), }; @@ -643,7 +643,7 @@ static final CoordRec char58_stroke0[] = { static final CoordRec char58_stroke1[] = { new CoordRec((float) 14.0819, (float) 9.5238), new CoordRec((float) 9.32, (float) 4.7619), - new CoordRec((float) 14.0819, (float) 0), + new CoordRec((float) 14.0819, 0), new CoordRec((float) 18.8438, (float) 4.7619), new CoordRec((float) 14.0819, (float) 9.5238), }; @@ -665,7 +665,7 @@ static final CoordRec char59_stroke0[] = { static final CoordRec char59_stroke1[] = { new CoordRec((float) 17.7238, (float) 4.7619), - new CoordRec((float) 12.9619, (float) 0), + new CoordRec((float) 12.9619, 0), new CoordRec((float) 8.2, (float) 4.7619), new CoordRec((float) 12.9619, (float) 9.5238), new CoordRec((float) 17.7238, (float) 4.7619), @@ -684,7 +684,7 @@ static final StrokeRec char59[] = { static final CoordRec char60_stroke0[] = { new CoordRec((float) 79.2505, (float) 85.7143), new CoordRec((float) 3.06, (float) 42.8571), - new CoordRec((float) 79.2505, (float) 0), + new CoordRec((float) 79.2505, 0), }; static final StrokeRec char60[] = { @@ -713,7 +713,7 @@ static final StrokeRec char61[] = { static final CoordRec char62_stroke0[] = { new CoordRec((float) 2.78, (float) 85.7143), new CoordRec((float) 78.9705, (float) 42.8571), - new CoordRec((float) 2.78, (float) 0), + new CoordRec((float) 2.78, 0), }; static final StrokeRec char62[] = { @@ -727,8 +727,8 @@ static final CoordRec char63_stroke0[] = { new CoordRec((float) 8.42, (float) 80.9524), new CoordRec((float) 13.1819, (float) 90.4762), new CoordRec((float) 17.9438, (float) 95.2381), - new CoordRec((float) 27.4676, (float) 100), - new CoordRec((float) 46.5152, (float) 100), + new CoordRec((float) 27.4676, 100), + new CoordRec((float) 46.5152, 100), new CoordRec((float) 56.039, (float) 95.2381), new CoordRec((float) 60.801, (float) 90.4762), new CoordRec((float) 65.5629, (float) 80.9524), @@ -742,7 +742,7 @@ static final CoordRec char63_stroke0[] = { static final CoordRec char63_stroke1[] = { new CoordRec((float) 36.9914, (float) 9.5238), new CoordRec((float) 32.2295, (float) 4.7619), - new CoordRec((float) 36.9914, (float) 0), + new CoordRec((float) 36.9914, 0), new CoordRec((float) 41.7533, (float) 4.7619), new CoordRec((float) 36.9914, (float) 9.5238), }; @@ -795,13 +795,13 @@ static final StrokeRec char64[] = { /* char: 65 'A' */ static final CoordRec char65_stroke0[] = { - new CoordRec((float) 40.5952, (float) 100), - new CoordRec((float) 2.5, (float) 0), + new CoordRec((float) 40.5952, 100), + new CoordRec((float) 2.5, 0), }; static final CoordRec char65_stroke1[] = { - new CoordRec((float) 40.5952, (float) 100), - new CoordRec((float) 78.6905, (float) 0), + new CoordRec((float) 40.5952, 100), + new CoordRec((float) 78.6905, 0), }; static final CoordRec char65_stroke2[] = { @@ -818,13 +818,13 @@ static final StrokeRec char65[] = { /* char: 66 'B' */ static final CoordRec char66_stroke0[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 11.42, (float) 0), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 11.42, 0), }; static final CoordRec char66_stroke1[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 54.2771, (float) 100), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 54.2771, 100), new CoordRec((float) 68.5629, (float) 95.2381), new CoordRec((float) 73.3248, (float) 90.4762), new CoordRec((float) 78.0867, (float) 80.9524), @@ -843,8 +843,8 @@ static final CoordRec char66_stroke2[] = { new CoordRec((float) 78.0867, (float) 19.0476), new CoordRec((float) 73.3248, (float) 9.5238), new CoordRec((float) 68.5629, (float) 4.7619), - new CoordRec((float) 54.2771, (float) 0), - new CoordRec((float) 11.42, (float) 0), + new CoordRec((float) 54.2771, 0), + new CoordRec((float) 11.42, 0), }; static final StrokeRec char66[] = { @@ -859,8 +859,8 @@ static final CoordRec char67_stroke0[] = { new CoordRec((float) 78.0886, (float) 76.1905), new CoordRec((float) 73.3267, (float) 85.7143), new CoordRec((float) 63.8029, (float) 95.2381), - new CoordRec((float) 54.279, (float) 100), - new CoordRec((float) 35.2314, (float) 100), + new CoordRec((float) 54.279, 100), + new CoordRec((float) 35.2314, 100), new CoordRec((float) 25.7076, (float) 95.2381), new CoordRec((float) 16.1838, (float) 85.7143), new CoordRec((float) 11.4219, (float) 76.1905), @@ -869,8 +869,8 @@ static final CoordRec char67_stroke0[] = { new CoordRec((float) 11.4219, (float) 23.8095), new CoordRec((float) 16.1838, (float) 14.2857), new CoordRec((float) 25.7076, (float) 4.7619), - new CoordRec((float) 35.2314, (float) 0), - new CoordRec((float) 54.279, (float) 0), + new CoordRec((float) 35.2314, 0), + new CoordRec((float) 54.279, 0), new CoordRec((float) 63.8029, (float) 4.7619), new CoordRec((float) 73.3267, (float) 14.2857), new CoordRec((float) 78.0886, (float) 23.8095), @@ -883,13 +883,13 @@ static final StrokeRec char67[] = { /* char: 68 'D' */ static final CoordRec char68_stroke0[] = { - new CoordRec((float) 11.96, (float) 100), - new CoordRec((float) 11.96, (float) 0), + new CoordRec((float) 11.96, 100), + new CoordRec((float) 11.96, 0), }; static final CoordRec char68_stroke1[] = { - new CoordRec((float) 11.96, (float) 100), - new CoordRec((float) 45.2933, (float) 100), + new CoordRec((float) 11.96, 100), + new CoordRec((float) 45.2933, 100), new CoordRec((float) 59.579, (float) 95.2381), new CoordRec((float) 69.1029, (float) 85.7143), new CoordRec((float) 73.8648, (float) 76.1905), @@ -898,8 +898,8 @@ static final CoordRec char68_stroke1[] = { new CoordRec((float) 73.8648, (float) 23.8095), new CoordRec((float) 69.1029, (float) 14.2857), new CoordRec((float) 59.579, (float) 4.7619), - new CoordRec((float) 45.2933, (float) 0), - new CoordRec((float) 11.96, (float) 0), + new CoordRec((float) 45.2933, 0), + new CoordRec((float) 11.96, 0), }; static final StrokeRec char68[] = { @@ -910,13 +910,13 @@ static final StrokeRec char68[] = { /* char: 69 'E' */ static final CoordRec char69_stroke0[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 11.42, (float) 0), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 11.42, 0), }; static final CoordRec char69_stroke1[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 73.3248, (float) 100), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 73.3248, 100), }; static final CoordRec char69_stroke2[] = { @@ -925,8 +925,8 @@ static final CoordRec char69_stroke2[] = { }; static final CoordRec char69_stroke3[] = { - new CoordRec((float) 11.42, (float) 0), - new CoordRec((float) 73.3248, (float) 0), + new CoordRec((float) 11.42, 0), + new CoordRec((float) 73.3248, 0), }; static final StrokeRec char69[] = { @@ -939,13 +939,13 @@ static final StrokeRec char69[] = { /* char: 70 'F' */ static final CoordRec char70_stroke0[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 11.42, (float) 0), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 11.42, 0), }; static final CoordRec char70_stroke1[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 73.3248, (float) 100), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 73.3248, 100), }; static final CoordRec char70_stroke2[] = { @@ -965,8 +965,8 @@ static final CoordRec char71_stroke0[] = { new CoordRec((float) 78.4886, (float) 76.1905), new CoordRec((float) 73.7267, (float) 85.7143), new CoordRec((float) 64.2029, (float) 95.2381), - new CoordRec((float) 54.679, (float) 100), - new CoordRec((float) 35.6314, (float) 100), + new CoordRec((float) 54.679, 100), + new CoordRec((float) 35.6314, 100), new CoordRec((float) 26.1076, (float) 95.2381), new CoordRec((float) 16.5838, (float) 85.7143), new CoordRec((float) 11.8219, (float) 76.1905), @@ -975,8 +975,8 @@ static final CoordRec char71_stroke0[] = { new CoordRec((float) 11.8219, (float) 23.8095), new CoordRec((float) 16.5838, (float) 14.2857), new CoordRec((float) 26.1076, (float) 4.7619), - new CoordRec((float) 35.6314, (float) 0), - new CoordRec((float) 54.679, (float) 0), + new CoordRec((float) 35.6314, 0), + new CoordRec((float) 54.679, 0), new CoordRec((float) 64.2029, (float) 4.7619), new CoordRec((float) 73.7267, (float) 14.2857), new CoordRec((float) 78.4886, (float) 23.8095), @@ -996,13 +996,13 @@ static final StrokeRec char71[] = { /* char: 72 'H' */ static final CoordRec char72_stroke0[] = { - new CoordRec((float) 11.42, (float) 100), - new CoordRec((float) 11.42, (float) 0), + new CoordRec((float) 11.42, 100), + new CoordRec((float) 11.42, 0), }; static final CoordRec char72_stroke1[] = { - new CoordRec((float) 78.0867, (float) 100), - new CoordRec((float) 78.0867, (float) 0), + new CoordRec((float) 78.0867, 100), + new CoordRec((float) 78.0867, 0), }; static final CoordRec char72_stroke2[] = { @@ -1019,8 +1019,8 @@ static final StrokeRec char72[] = { /* char: 73 'I' */ static final CoordRec char73_stroke0[] = { - new CoordRec((float) 10.86, (float) 100), - new CoordRec((float) 10.86, (float) 0), + new CoordRec((float) 10.86, 100), + new CoordRec((float) 10.86, 0), }; static final StrokeRec char73[] = { @@ -1030,12 +1030,12 @@ static final StrokeRec char73[] = { /* char: 74 'J' */ static final CoordRec char74_stroke0[] = { - new CoordRec((float) 50.119, (float) 100), + new CoordRec((float) 50.119, 100), new CoordRec((float) 50.119, (float) 23.8095), new CoordRec((float) 45.3571, (float) 9.5238), new CoordRec((float) 40.5952, (float) 4.7619), - new CoordRec((float) 31.0714, (float) 0), - new CoordRec((float) 21.5476, (float) 0), + new CoordRec((float) 31.0714, 0), + new CoordRec((float) 21.5476, 0), new CoordRec((float) 12.0238, (float) 4.7619), new CoordRec((float) 7.2619, (float) 9.5238), new CoordRec((float) 2.5, (float) 23.8095), @@ -1049,18 +1049,18 @@ static final StrokeRec char74[] = { /* char: 75 'K' */ static final CoordRec char75_stroke0[] = { - new CoordRec((float) 11.28, (float) 100), - new CoordRec((float) 11.28, (float) 0), + new CoordRec((float) 11.28, 100), + new CoordRec((float) 11.28, 0), }; static final CoordRec char75_stroke1[] = { - new CoordRec((float) 77.9467, (float) 100), + new CoordRec((float) 77.9467, 100), new CoordRec((float) 11.28, (float) 33.3333), }; static final CoordRec char75_stroke2[] = { new CoordRec((float) 35.0895, (float) 57.1429), - new CoordRec((float) 77.9467, (float) 0), + new CoordRec((float) 77.9467, 0), }; static final StrokeRec char75[] = { @@ -1072,13 +1072,13 @@ static final StrokeRec char75[] = { /* char: 76 'L' */ static final CoordRec char76_stroke0[] = { - new CoordRec((float) 11.68, (float) 100), - new CoordRec((float) 11.68, (float) 0), + new CoordRec((float) 11.68, 100), + new CoordRec((float) 11.68, 0), }; static final CoordRec char76_stroke1[] = { - new CoordRec((float) 11.68, (float) 0), - new CoordRec((float) 68.8229, (float) 0), + new CoordRec((float) 11.68, 0), + new CoordRec((float) 68.8229, 0), }; static final StrokeRec char76[] = { @@ -1089,23 +1089,23 @@ static final StrokeRec char76[] = { /* char: 77 'M' */ static final CoordRec char77_stroke0[] = { - new CoordRec((float) 10.86, (float) 100), - new CoordRec((float) 10.86, (float) 0), + new CoordRec((float) 10.86, 100), + new CoordRec((float) 10.86, 0), }; static final CoordRec char77_stroke1[] = { - new CoordRec((float) 10.86, (float) 100), - new CoordRec((float) 48.9552, (float) 0), + new CoordRec((float) 10.86, 100), + new CoordRec((float) 48.9552, 0), }; static final CoordRec char77_stroke2[] = { - new CoordRec((float) 87.0505, (float) 100), - new CoordRec((float) 48.9552, (float) 0), + new CoordRec((float) 87.0505, 100), + new CoordRec((float) 48.9552, 0), }; static final CoordRec char77_stroke3[] = { - new CoordRec((float) 87.0505, (float) 100), - new CoordRec((float) 87.0505, (float) 0), + new CoordRec((float) 87.0505, 100), + new CoordRec((float) 87.0505, 0), }; static final StrokeRec char77[] = { @@ -1118,18 +1118,18 @@ static final StrokeRec char77[] = { /* char: 78 'N' */ static final CoordRec char78_stroke0[] = { - new CoordRec((float) 11.14, (float) 100), - new CoordRec((float) 11.14, (float) 0), + new CoordRec((float) 11.14, 100), + new CoordRec((float) 11.14, 0), }; static final CoordRec char78_stroke1[] = { - new CoordRec((float) 11.14, (float) 100), - new CoordRec((float) 77.8067, (float) 0), + new CoordRec((float) 11.14, 100), + new CoordRec((float) 77.8067, 0), }; static final CoordRec char78_stroke2[] = { - new CoordRec((float) 77.8067, (float) 100), - new CoordRec((float) 77.8067, (float) 0), + new CoordRec((float) 77.8067, 100), + new CoordRec((float) 77.8067, 0), }; static final StrokeRec char78[] = { @@ -1141,7 +1141,7 @@ static final StrokeRec char78[] = { /* char: 79 'O' */ static final CoordRec char79_stroke0[] = { - new CoordRec((float) 34.8114, (float) 100), + new CoordRec((float) 34.8114, 100), new CoordRec((float) 25.2876, (float) 95.2381), new CoordRec((float) 15.7638, (float) 85.7143), new CoordRec((float) 11.0019, (float) 76.1905), @@ -1150,8 +1150,8 @@ static final CoordRec char79_stroke0[] = { new CoordRec((float) 11.0019, (float) 23.8095), new CoordRec((float) 15.7638, (float) 14.2857), new CoordRec((float) 25.2876, (float) 4.7619), - new CoordRec((float) 34.8114, (float) 0), - new CoordRec((float) 53.859, (float) 0), + new CoordRec((float) 34.8114, 0), + new CoordRec((float) 53.859, 0), new CoordRec((float) 63.3829, (float) 4.7619), new CoordRec((float) 72.9067, (float) 14.2857), new CoordRec((float) 77.6686, (float) 23.8095), @@ -1160,8 +1160,8 @@ static final CoordRec char79_stroke0[] = { new CoordRec((float) 77.6686, (float) 76.1905), new CoordRec((float) 72.9067, (float) 85.7143), new CoordRec((float) 63.3829, (float) 95.2381), - new CoordRec((float) 53.859, (float) 100), - new CoordRec((float) 34.8114, (float) 100), + new CoordRec((float) 53.859, 100), + new CoordRec((float) 34.8114, 100), }; static final StrokeRec char79[] = { @@ -1171,13 +1171,13 @@ static final StrokeRec char79[] = { /* char: 80 'P' */ static final CoordRec char80_stroke0[] = { - new CoordRec((float) 12.1, (float) 100), - new CoordRec((float) 12.1, (float) 0), + new CoordRec((float) 12.1, 100), + new CoordRec((float) 12.1, 0), }; static final CoordRec char80_stroke1[] = { - new CoordRec((float) 12.1, (float) 100), - new CoordRec((float) 54.9571, (float) 100), + new CoordRec((float) 12.1, 100), + new CoordRec((float) 54.9571, 100), new CoordRec((float) 69.2429, (float) 95.2381), new CoordRec((float) 74.0048, (float) 90.4762), new CoordRec((float) 78.7667, (float) 80.9524), @@ -1196,7 +1196,7 @@ static final StrokeRec char80[] = { /* char: 81 'Q' */ static final CoordRec char81_stroke0[] = { - new CoordRec((float) 33.8714, (float) 100), + new CoordRec((float) 33.8714, 100), new CoordRec((float) 24.3476, (float) 95.2381), new CoordRec((float) 14.8238, (float) 85.7143), new CoordRec((float) 10.0619, (float) 76.1905), @@ -1205,8 +1205,8 @@ static final CoordRec char81_stroke0[] = { new CoordRec((float) 10.0619, (float) 23.8095), new CoordRec((float) 14.8238, (float) 14.2857), new CoordRec((float) 24.3476, (float) 4.7619), - new CoordRec((float) 33.8714, (float) 0), - new CoordRec((float) 52.919, (float) 0), + new CoordRec((float) 33.8714, 0), + new CoordRec((float) 52.919, 0), new CoordRec((float) 62.4429, (float) 4.7619), new CoordRec((float) 71.9667, (float) 14.2857), new CoordRec((float) 76.7286, (float) 23.8095), @@ -1215,8 +1215,8 @@ static final CoordRec char81_stroke0[] = { new CoordRec((float) 76.7286, (float) 76.1905), new CoordRec((float) 71.9667, (float) 85.7143), new CoordRec((float) 62.4429, (float) 95.2381), - new CoordRec((float) 52.919, (float) 100), - new CoordRec((float) 33.8714, (float) 100), + new CoordRec((float) 52.919, 100), + new CoordRec((float) 33.8714, 100), }; static final CoordRec char81_stroke1[] = { @@ -1232,13 +1232,13 @@ static final StrokeRec char81[] = { /* char: 82 'R' */ static final CoordRec char82_stroke0[] = { - new CoordRec((float) 11.68, (float) 100), - new CoordRec((float) 11.68, (float) 0), + new CoordRec((float) 11.68, 100), + new CoordRec((float) 11.68, 0), }; static final CoordRec char82_stroke1[] = { - new CoordRec((float) 11.68, (float) 100), - new CoordRec((float) 54.5371, (float) 100), + new CoordRec((float) 11.68, 100), + new CoordRec((float) 54.5371, 100), new CoordRec((float) 68.8229, (float) 95.2381), new CoordRec((float) 73.5848, (float) 90.4762), new CoordRec((float) 78.3467, (float) 80.9524), @@ -1251,7 +1251,7 @@ static final CoordRec char82_stroke1[] = { static final CoordRec char82_stroke2[] = { new CoordRec((float) 45.0133, (float) 52.381), - new CoordRec((float) 78.3467, (float) 0), + new CoordRec((float) 78.3467, 0), }; static final StrokeRec char82[] = { @@ -1265,11 +1265,11 @@ static final StrokeRec char82[] = { static final CoordRec char83_stroke0[] = { new CoordRec((float) 74.6667, (float) 85.7143), new CoordRec((float) 65.1429, (float) 95.2381), - new CoordRec((float) 50.8571, (float) 100), - new CoordRec((float) 31.8095, (float) 100), + new CoordRec((float) 50.8571, 100), + new CoordRec((float) 31.8095, 100), new CoordRec((float) 17.5238, (float) 95.2381), - new CoordRec((float) 8, (float) 85.7143), - new CoordRec((float) 8, (float) 76.1905), + new CoordRec(8, (float) 85.7143), + new CoordRec(8, (float) 76.1905), new CoordRec((float) 12.7619, (float) 66.6667), new CoordRec((float) 17.5238, (float) 61.9048), new CoordRec((float) 27.0476, (float) 57.1429), @@ -1279,10 +1279,10 @@ static final CoordRec char83_stroke0[] = { new CoordRec((float) 74.6667, (float) 28.5714), new CoordRec((float) 74.6667, (float) 14.2857), new CoordRec((float) 65.1429, (float) 4.7619), - new CoordRec((float) 50.8571, (float) 0), - new CoordRec((float) 31.8095, (float) 0), + new CoordRec((float) 50.8571, 0), + new CoordRec((float) 31.8095, 0), new CoordRec((float) 17.5238, (float) 4.7619), - new CoordRec((float) 8, (float) 14.2857), + new CoordRec(8, (float) 14.2857), }; static final StrokeRec char83[] = { @@ -1292,13 +1292,13 @@ static final StrokeRec char83[] = { /* char: 84 'T' */ static final CoordRec char84_stroke0[] = { - new CoordRec((float) 35.6933, (float) 100), - new CoordRec((float) 35.6933, (float) 0), + new CoordRec((float) 35.6933, 100), + new CoordRec((float) 35.6933, 0), }; static final CoordRec char84_stroke1[] = { - new CoordRec((float) 2.36, (float) 100), - new CoordRec((float) 69.0267, (float) 100), + new CoordRec((float) 2.36, 100), + new CoordRec((float) 69.0267, 100), }; static final StrokeRec char84[] = { @@ -1309,16 +1309,16 @@ static final StrokeRec char84[] = { /* char: 85 'U' */ static final CoordRec char85_stroke0[] = { - new CoordRec((float) 11.54, (float) 100), + new CoordRec((float) 11.54, 100), new CoordRec((float) 11.54, (float) 28.5714), new CoordRec((float) 16.3019, (float) 14.2857), new CoordRec((float) 25.8257, (float) 4.7619), - new CoordRec((float) 40.1114, (float) 0), - new CoordRec((float) 49.6352, (float) 0), + new CoordRec((float) 40.1114, 0), + new CoordRec((float) 49.6352, 0), new CoordRec((float) 63.921, (float) 4.7619), new CoordRec((float) 73.4448, (float) 14.2857), new CoordRec((float) 78.2067, (float) 28.5714), - new CoordRec((float) 78.2067, (float) 100), + new CoordRec((float) 78.2067, 100), }; static final StrokeRec char85[] = { @@ -1328,13 +1328,13 @@ static final StrokeRec char85[] = { /* char: 86 'V' */ static final CoordRec char86_stroke0[] = { - new CoordRec((float) 2.36, (float) 100), - new CoordRec((float) 40.4552, (float) 0), + new CoordRec((float) 2.36, 100), + new CoordRec((float) 40.4552, 0), }; static final CoordRec char86_stroke1[] = { - new CoordRec((float) 78.5505, (float) 100), - new CoordRec((float) 40.4552, (float) 0), + new CoordRec((float) 78.5505, 100), + new CoordRec((float) 40.4552, 0), }; static final StrokeRec char86[] = { @@ -1345,23 +1345,23 @@ static final StrokeRec char86[] = { /* char: 87 'W' */ static final CoordRec char87_stroke0[] = { - new CoordRec((float) 2.22, (float) 100), - new CoordRec((float) 26.0295, (float) 0), + new CoordRec((float) 2.22, 100), + new CoordRec((float) 26.0295, 0), }; static final CoordRec char87_stroke1[] = { - new CoordRec((float) 49.839, (float) 100), - new CoordRec((float) 26.0295, (float) 0), + new CoordRec((float) 49.839, 100), + new CoordRec((float) 26.0295, 0), }; static final CoordRec char87_stroke2[] = { - new CoordRec((float) 49.839, (float) 100), - new CoordRec((float) 73.6486, (float) 0), + new CoordRec((float) 49.839, 100), + new CoordRec((float) 73.6486, 0), }; static final CoordRec char87_stroke3[] = { - new CoordRec((float) 97.4581, (float) 100), - new CoordRec((float) 73.6486, (float) 0), + new CoordRec((float) 97.4581, 100), + new CoordRec((float) 73.6486, 0), }; static final StrokeRec char87[] = { @@ -1374,13 +1374,13 @@ static final StrokeRec char87[] = { /* char: 88 'X' */ static final CoordRec char88_stroke0[] = { - new CoordRec((float) 2.5, (float) 100), - new CoordRec((float) 69.1667, (float) 0), + new CoordRec((float) 2.5, 100), + new CoordRec((float) 69.1667, 0), }; static final CoordRec char88_stroke1[] = { - new CoordRec((float) 69.1667, (float) 100), - new CoordRec((float) 2.5, (float) 0), + new CoordRec((float) 69.1667, 100), + new CoordRec((float) 2.5, 0), }; static final StrokeRec char88[] = { @@ -1391,13 +1391,13 @@ static final StrokeRec char88[] = { /* char: 89 'Y' */ static final CoordRec char89_stroke0[] = { - new CoordRec((float) 1.52, (float) 100), + new CoordRec((float) 1.52, 100), new CoordRec((float) 39.6152, (float) 52.381), - new CoordRec((float) 39.6152, (float) 0), + new CoordRec((float) 39.6152, 0), }; static final CoordRec char89_stroke1[] = { - new CoordRec((float) 77.7105, (float) 100), + new CoordRec((float) 77.7105, 100), new CoordRec((float) 39.6152, (float) 52.381), }; @@ -1409,18 +1409,18 @@ static final StrokeRec char89[] = { /* char: 90 'Z' */ static final CoordRec char90_stroke0[] = { - new CoordRec((float) 69.1667, (float) 100), - new CoordRec((float) 2.5, (float) 0), + new CoordRec((float) 69.1667, 100), + new CoordRec((float) 2.5, 0), }; static final CoordRec char90_stroke1[] = { - new CoordRec((float) 2.5, (float) 100), - new CoordRec((float) 69.1667, (float) 100), + new CoordRec((float) 2.5, 100), + new CoordRec((float) 69.1667, 100), }; static final CoordRec char90_stroke2[] = { - new CoordRec((float) 2.5, (float) 0), - new CoordRec((float) 69.1667, (float) 0), + new CoordRec((float) 2.5, 0), + new CoordRec((float) 69.1667, 0), }; static final StrokeRec char90[] = { @@ -1461,7 +1461,7 @@ static final StrokeRec char91[] = { /* char: 92 '\' */ static final CoordRec char92_stroke0[] = { - new CoordRec((float) 5.84, (float) 100), + new CoordRec((float) 5.84, 100), new CoordRec((float) 72.5067, (float) -14.2857), }; @@ -1532,12 +1532,12 @@ static final StrokeRec char95[] = { /* char: 96 '`' */ static final CoordRec char96_stroke0[] = { - new CoordRec((float) 33.0219, (float) 100), + new CoordRec((float) 33.0219, 100), new CoordRec((float) 56.8314, (float) 71.4286), }; static final CoordRec char96_stroke1[] = { - new CoordRec((float) 33.0219, (float) 100), + new CoordRec((float) 33.0219, 100), new CoordRec((float) 28.26, (float) 95.2381), new CoordRec((float) 56.8314, (float) 71.4286), }; @@ -1551,7 +1551,7 @@ static final StrokeRec char96[] = { static final CoordRec char97_stroke0[] = { new CoordRec((float) 63.8229, (float) 66.6667), - new CoordRec((float) 63.8229, (float) 0), + new CoordRec((float) 63.8229, 0), }; static final CoordRec char97_stroke1[] = { @@ -1565,8 +1565,8 @@ static final CoordRec char97_stroke1[] = { new CoordRec((float) 6.68, (float) 28.5714), new CoordRec((float) 11.4419, (float) 14.2857), new CoordRec((float) 20.9657, (float) 4.7619), - new CoordRec((float) 30.4895, (float) 0), - new CoordRec((float) 44.7752, (float) 0), + new CoordRec((float) 30.4895, 0), + new CoordRec((float) 44.7752, 0), new CoordRec((float) 54.299, (float) 4.7619), new CoordRec((float) 63.8229, (float) 14.2857), }; @@ -1579,8 +1579,8 @@ static final StrokeRec char97[] = { /* char: 98 'b' */ static final CoordRec char98_stroke0[] = { - new CoordRec((float) 8.76, (float) 100), - new CoordRec((float) 8.76, (float) 0), + new CoordRec((float) 8.76, 100), + new CoordRec((float) 8.76, 0), }; static final CoordRec char98_stroke1[] = { @@ -1594,8 +1594,8 @@ static final CoordRec char98_stroke1[] = { new CoordRec((float) 65.9029, (float) 28.5714), new CoordRec((float) 61.141, (float) 14.2857), new CoordRec((float) 51.6171, (float) 4.7619), - new CoordRec((float) 42.0933, (float) 0), - new CoordRec((float) 27.8076, (float) 0), + new CoordRec((float) 42.0933, 0), + new CoordRec((float) 27.8076, 0), new CoordRec((float) 18.2838, (float) 4.7619), new CoordRec((float) 8.76, (float) 14.2857), }; @@ -1618,8 +1618,8 @@ static final CoordRec char99_stroke0[] = { new CoordRec((float) 5.52, (float) 28.5714), new CoordRec((float) 10.2819, (float) 14.2857), new CoordRec((float) 19.8057, (float) 4.7619), - new CoordRec((float) 29.3295, (float) 0), - new CoordRec((float) 43.6152, (float) 0), + new CoordRec((float) 29.3295, 0), + new CoordRec((float) 43.6152, 0), new CoordRec((float) 53.139, (float) 4.7619), new CoordRec((float) 62.6629, (float) 14.2857), }; @@ -1631,8 +1631,8 @@ static final StrokeRec char99[] = { /* char: 100 'd' */ static final CoordRec char100_stroke0[] = { - new CoordRec((float) 61.7829, (float) 100), - new CoordRec((float) 61.7829, (float) 0), + new CoordRec((float) 61.7829, 100), + new CoordRec((float) 61.7829, 0), }; static final CoordRec char100_stroke1[] = { @@ -1646,8 +1646,8 @@ static final CoordRec char100_stroke1[] = { new CoordRec((float) 4.64, (float) 28.5714), new CoordRec((float) 9.4019, (float) 14.2857), new CoordRec((float) 18.9257, (float) 4.7619), - new CoordRec((float) 28.4495, (float) 0), - new CoordRec((float) 42.7352, (float) 0), + new CoordRec((float) 28.4495, 0), + new CoordRec((float) 42.7352, 0), new CoordRec((float) 52.259, (float) 4.7619), new CoordRec((float) 61.7829, (float) 14.2857), }; @@ -1673,8 +1673,8 @@ static final CoordRec char101_stroke0[] = { new CoordRec((float) 5.72, (float) 28.5714), new CoordRec((float) 10.4819, (float) 14.2857), new CoordRec((float) 20.0057, (float) 4.7619), - new CoordRec((float) 29.5295, (float) 0), - new CoordRec((float) 43.8152, (float) 0), + new CoordRec((float) 29.5295, 0), + new CoordRec((float) 43.8152, 0), new CoordRec((float) 53.339, (float) 4.7619), new CoordRec((float) 62.8629, (float) 14.2857), }; @@ -1686,11 +1686,11 @@ static final StrokeRec char101[] = { /* char: 102 'f' */ static final CoordRec char102_stroke0[] = { - new CoordRec((float) 38.7752, (float) 100), - new CoordRec((float) 29.2514, (float) 100), + new CoordRec((float) 38.7752, 100), + new CoordRec((float) 29.2514, 100), new CoordRec((float) 19.7276, (float) 95.2381), new CoordRec((float) 14.9657, (float) 80.9524), - new CoordRec((float) 14.9657, (float) 0), + new CoordRec((float) 14.9657, 0), }; static final CoordRec char102_stroke1[] = { @@ -1726,8 +1726,8 @@ static final CoordRec char103_stroke1[] = { new CoordRec((float) 5.36, (float) 28.5714), new CoordRec((float) 10.1219, (float) 14.2857), new CoordRec((float) 19.6457, (float) 4.7619), - new CoordRec((float) 29.1695, (float) 0), - new CoordRec((float) 43.4552, (float) 0), + new CoordRec((float) 29.1695, 0), + new CoordRec((float) 43.4552, 0), new CoordRec((float) 52.979, (float) 4.7619), new CoordRec((float) 62.5029, (float) 14.2857), }; @@ -1740,8 +1740,8 @@ static final StrokeRec char103[] = { /* char: 104 'h' */ static final CoordRec char104_stroke0[] = { - new CoordRec((float) 9.6, (float) 100), - new CoordRec((float) 9.6, (float) 0), + new CoordRec((float) 9.6, 100), + new CoordRec((float) 9.6, 0), }; static final CoordRec char104_stroke1[] = { @@ -1751,7 +1751,7 @@ static final CoordRec char104_stroke1[] = { new CoordRec((float) 47.6952, (float) 66.6667), new CoordRec((float) 57.219, (float) 61.9048), new CoordRec((float) 61.981, (float) 47.619), - new CoordRec((float) 61.981, (float) 0), + new CoordRec((float) 61.981, 0), }; static final StrokeRec char104[] = { @@ -1762,16 +1762,16 @@ static final StrokeRec char104[] = { /* char: 105 'i' */ static final CoordRec char105_stroke0[] = { - new CoordRec((float) 10.02, (float) 100), + new CoordRec((float) 10.02, 100), new CoordRec((float) 14.7819, (float) 95.2381), - new CoordRec((float) 19.5438, (float) 100), + new CoordRec((float) 19.5438, 100), new CoordRec((float) 14.7819, (float) 104.762), - new CoordRec((float) 10.02, (float) 100), + new CoordRec((float) 10.02, 100), }; static final CoordRec char105_stroke1[] = { new CoordRec((float) 14.7819, (float) 66.6667), - new CoordRec((float) 14.7819, (float) 0), + new CoordRec((float) 14.7819, 0), }; static final StrokeRec char105[] = { @@ -1782,11 +1782,11 @@ static final StrokeRec char105[] = { /* char: 106 'j' */ static final CoordRec char106_stroke0[] = { - new CoordRec((float) 17.3876, (float) 100), + new CoordRec((float) 17.3876, 100), new CoordRec((float) 22.1495, (float) 95.2381), - new CoordRec((float) 26.9114, (float) 100), + new CoordRec((float) 26.9114, 100), new CoordRec((float) 22.1495, (float) 104.762), - new CoordRec((float) 17.3876, (float) 100), + new CoordRec((float) 17.3876, 100), }; static final CoordRec char106_stroke1[] = { @@ -1805,8 +1805,8 @@ static final StrokeRec char106[] = { /* char: 107 'k' */ static final CoordRec char107_stroke0[] = { - new CoordRec((float) 9.6, (float) 100), - new CoordRec((float) 9.6, (float) 0), + new CoordRec((float) 9.6, 100), + new CoordRec((float) 9.6, 0), }; static final CoordRec char107_stroke1[] = { @@ -1816,7 +1816,7 @@ static final CoordRec char107_stroke1[] = { static final CoordRec char107_stroke2[] = { new CoordRec((float) 28.6476, (float) 38.0952), - new CoordRec((float) 61.981, (float) 0), + new CoordRec((float) 61.981, 0), }; static final StrokeRec char107[] = { @@ -1828,8 +1828,8 @@ static final StrokeRec char107[] = { /* char: 108 'l' */ static final CoordRec char108_stroke0[] = { - new CoordRec((float) 10.02, (float) 100), - new CoordRec((float) 10.02, (float) 0), + new CoordRec((float) 10.02, 100), + new CoordRec((float) 10.02, 0), }; static final StrokeRec char108[] = { @@ -1840,7 +1840,7 @@ static final StrokeRec char108[] = { static final CoordRec char109_stroke0[] = { new CoordRec((float) 9.6, (float) 66.6667), - new CoordRec((float) 9.6, (float) 0), + new CoordRec((float) 9.6, 0), }; static final CoordRec char109_stroke1[] = { @@ -1850,7 +1850,7 @@ static final CoordRec char109_stroke1[] = { new CoordRec((float) 47.6952, (float) 66.6667), new CoordRec((float) 57.219, (float) 61.9048), new CoordRec((float) 61.981, (float) 47.619), - new CoordRec((float) 61.981, (float) 0), + new CoordRec((float) 61.981, 0), }; static final CoordRec char109_stroke2[] = { @@ -1860,7 +1860,7 @@ static final CoordRec char109_stroke2[] = { new CoordRec((float) 100.076, (float) 66.6667), new CoordRec((float) 109.6, (float) 61.9048), new CoordRec((float) 114.362, (float) 47.619), - new CoordRec((float) 114.362, (float) 0), + new CoordRec((float) 114.362, 0), }; static final StrokeRec char109[] = { @@ -1873,7 +1873,7 @@ static final StrokeRec char109[] = { static final CoordRec char110_stroke0[] = { new CoordRec((float) 9.18, (float) 66.6667), - new CoordRec((float) 9.18, (float) 0), + new CoordRec((float) 9.18, 0), }; static final CoordRec char110_stroke1[] = { @@ -1883,7 +1883,7 @@ static final CoordRec char110_stroke1[] = { new CoordRec((float) 47.2752, (float) 66.6667), new CoordRec((float) 56.799, (float) 61.9048), new CoordRec((float) 61.561, (float) 47.619), - new CoordRec((float) 61.561, (float) 0), + new CoordRec((float) 61.561, 0), }; static final StrokeRec char110[] = { @@ -1901,8 +1901,8 @@ static final CoordRec char111_stroke0[] = { new CoordRec((float) 4.98, (float) 28.5714), new CoordRec((float) 9.7419, (float) 14.2857), new CoordRec((float) 19.2657, (float) 4.7619), - new CoordRec((float) 28.7895, (float) 0), - new CoordRec((float) 43.0752, (float) 0), + new CoordRec((float) 28.7895, 0), + new CoordRec((float) 43.0752, 0), new CoordRec((float) 52.599, (float) 4.7619), new CoordRec((float) 62.1229, (float) 14.2857), new CoordRec((float) 66.8848, (float) 28.5714), @@ -1935,8 +1935,8 @@ static final CoordRec char112_stroke1[] = { new CoordRec((float) 66.6029, (float) 28.5714), new CoordRec((float) 61.841, (float) 14.2857), new CoordRec((float) 52.3171, (float) 4.7619), - new CoordRec((float) 42.7933, (float) 0), - new CoordRec((float) 28.5076, (float) 0), + new CoordRec((float) 42.7933, 0), + new CoordRec((float) 28.5076, 0), new CoordRec((float) 18.9838, (float) 4.7619), new CoordRec((float) 9.46, (float) 14.2857), }; @@ -1964,8 +1964,8 @@ static final CoordRec char113_stroke1[] = { new CoordRec((float) 4.84, (float) 28.5714), new CoordRec((float) 9.6019, (float) 14.2857), new CoordRec((float) 19.1257, (float) 4.7619), - new CoordRec((float) 28.6495, (float) 0), - new CoordRec((float) 42.9352, (float) 0), + new CoordRec((float) 28.6495, 0), + new CoordRec((float) 42.9352, 0), new CoordRec((float) 52.459, (float) 4.7619), new CoordRec((float) 61.9829, (float) 14.2857), }; @@ -1979,7 +1979,7 @@ static final StrokeRec char113[] = { static final CoordRec char114_stroke0[] = { new CoordRec((float) 9.46, (float) 66.6667), - new CoordRec((float) 9.46, (float) 0), + new CoordRec((float) 9.46, 0), }; static final CoordRec char114_stroke1[] = { @@ -2011,8 +2011,8 @@ static final CoordRec char115_stroke0[] = { new CoordRec((float) 57.081, (float) 19.0476), new CoordRec((float) 57.081, (float) 14.2857), new CoordRec((float) 52.319, (float) 4.7619), - new CoordRec((float) 38.0333, (float) 0), - new CoordRec((float) 23.7476, (float) 0), + new CoordRec((float) 38.0333, 0), + new CoordRec((float) 23.7476, 0), new CoordRec((float) 9.4619, (float) 4.7619), new CoordRec((float) 4.7, (float) 14.2857), }; @@ -2024,11 +2024,11 @@ static final StrokeRec char115[] = { /* char: 116 't' */ static final CoordRec char116_stroke0[] = { - new CoordRec((float) 14.8257, (float) 100), + new CoordRec((float) 14.8257, 100), new CoordRec((float) 14.8257, (float) 19.0476), new CoordRec((float) 19.5876, (float) 4.7619), - new CoordRec((float) 29.1114, (float) 0), - new CoordRec((float) 38.6352, (float) 0), + new CoordRec((float) 29.1114, 0), + new CoordRec((float) 38.6352, 0), }; static final CoordRec char116_stroke1[] = { @@ -2047,15 +2047,15 @@ static final CoordRec char117_stroke0[] = { new CoordRec((float) 9.46, (float) 66.6667), new CoordRec((float) 9.46, (float) 19.0476), new CoordRec((float) 14.2219, (float) 4.7619), - new CoordRec((float) 23.7457, (float) 0), - new CoordRec((float) 38.0314, (float) 0), + new CoordRec((float) 23.7457, 0), + new CoordRec((float) 38.0314, 0), new CoordRec((float) 47.5552, (float) 4.7619), new CoordRec((float) 61.841, (float) 19.0476), }; static final CoordRec char117_stroke1[] = { new CoordRec((float) 61.841, (float) 66.6667), - new CoordRec((float) 61.841, (float) 0), + new CoordRec((float) 61.841, 0), }; static final StrokeRec char117[] = { @@ -2067,12 +2067,12 @@ static final StrokeRec char117[] = { static final CoordRec char118_stroke0[] = { new CoordRec((float) 1.8, (float) 66.6667), - new CoordRec((float) 30.3714, (float) 0), + new CoordRec((float) 30.3714, 0), }; static final CoordRec char118_stroke1[] = { new CoordRec((float) 58.9429, (float) 66.6667), - new CoordRec((float) 30.3714, (float) 0), + new CoordRec((float) 30.3714, 0), }; static final StrokeRec char118[] = { @@ -2084,22 +2084,22 @@ static final StrokeRec char118[] = { static final CoordRec char119_stroke0[] = { new CoordRec((float) 2.5, (float) 66.6667), - new CoordRec((float) 21.5476, (float) 0), + new CoordRec((float) 21.5476, 0), }; static final CoordRec char119_stroke1[] = { new CoordRec((float) 40.5952, (float) 66.6667), - new CoordRec((float) 21.5476, (float) 0), + new CoordRec((float) 21.5476, 0), }; static final CoordRec char119_stroke2[] = { new CoordRec((float) 40.5952, (float) 66.6667), - new CoordRec((float) 59.6429, (float) 0), + new CoordRec((float) 59.6429, 0), }; static final CoordRec char119_stroke3[] = { new CoordRec((float) 78.6905, (float) 66.6667), - new CoordRec((float) 59.6429, (float) 0), + new CoordRec((float) 59.6429, 0), }; static final StrokeRec char119[] = { @@ -2113,12 +2113,12 @@ static final StrokeRec char119[] = { static final CoordRec char120_stroke0[] = { new CoordRec((float) 1.66, (float) 66.6667), - new CoordRec((float) 54.041, (float) 0), + new CoordRec((float) 54.041, 0), }; static final CoordRec char120_stroke1[] = { new CoordRec((float) 54.041, (float) 66.6667), - new CoordRec((float) 1.66, (float) 0), + new CoordRec((float) 1.66, 0), }; static final StrokeRec char120[] = { @@ -2130,12 +2130,12 @@ static final StrokeRec char120[] = { static final CoordRec char121_stroke0[] = { new CoordRec((float) 6.5619, (float) 66.6667), - new CoordRec((float) 35.1333, (float) 0), + new CoordRec((float) 35.1333, 0), }; static final CoordRec char121_stroke1[] = { new CoordRec((float) 63.7048, (float) 66.6667), - new CoordRec((float) 35.1333, (float) 0), + new CoordRec((float) 35.1333, 0), new CoordRec((float) 25.6095, (float) -19.0476), new CoordRec((float) 16.0857, (float) -28.5714), new CoordRec((float) 6.5619, (float) -33.3333), @@ -2151,7 +2151,7 @@ static final StrokeRec char121[] = { static final CoordRec char122_stroke0[] = { new CoordRec((float) 56.821, (float) 66.6667), - new CoordRec((float) 4.44, (float) 0), + new CoordRec((float) 4.44, 0), }; static final CoordRec char122_stroke1[] = { @@ -2160,8 +2160,8 @@ static final CoordRec char122_stroke1[] = { }; static final CoordRec char122_stroke2[] = { - new CoordRec((float) 4.44, (float) 0), - new CoordRec((float) 56.821, (float) 0), + new CoordRec((float) 4.44, 0), + new CoordRec((float) 56.821, 0), }; static final StrokeRec char122[] = { @@ -2176,7 +2176,7 @@ static final CoordRec char123_stroke0[] = { new CoordRec((float) 31.1895, (float) 119.048), new CoordRec((float) 21.6657, (float) 114.286), new CoordRec((float) 16.9038, (float) 109.524), - new CoordRec((float) 12.1419, (float) 100), + new CoordRec((float) 12.1419, 100), new CoordRec((float) 12.1419, (float) 90.4762), new CoordRec((float) 16.9038, (float) 80.9524), new CoordRec((float) 21.6657, (float) 76.1905), @@ -2199,7 +2199,7 @@ static final CoordRec char123_stroke1[] = { new CoordRec((float) 31.1895, (float) 23.8095), new CoordRec((float) 31.1895, (float) 14.2857), new CoordRec((float) 26.4276, (float) 4.7619), - new CoordRec((float) 21.6657, (float) 0), + new CoordRec((float) 21.6657, 0), new CoordRec((float) 16.9038, (float) -9.5238), new CoordRec((float) 16.9038, (float) -19.0476), new CoordRec((float) 21.6657, (float) -28.5714), @@ -2241,7 +2241,7 @@ static final CoordRec char125_stroke0[] = { new CoordRec((float) 9.18, (float) 119.048), new CoordRec((float) 18.7038, (float) 114.286), new CoordRec((float) 23.4657, (float) 109.524), - new CoordRec((float) 28.2276, (float) 100), + new CoordRec((float) 28.2276, 100), new CoordRec((float) 28.2276, (float) 90.4762), new CoordRec((float) 23.4657, (float) 80.9524), new CoordRec((float) 18.7038, (float) 76.1905), @@ -2264,7 +2264,7 @@ static final CoordRec char125_stroke1[] = { new CoordRec((float) 9.18, (float) 23.8095), new CoordRec((float) 9.18, (float) 14.2857), new CoordRec((float) 13.9419, (float) 4.7619), - new CoordRec((float) 18.7038, (float) 0), + new CoordRec((float) 18.7038, 0), new CoordRec((float) 23.4657, (float) -9.5238), new CoordRec((float) 23.4657, (float) -19.0476), new CoordRec((float) 18.7038, (float) -28.5714), @@ -2327,7 +2327,7 @@ static final StrokeRec char126[] = { /* char: 127 */ static final CoordRec char127_stroke0[] = { - new CoordRec((float) 52.381, (float) 100), + new CoordRec((float) 52.381, 100), new CoordRec((float) 14.2857, (float) -33.3333), }; @@ -2335,12 +2335,12 @@ static final CoordRec char127_stroke1[] = { new CoordRec((float) 28.5714, (float) 66.6667), new CoordRec((float) 14.2857, (float) 61.9048), new CoordRec((float) 4.7619, (float) 52.381), - new CoordRec((float) 0, (float) 38.0952), - new CoordRec((float) 0, (float) 23.8095), + new CoordRec(0, (float) 38.0952), + new CoordRec(0, (float) 23.8095), new CoordRec((float) 4.7619, (float) 14.2857), new CoordRec((float) 14.2857, (float) 4.7619), - new CoordRec((float) 28.5714, (float) 0), - new CoordRec((float) 38.0952, (float) 0), + new CoordRec((float) 28.5714, 0), + new CoordRec((float) 38.0952, 0), new CoordRec((float) 52.381, (float) 4.7619), new CoordRec((float) 61.9048, (float) 14.2857), new CoordRec((float) 66.6667, (float) 28.5714), @@ -2357,38 +2357,38 @@ static final StrokeRec char127[] = { }; static final StrokeCharRec chars[] = { - new StrokeCharRec( 0, /* char0 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char1 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char2 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char3 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char4 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char5 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char6 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char7 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char8 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char9 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char10 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char11 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char12 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char13 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char14 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char15 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char16 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char17 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char18 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char19 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char20 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char21 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char22 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char23 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char24 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char25 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char26 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char27 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char28 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char29 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char30 */ null, (float) 0, (float) 0 ), - new StrokeCharRec( 0, /* char31 */ null, (float) 0, (float) 0 ), + new StrokeCharRec( 0, /* char0 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char1 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char2 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char3 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char4 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char5 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char6 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char7 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char8 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char9 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char10 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char11 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char12 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char13 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char14 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char15 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char16 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char17 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char18 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char19 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char20 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char21 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char22 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char23 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char24 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char25 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char26 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char27 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char28 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char29 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char30 */ null, 0, 0 ), + new StrokeCharRec( 0, /* char31 */ null, 0, 0 ), new StrokeCharRec( 0, /* char32 */ null, (float) 52.381, (float) 104.762 ), new StrokeCharRec( 2, char33, (float) 13.3819, (float) 26.6238 ), new StrokeCharRec( 2, char34, (float) 23.0676, (float) 51.4352 ), diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeCharRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeCharRec.java index af3d538ae..59935e358 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeCharRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeCharRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,8 +41,8 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class StrokeCharRec { @@ -51,10 +51,10 @@ class StrokeCharRec { public float center; public float right; - public StrokeCharRec(int num_strokes, - StrokeRec[] stroke, - float center, - float right) { + public StrokeCharRec(final int num_strokes, + final StrokeRec[] stroke, + final float center, + final float right) { this.num_strokes = num_strokes; this.stroke = stroke; this.center = center; diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeFontRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeFontRec.java index d3195f24d..5810ac38a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeFontRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeFontRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,8 +41,8 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class StrokeFontRec { @@ -52,11 +52,11 @@ class StrokeFontRec { public float top; public float bottom; - public StrokeFontRec(String name, - int num_chars, - StrokeCharRec[] ch, - float top, - float bottom) { + public StrokeFontRec(final String name, + final int num_chars, + final StrokeCharRec[] ch, + final float top, + final float bottom) { this.name = name; this.num_chars = num_chars; this.ch = ch; diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeRec.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeRec.java index 8796e8b08..fcb7c912a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeRec.java +++ b/src/jogl/classes/com/jogamp/opengl/util/gl2/StrokeRec.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,16 +41,16 @@ package com.jogamp.opengl.util.gl2; /* Copyright (c) Mark J. Kilgard, 1994, 1998. */ -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or +/* This program is freely distributable without licensing fees + and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ class StrokeRec { public int num_coords; public CoordRec[] coord; - - public StrokeRec(int num_coords, - CoordRec[] coord) { + + public StrokeRec(final int num_coords, + final CoordRec[] coord) { this.num_coords = num_coords; this.coord = coord; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java deleted file mode 100644 index 714c134d4..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java +++ /dev/null @@ -1,601 +0,0 @@ -package com.jogamp.opengl.util.gl2; - -import java.awt.Dimension; -import java.nio.Buffer; - -import javax.media.opengl.*; -import javax.media.opengl.glu.*; -import javax.media.opengl.glu.gl2.*; - -/** - * A fairly direct port of Brian Paul's tile rendering library, found - * at <a href = "http://www.mesa3d.org/brianp/TR.html"> - * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but - * the functionality is the same. - * - * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under - * BSD-compatible terms with permission of the author. See LICENSE.txt - * for license information. - * - * @author ryanm - */ -public class TileRenderer -{ - private static final int DEFAULT_TILE_WIDTH = 256; - - private static final int DEFAULT_TILE_HEIGHT = 256; - - private static final int DEFAULT_TILE_BORDER = 0; - - // - // Enumeration flags for accessing variables - // - // @author ryanm - // - - /** - * The width of a tile - */ - public static final int TR_TILE_WIDTH = 0; - /** - * The height of a tile - */ - public static final int TR_TILE_HEIGHT = 1; - /** - * The width of the border around the tiles - */ - public static final int TR_TILE_BORDER = 2; - /** - * The width of the final image - */ - public static final int TR_IMAGE_WIDTH = 3; - /** - * The height of the final image - */ - public static final int TR_IMAGE_HEIGHT = 4; - /** - * The number of rows of tiles - */ - public static final int TR_ROWS = 5; - /** - * The number of columns of tiles - */ - public static final int TR_COLUMNS = 6; - /** - * The current row number - */ - public static final int TR_CURRENT_ROW = 7; - /** - * The current column number - */ - public static final int TR_CURRENT_COLUMN = 8; - /** - * The width of the current tile - */ - public static final int TR_CURRENT_TILE_WIDTH = 9; - /** - * The height of the current tile - */ - public static final int TR_CURRENT_TILE_HEIGHT = 10; - /** - * The order that the rows are traversed - */ - public static final int TR_ROW_ORDER = 11; - - - /** - * Indicates we are traversing rows from the top to the bottom - */ - public static final int TR_TOP_TO_BOTTOM = 1; - - /** - * Indicates we are traversing rows from the bottom to the top - */ - public static final int TR_BOTTOM_TO_TOP = 2; - - /* Final image parameters */ - private Dimension imageSize = new Dimension(); - - private int imageFormat, imageType; - - private Buffer imageBuffer; - - /* Tile parameters */ - private Dimension tileSize = new Dimension(); - - private Dimension tileSizeNB = new Dimension(); - - private int tileBorder; - - private int tileFormat, tileType; - - private Buffer tileBuffer; - - /* Projection parameters */ - private boolean perspective; - - private double left; - - private double right; - - private double bottom; - - private double top; - - private double near; - - private double far; - - /* Misc */ - private int rowOrder; - - private int rows, columns; - - private int currentTile; - - private int currentTileWidth, currentTileHeight; - - private int currentRow, currentColumn; - - private int[] viewportSave = new int[ 4 ]; - - /** - * Creates a new TileRenderer object - */ - public TileRenderer() - { - tileSize.width = DEFAULT_TILE_WIDTH; - tileSize.height = DEFAULT_TILE_HEIGHT; - tileBorder = DEFAULT_TILE_BORDER; - rowOrder = TR_BOTTOM_TO_TOP; - currentTile = -1; - } - - /** - * Sets up the number of rows and columns needed - */ - private void setup() - { - columns = ( imageSize.width + tileSizeNB.width - 1 ) / tileSizeNB.width; - rows = ( imageSize.height + tileSizeNB.height - 1 ) / tileSizeNB.height; - currentTile = 0; - - assert columns >= 0; - assert rows >= 0; - } - - /** - * Sets the size of the tiles to use in rendering. The actual - * effective size of the tile depends on the border size, ie ( - * width - 2*border ) * ( height - 2 * border ) - * - * @param width - * The width of the tiles. Must not be larger than the GL - * context - * @param height - * The height of the tiles. Must not be larger than the - * GL context - * @param border - * The width of the borders on each tile. This is needed - * to avoid artifacts when rendering lines or points with - * thickness > 1. - */ - public void setTileSize( int width, int height, int border ) - { - assert ( border >= 0 ); - assert ( width >= 1 ); - assert ( height >= 1 ); - assert ( width >= 2 * border ); - assert ( height >= 2 * border ); - - tileBorder = border; - tileSize.width = width; - tileSize.height = height; - tileSizeNB.width = width - 2 * border; - tileSizeNB.height = height - 2 * border; - setup(); - } - - /** - * Specify a buffer the tiles to be copied to. This is not - * necessary for the creation of the final image, but useful if you - * want to inspect each tile in turn. - * - * @param format - * Interpreted as in glReadPixels - * @param type - * Interpreted as in glReadPixels - * @param image - * The buffer itself. Must be large enough to contain a - * tile, minus any borders - */ - public void setTileBuffer( int format, int type, Buffer image ) - { - tileFormat = format; - tileType = type; - tileBuffer = image; - } - - /** - * Sets the desired size of the final image - * - * @param width - * The width of the final image - * @param height - * The height of the final image - */ - public void setImageSize( int width, int height ) - { - imageSize.width = width; - imageSize.height = height; - setup(); - } - - /** - * Sets the buffer in which to store the final image - * - * @param format - * Interpreted as in glReadPixels - * @param type - * Interpreted as in glReadPixels - * @param image - * the buffer itself, must be large enough to hold the - * final image - */ - public void setImageBuffer( int format, int type, Buffer image ) - { - imageFormat = format; - imageType = type; - imageBuffer = image; - } - - /** - * Gets the parameters of this TileRenderer object - * - * @param param - * The parameter that is to be retrieved - * @return the value of the parameter - */ - public int getParam( int param ) - { - switch (param) { - case TR_TILE_WIDTH: - return tileSize.width; - case TR_TILE_HEIGHT: - return tileSize.height; - case TR_TILE_BORDER: - return tileBorder; - case TR_IMAGE_WIDTH: - return imageSize.width; - case TR_IMAGE_HEIGHT: - return imageSize.height; - case TR_ROWS: - return rows; - case TR_COLUMNS: - return columns; - case TR_CURRENT_ROW: - if( currentTile < 0 ) - return -1; - else - return currentRow; - case TR_CURRENT_COLUMN: - if( currentTile < 0 ) - return -1; - else - return currentColumn; - case TR_CURRENT_TILE_WIDTH: - return currentTileWidth; - case TR_CURRENT_TILE_HEIGHT: - return currentTileHeight; - case TR_ROW_ORDER: - return rowOrder; - default: - throw new IllegalArgumentException("Invalid enumerant as argument"); - } - } - - /** - * Sets the order of row traversal - * - * @param order - * The row traversal order, must be - * eitherTR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP - */ - public void setRowOrder( int order ) - { - if (order == TR_TOP_TO_BOTTOM || order == TR_BOTTOM_TO_TOP) { - rowOrder = order; - } else { - throw new IllegalArgumentException("Must pass TR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP"); - } - } - - /** - * Sets the context to use an orthographic projection. Must be - * called before rendering the first tile - * - * @param left - * As in glOrtho - * @param right - * As in glOrtho - * @param bottom - * As in glOrtho - * @param top - * As in glOrtho - * @param zNear - * As in glOrtho - * @param zFar - * As in glOrtho - */ - public void trOrtho( double left, double right, double bottom, double top, double zNear, - double zFar ) - { - this.perspective = false; - this.left = left; - this.right = right; - this.bottom = bottom; - this.top = top; - this.near = zNear; - this.far = zFar; - } - - /** - * Sets the perspective projection frustrum. Must be called before - * rendering the first tile - * - * @param left - * As in glFrustrum - * @param right - * As in glFrustrum - * @param bottom - * As in glFrustrum - * @param top - * As in glFrustrum - * @param zNear - * As in glFrustrum - * @param zFar - * As in glFrustrum - */ - public void trFrustum( double left, double right, double bottom, double top, double zNear, - double zFar ) - { - this.perspective = true; - this.left = left; - this.right = right; - this.bottom = bottom; - this.top = top; - this.near = zNear; - this.far = zFar; - } - - /** - * Convenient way to specify a perspective projection - * - * @param fovy - * As in gluPerspective - * @param aspect - * As in gluPerspective - * @param zNear - * As in gluPerspective - * @param zFar - * As in gluPerspective - */ - public void trPerspective( double fovy, double aspect, double zNear, double zFar ) - { - double xmin, xmax, ymin, ymax; - ymax = zNear * Math.tan( fovy * 3.14159265 / 360.0 ); - ymin = -ymax; - xmin = ymin * aspect; - xmax = ymax * aspect; - trFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); - } - - /** - * Begins rendering a tile. The projection matrix stack should be - * left alone after calling this - * - * @param gl - * The gl context - */ - public void beginTile( GL2 gl ) - { - if (currentTile <= 0) { - setup(); - /* - * Save user's viewport, will be restored after last tile - * rendered - */ - gl.glGetIntegerv( GL2.GL_VIEWPORT, viewportSave, 0 ); - } - - /* which tile (by row and column) we're about to render */ - if (rowOrder == TR_BOTTOM_TO_TOP) { - currentRow = currentTile / columns; - currentColumn = currentTile % columns; - } else { - currentRow = rows - ( currentTile / columns ) - 1; - currentColumn = currentTile % columns; - } - assert ( currentRow < rows ); - assert ( currentColumn < columns ); - - int border = tileBorder; - - int th, tw; - - /* Compute actual size of this tile with border */ - if (currentRow < rows - 1) { - th = tileSize.height; - } else { - th = imageSize.height - ( rows - 1 ) * ( tileSizeNB.height ) + 2 * border; - } - - if (currentColumn < columns - 1) { - tw = tileSize.width; - } else { - tw = imageSize.width - ( columns - 1 ) * ( tileSizeNB.width ) + 2 * border; - } - - /* Save tile size, with border */ - currentTileWidth = tw; - currentTileHeight = th; - - gl.glViewport( 0, 0, tw, th ); - - /* save current matrix mode */ - int[] matrixMode = new int[ 1 ]; - gl.glGetIntegerv( GL2.GL_MATRIX_MODE, matrixMode, 0 ); - gl.glMatrixMode( GL2.GL_PROJECTION ); - gl.glLoadIdentity(); - - /* compute projection parameters */ - double l = - left + ( right - left ) * ( currentColumn * tileSizeNB.width - border ) - / imageSize.width; - double r = l + ( right - left ) * tw / imageSize.width; - double b = - bottom + ( top - bottom ) * ( currentRow * tileSizeNB.height - border ) - / imageSize.height; - double t = b + ( top - bottom ) * th / imageSize.height; - - if( perspective ) { - gl.glFrustum( l, r, b, t, near, far ); - } else { - gl.glOrtho( l, r, b, t, near, far ); - } - - /* restore user's matrix mode */ - gl.glMatrixMode( matrixMode[ 0 ] ); - } - - /** - * Must be called after rendering the scene - * - * @param gl - * the gl context - * @return true if there are more tiles to be rendered, false if - * the final image is complete - */ - public boolean endTile( GL2 gl ) - { - int[] prevRowLength = new int[ 1 ], prevSkipRows = new int[ 1 ], prevSkipPixels = new int[ 1 ], prevAlignment = - new int[ 1 ]; - - assert ( currentTile >= 0 ); - - // be sure OpenGL rendering is finished - gl.glFlush(); - - // save current glPixelStore values - gl.glGetIntegerv( GL2.GL_PACK_ROW_LENGTH, prevRowLength, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_SKIP_ROWS, prevSkipRows, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_SKIP_PIXELS, prevSkipPixels, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_ALIGNMENT, prevAlignment, 0 ); - - if( tileBuffer != null ) { - int srcX = tileBorder; - int srcY = tileBorder; - int srcWidth = tileSizeNB.width; - int srcHeight = tileSizeNB.height; - gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, tileFormat, tileType, tileBuffer ); - } - - if( imageBuffer != null ) { - int srcX = tileBorder; - int srcY = tileBorder; - int srcWidth = currentTileWidth - 2 * tileBorder; - int srcHeight = currentTileHeight - 2 * tileBorder; - int destX = tileSizeNB.width * currentColumn; - int destY = tileSizeNB.height * currentRow; - - /* setup pixel store for glReadPixels */ - gl.glPixelStorei( GL2.GL_PACK_ROW_LENGTH, imageSize.width ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_ROWS, destY ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_PIXELS, destX ); - gl.glPixelStorei( GL2.GL_PACK_ALIGNMENT, 1 ); - - /* read the tile into the final image */ - gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, imageFormat, imageType, imageBuffer ); - } - - /* restore previous glPixelStore values */ - gl.glPixelStorei( GL2.GL_PACK_ROW_LENGTH, prevRowLength[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_ROWS, prevSkipRows[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_PIXELS, prevSkipPixels[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_ALIGNMENT, prevAlignment[ 0 ] ); - - /* increment tile counter, return 1 if more tiles left to render */ - currentTile++; - if( currentTile >= rows * columns ) { - /* restore user's viewport */ - gl.glViewport( viewportSave[ 0 ], viewportSave[ 1 ], viewportSave[ 2 ], viewportSave[ 3 ] ); - currentTile = -1; /* all done */ - return false; - } else { - return true; - } - } - - /** - * Tile rendering causes problems with using glRasterPos3f, so you - * should use this replacement instead - * - * @param x - * As in glRasterPos3f - * @param y - * As in glRasterPos3f - * @param z - * As in glRasterPos3f - * @param gl - * The gl context - * @param glu - * A GLUgl2 object - */ - public void trRasterPos3f( float x, float y, float z, GL2 gl, GLUgl2 glu ) - { - if (currentTile < 0) { - /* not doing tile rendering right now. Let OpenGL do this. */ - gl.glRasterPos3f( x, y, z ); - } else { - double[] modelview = new double[ 16 ], proj = new double[ 16 ]; - int[] viewport = new int[ 4 ]; - double[] win = new double[3]; - - /* Get modelview, projection and viewport */ - gl.glGetDoublev( GL2.GL_MODELVIEW_MATRIX, modelview, 0 ); - gl.glGetDoublev( GL2.GL_PROJECTION_MATRIX, proj, 0 ); - viewport[ 0 ] = 0; - viewport[ 1 ] = 0; - viewport[ 2 ] = currentTileWidth; - viewport[ 3 ] = currentTileHeight; - - /* Project object coord to window coordinate */ - if( glu.gluProject( x, y, z, modelview, 0, proj, 0, viewport, 0, win, 0 ) ) { - - /* set raster pos to window coord (0,0) */ - gl.glMatrixMode( GL2.GL_MODELVIEW ); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glMatrixMode( GL2.GL_PROJECTION ); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glOrtho( 0.0, currentTileWidth, 0.0, currentTileHeight, 0.0, 1.0 ); - gl.glRasterPos3d( 0.0, 0.0, -win[ 2 ] ); - - /* - * Now use empty bitmap to adjust raster position to - * (winX,winY) - */ - { - byte[] bitmap = { 0 }; - gl.glBitmap( 1, 1, 0.0f, 0.0f, ( float ) win[ 0 ], ( float ) win[ 1 ], bitmap , 0 ); - } - - /* restore original matrices */ - gl.glPopMatrix(); /* proj */ - gl.glMatrixMode( GL2.GL_MODELVIEW ); - gl.glPopMatrix(); - } - } - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index f6b686d7e..3b8706a24 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; +import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -45,13 +46,18 @@ import java.util.Set; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL3; +import javax.media.opengl.GL4; import javax.media.opengl.GLES2; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.VersionNumber; /** * Convenient shader code class to use and instantiate vertex or fragment programs. @@ -59,42 +65,79 @@ import com.jogamp.common.util.IOUtil; * A documented example of how to use this code is available * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}. - * </p> + * </p> + * <p> + * Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER} + * was added since 2.2.1. + * </p> */ public class ShaderCode { - public static final boolean DEBUG = Debug.debug("GLSLCode"); public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>vp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; - - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>bvp</code> */ + + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; - - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>fp</code> */ + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>{@value}</code> */ + public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>{@value}</code> */ + public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_SOURCE = "tcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_SOURCE = "tep" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ; + + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; - - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>bfp</code> */ + + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ; - - /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>nvidia</code> */ + + /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>{@value}</code> */ public static final String SUB_PATH_NVIDIA = "nvidia" ; /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param source CharSequence array containing the shader sources, organized as <code>source[count][strings-per-shader]</code>. * May be either an immutable <code>String</code> - or mutable <code>StringBuilder</code> array. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>source.length</code> do not match */ - public ShaderCode(int type, int count, CharSequence[][] source) { + public ShaderCode(final int type, final int count, final CharSequence[][] source) { if(source.length != count) { throw new IllegalArgumentException("shader number ("+count+") and sourceFiles array ("+source.length+") of different lenght."); } switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -113,14 +156,18 @@ public class ShaderCode { } /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders - * @param binary binary buffer containing the shader binaries, + * @param binary binary buffer containing the shader binaries, */ - public ShaderCode(int type, int count, int binFormat, Buffer binary) { + public ShaderCode(final int type, final int count, final int binFormat, final Buffer binary) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -136,23 +183,24 @@ public class ShaderCode { /** * Creates a complete {@link ShaderCode} object while reading all shader source of <code>sourceFiles</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderSource(Class, String)}. - * + * * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed. - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>sourceFiles.length</code> do not match * @see #readShaderSource(Class, String) */ - public static ShaderCode create(GL2ES2 gl, int type, int count, Class<?> context, String[] sourceFiles, boolean mutableStringBuilder) { + public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class<?> context, final String[] sourceFiles, final boolean mutableStringBuilder) { if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) { return null; } @@ -164,7 +212,7 @@ public class ShaderCode { for(int i=0; i<sourceFiles.length; i++) { try { shaderSources[i][0] = readShaderSource(context, sourceFiles[i], mutableStringBuilder); - } catch (IOException ioe) { + } catch (final IOException ioe) { throw new RuntimeException("readShaderSource("+sourceFiles[i]+") error: ", ioe); } if(null == shaderSources[i][0]) { @@ -181,22 +229,23 @@ public class ShaderCode { /** * Creates a complete {@link ShaderCode} object while reading the shader binary of <code>binaryFile</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. - * - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}. * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> - * + * * @see #readShaderBinary(Class, String) * @see ShaderUtil#getShaderBinaryFormats(GL) - */ - public static ShaderCode create(int type, int count, Class<?> context, int binFormat, String binaryFile) { + */ + public static ShaderCode create(final int type, final int count, final Class<?> context, int binFormat, final String binaryFile) { ByteBuffer shaderBinary = null; if(null!=binaryFile && 0<=binFormat) { try { shaderBinary = readShaderBinary(context, binaryFile); - } catch (IOException ioe) { + } catch (final IOException ioe) { throw new RuntimeException("readShaderBinary("+binaryFile+") error: ", ioe); } if(null == shaderBinary) { @@ -214,40 +263,55 @@ public class ShaderCode { * <ul> * <li>Source<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_SOURCE}</li> - * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li></ul></li> + * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_SOURCE}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_SOURCE}</li> + * </ul></li> * <li>Binary<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_BINARY}</li> - * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li></ul></li> - * </ul> - * @param binary true for a binary resource, false for a source resource - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} - * + * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_BINARY}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_BINARY}</li> + * </ul></li> + * </ul> + * @param binary true for a binary resource, false for a source resource + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. + * * @throws GLException if <code>type</code> is not supported - * + * * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ - public static String getFileSuffix(boolean binary, int type) { + public static String getFileSuffix(final boolean binary, final int type) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: return binary?SUFFIX_VERTEX_BINARY:SUFFIX_VERTEX_SOURCE; case GL2ES2.GL_FRAGMENT_SHADER: return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE; + case GL3.GL_GEOMETRY_SHADER: + return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE; + case GL4.GL_TESS_CONTROL_SHADER: + return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE; + case GL4.GL_TESS_EVALUATION_SHADER: + return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE; default: throw new GLException("illegal shader type: "+type); } } - /** + /** * Returns a unique relative path for binary shader resources as follows: * <ul> * <li>{@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: {@link #SUB_PATH_NVIDIA}</li> * </ul> - * + * * @throws GLException if <code>binFormat</code> is not supported - * + * * @see #create(GL2ES2, int, Class, String, String, String, boolean) - */ - public static String getBinarySubPath(int binFormat) { + */ + public static String getBinarySubPath(final int binFormat) { switch (binFormat) { case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV: return SUB_PATH_NVIDIA; @@ -257,42 +321,42 @@ public class ShaderCode { } /** - * Convenient creation method for instantiating a complete {@link ShaderCode} object - * either from source code using {@link #create(GL2ES2, int, int, Class, String[])}, + * Convenient creation method for instantiating a complete {@link ShaderCode} object + * either from source code using {@link #create(GL2ES2, int, int, Class, String[])}, * or from a binary code using {@link #create(int, int, Class, int, String)}, * whatever is available first. * <p> - * The source and binary location names are expected w/o suffixes which are + * The source and binary location names are expected w/o suffixes which are * resolved and appended using {@link #getFileSuffix(boolean, int)}. * </p> * <p> * Additionally, the binary resource is expected within a subfolder of <code>binRoot</code> * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}. * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated - * using the binary subfolder, the first existing resource is being used. + * using the binary subfolder, the first existing resource is being used. * </p> - * + * * Example: * <pre> * Your std JVM layout (plain or within a JAR): - * + * * org/test/glsl/MyShaderTest.class * org/test/glsl/shader/vertex.vp * org/test/glsl/shader/fragment.fp * org/test/glsl/shader/bin/nvidia/vertex.bvp * org/test/glsl/shader/bin/nvidia/fragment.bfp - * + * * Your Android APK layout: - * + * * classes.dex * assets/org/test/glsl/shader/vertex.vp * assets/org/test/glsl/shader/fragment.fp * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp * ... - * + * * Your invocation in org/test/glsl/MyShaderTest.java: - * + * * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(), * "shader", new String[] { "vertex" }, "shader/bin", "vertex"); * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(), @@ -303,49 +367,56 @@ public class ShaderCode { * st.attachShaderProgram(gl, sp0, true); * </pre> * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}. - * + * * <p> * The location is finally being resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. * </p> - * + * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source and binary location - * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> + * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> optional * @param srcBasenames basenames w/o path or suffix relative to <code>srcRoot</code> for the shader's source code * @param binRoot relative <i>root</i> path for <code>binBasenames</code> * @param binBasename basename w/o path or suffix relative to <code>binRoot</code> for the shader's binary code * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>srcBasenames.length</code> do not match - * + * * @see #create(GL2ES2, int, int, Class, String[]) * @see #create(int, int, Class, int, String) * @see #readShaderSource(Class, String) * @see #getFileSuffix(boolean, int) * @see ShaderUtil#getShaderBinaryFormats(GL) * @see #getBinarySubPath(int) - */ - public static ShaderCode create(GL2ES2 gl, int type, int count, Class<?> context, - String srcRoot, String[] srcBasenames, String binRoot, String binBasename, - boolean mutableStringBuilder) { + */ + public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class<?> context, + final String srcRoot, final String[] srcBasenames, final String binRoot, final String binBasename, + final boolean mutableStringBuilder) { ShaderCode res = null; final String srcPath[]; String srcPathString = null; String binFileName = null; - if(null!=srcRoot && null!=srcBasenames && ShaderUtil.isShaderCompilerAvailable(gl)) { + if( null!=srcBasenames && ShaderUtil.isShaderCompilerAvailable(gl) ) { srcPath = new String[srcBasenames.length]; final String srcSuffix = getFileSuffix(false, type); - for(int i=0; i<srcPath.length; i++) { - srcPath[i] = srcRoot + '/' + srcBasenames[i] + "." + srcSuffix; + if( null != srcRoot && srcRoot.length() > 0 ) { + for(int i=0; i<srcPath.length; i++) { + srcPath[i] = srcRoot + '/' + srcBasenames[i] + "." + srcSuffix; + } + } else { + for(int i=0; i<srcPath.length; i++) { + srcPath[i] = srcBasenames[i] + "." + srcSuffix; + } } res = create(gl, type, count, context, srcPath, mutableStringBuilder); if(null!=res) { @@ -355,12 +426,12 @@ public class ShaderCode { } else { srcPath = null; } - if(null!=binRoot && null!=binBasename) { - Set<Integer> binFmts = ShaderUtil.getShaderBinaryFormats(gl); + if( null!=binBasename ) { + final Set<Integer> binFmts = ShaderUtil.getShaderBinaryFormats(gl); final String binSuffix = getFileSuffix(true, type); - for(Iterator<Integer> iter=binFmts.iterator(); iter.hasNext(); ) { - int bFmt = iter.next().intValue(); - String bFmtPath = getBinarySubPath(bFmt); + for(final Iterator<Integer> iter=binFmts.iterator(); iter.hasNext(); ) { + final int bFmt = iter.next().intValue(); + final String bFmtPath = getBinarySubPath(bFmt); if(null==bFmtPath) continue; binFileName = binRoot + '/' + bFmtPath + '/' + binBasename + "." + binSuffix; res = create(type, count, context, bFmt, binFileName); @@ -374,30 +445,29 @@ public class ShaderCode { } /** - * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String)}. - * <br> - * + * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, boolean)}. + * <p> * Example: * <pre> * Your std JVM layout (plain or within a JAR): - * + * * org/test/glsl/MyShaderTest.class * org/test/glsl/shader/vertex.vp * org/test/glsl/shader/fragment.fp * org/test/glsl/shader/bin/nvidia/vertex.bvp * org/test/glsl/shader/bin/nvidia/fragment.bfp - * + * * Your Android APK layout: - * + * * classes.dex * assets/org/test/glsl/shader/vertex.vp * assets/org/test/glsl/shader/fragment.fp * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp * ... - * + * * Your invocation in org/test/glsl/MyShaderTest.java: - * + * * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), * "shader", "shader/bin", "vertex"); * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), @@ -407,31 +477,33 @@ public class ShaderCode { * sp0.add(gl, fp0, System.err); * st.attachShaderProgram(gl, sp0, true); * </pre> - * + * </p> + * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param context class used to help resolving the source and binary location - * @param srcRoot relative <i>root</i> path for <code>basename</code> + * @param srcRoot relative <i>root</i> path for <code>basename</code> optional * @param binRoot relative <i>root</i> path for <code>basename</code> * @param mutableStringBuilder TODO * @param basenames basename w/o path or suffix relative to <code>srcRoot</code> and <code>binRoot</code> * for the shader's source and binary code. * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IllegalArgumentException if <code>count</count> is not 1 - * + * * @see #create(GL2ES2, int, int, Class, String, String[], String, String) - */ - public static ShaderCode create(GL2ES2 gl, int type, Class<?> context, - String srcRoot, String binRoot, String basename, boolean mutableStringBuilder) { - return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); + */ + public static ShaderCode create(final GL2ES2 gl, final int type, final Class<?> context, + final String srcRoot, final String binRoot, final String basename, final boolean mutableStringBuilder) { + return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); } - + /** * returns the uniq shader id as an integer */ @@ -440,12 +512,18 @@ public class ShaderCode { public int shaderType() { return shaderType; } public String shaderTypeStr() { return shaderTypeStr(shaderType); } - public static String shaderTypeStr(int type) { + public static String shaderTypeStr(final int type) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: return "VERTEX_SHADER"; case GL2ES2.GL_FRAGMENT_SHADER: return "FRAGMENT_SHADER"; + case GL3.GL_GEOMETRY_SHADER: + return "GEOMETRY_SHADER"; + case GL4.GL_TESS_CONTROL_SHADER: + return "TESS_CONTROL_SHADER"; + case GL4.GL_TESS_EVALUATION_SHADER: + return "TESS_EVALUATION_SHADER"; } return "UNKNOWN_SHADER"; } @@ -458,15 +536,16 @@ public class ShaderCode { public IntBuffer shader() { return shader; } - public boolean compile(GL2ES2 gl) { + public boolean compile(final GL2ES2 gl) { return compile(gl, null); } - public boolean compile(GL2ES2 gl, PrintStream verboseOut) { + public boolean compile(final GL2ES2 gl, final PrintStream verboseOut) { if(isValid()) return true; // Create & Compile the vertex/fragment shader objects if(null!=shaderSource) { if(DEBUG_CODE) { + System.err.println("ShaderCode.compile:"); dumpShaderSource(System.err); } valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType, @@ -480,7 +559,7 @@ public class ShaderCode { return valid; } - public void destroy(GL2ES2 gl) { + public void destroy(final GL2ES2 gl) { if(isValid()) { if(null!=gl) { ShaderUtil.deleteShader(gl, shader()); @@ -497,18 +576,21 @@ public class ShaderCode { id=-1; } - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if(this==obj) { return true; } if(obj instanceof ShaderCode) { return id()==((ShaderCode)obj).id(); } return false; } + @Override public int hashCode() { return id; } + @Override public String toString() { - StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); + final StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); for(int i=0; i<shader.remaining(); i++) { buf.append(" "+shader.get(i)); } @@ -520,12 +602,12 @@ public class ShaderCode { return buf.toString(); } - public void dumpShaderSource(PrintStream out) { + public void dumpShaderSource(final PrintStream out) { if(null==shaderSource) { out.println("<no shader source>"); return; } - final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + final int sourceCount = shaderSource.length; final int shaderCount = (null!=shader)?shader.capacity():0; for(int i=0; i<shaderCount; i++) { out.println(""); @@ -534,9 +616,9 @@ public class ShaderCode { if(i>=sourceCount) { out.println("<no shader source>"); } else { - CharSequence[] src = shaderSource[i]; + final CharSequence[] src = shaderSource[i]; int lineno=0; - + for(int j=0; j<src.length; j++) { out.printf("%4d: // Segment %d/%d: \n", lineno, j, src.length); final BufferedReader reader = new BufferedReader(new StringReader(src[j].toString())); @@ -546,28 +628,28 @@ public class ShaderCode { lineno++; out.printf("%4d: %s\n", lineno, line); } - } catch (IOException e) { /* impossible .. StringReader */ } + } catch (final IOException e) { /* impossible .. StringReader */ } } } out.println("--------------------------------------------------------------"); } } - + /** * Adds <code>data</code> after the line containing <code>tag</code>. * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param shaderIdx the shader index to be used. * @param tag search string * @param fromIndex start search <code>tag</code> begininig with this index * @param data the text to be inserted. Shall end with an EOL '\n' character. * @return index after the inserted <code>data</code> - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ - public int insertShaderSource(int shaderIdx, String tag, int fromIndex, CharSequence data) { + public int insertShaderSource(final int shaderIdx, final String tag, final int fromIndex, final CharSequence data) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } @@ -575,10 +657,10 @@ public class ShaderCode { if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } - final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + final int sourceCount = shaderSource.length; if(shaderIdx>=sourceCount) { throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); - } + } final CharSequence[] src = shaderSource[shaderIdx]; int curEndIndex = 0; for(int j=0; j<src.length; j++) { @@ -586,8 +668,8 @@ public class ShaderCode { throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName()); } final StringBuilder sb = (StringBuilder)src[j]; - curEndIndex += sb.length(); - if(fromIndex < curEndIndex) { + curEndIndex += sb.length(); + if(fromIndex < curEndIndex) { int insertIdx = sb.indexOf(tag, fromIndex); if(0<=insertIdx) { insertIdx += tag.length(); @@ -613,18 +695,18 @@ public class ShaderCode { * Replaces <code>oldName</code> with <code>newName</code> in all shader sources. * <p> * In case <code>oldName</code> and <code>newName</code> are equal, no action is performed. - * </p> + * </p> * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param oldName the to be replace string * @param newName the replacement string * @return the number of replacements - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ - public int replaceInShaderSource(String oldName, String newName) { + public int replaceInShaderSource(final String oldName, final String newName) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } @@ -634,7 +716,7 @@ public class ShaderCode { final int oldNameLen = oldName.length(); final int newNameLen = newName.length(); int num = 0; - final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + final int sourceCount = shaderSource.length; for(int shaderIdx = 0; shaderIdx<sourceCount; shaderIdx++) { final CharSequence[] src = shaderSource[shaderIdx]; for(int j=0; j<src.length; j++) { @@ -644,9 +726,9 @@ public class ShaderCode { final StringBuilder sb = (StringBuilder)src[j]; int curPos = 0; while(curPos<sb.length()-oldNameLen+1) { - int startIdx = sb.indexOf(oldName, curPos); + final int startIdx = sb.indexOf(oldName, curPos); if(0<=startIdx) { - int endIdx = startIdx + oldNameLen; + final int endIdx = startIdx + oldNameLen; sb.replace(startIdx, endIdx, newName); curPos = startIdx + newNameLen; num++; @@ -658,21 +740,21 @@ public class ShaderCode { } return num; } - + /** - * Adds <code>data</code> at <code>offset</code> in shader source for shader <code>shaderIdx</code>. + * Adds <code>data</code> at <code>position</code> in shader source for shader <code>shaderIdx</code>. * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param shaderIdx the shader index to be used. - * @param position in shader source segments of shader <code>shaderIdx</code> - * @param data the text to be inserted. Shall end with an EOL '\n' character. + * @param position in shader source segments of shader <code>shaderIdx</code>, -1 will append data + * @param data the text to be inserted. Shall end with an EOL '\n' character * @return index after the inserted <code>data</code> - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ - public int insertShaderSource(int shaderIdx, int position, CharSequence data) { + public int insertShaderSource(final int shaderIdx, int position, final CharSequence data) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } @@ -680,10 +762,10 @@ public class ShaderCode { if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } - final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + final int sourceCount = shaderSource.length; if(shaderIdx>=sourceCount) { throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); - } + } final CharSequence[] src = shaderSource[shaderIdx]; int curEndIndex = 0; for(int j=0; j<src.length; j++) { @@ -691,16 +773,50 @@ public class ShaderCode { throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName()); } final StringBuilder sb = (StringBuilder)src[j]; - curEndIndex += sb.length(); - if(position < curEndIndex) { + curEndIndex += sb.length(); + if( 0 > position && j == src.length - 1 ) { + position = curEndIndex; + } + if(0 <= position && position <= curEndIndex ) { sb.insert(position, data); return position+data.length(); } } - return -1; + return position; } - private static int readShaderSource(Class<?> context, URLConnection conn, StringBuilder result, int lineno) throws IOException { + /** + * Adds shader source located in <code>path</code>, + * either relative to the <code>context</code> class or absolute <i>as-is</i> + * at <code>position</code> in shader source for shader <code>shaderIdx</code>. + * <p> + * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, + * see {@link IOUtil#getResource(Class, String)}. + * </p> + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * + * @param shaderIdx the shader index to be used. + * @param position in shader source segments of shader <code>shaderIdx</code>, -1 will append data + * @param context class used to help resolve the source location + * @param path location of shader source + * @return index after the inserted code. + * @throws IOException + * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> + * @see IOUtil#getResource(Class, String) + */ + public int insertShaderSource(final int shaderIdx, final int position, final Class<?> context, final String path) throws IOException { + final CharSequence data = readShaderSource(context, path, true); + if( null != data ) { + return insertShaderSource(shaderIdx, position, data); + } else { + return position; + } + } + + @SuppressWarnings("resource") + private static int readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result, int lineno) throws IOException { if(DEBUG_CODE) { if(0 == lineno) { result.append("// "+conn.getURL().toExternalForm()+"\n"); @@ -714,14 +830,15 @@ public class ShaderCode { while ((line = reader.readLine()) != null) { lineno++; if (line.startsWith("#include ")) { - String includeFile = line.substring(9).trim(); + final String includeFile = line.substring(9).trim(); URLConnection nextConn = null; - + // Try relative of current shader location - nextConn = IOUtil.openURL(IOUtil.getRelativeOf(conn.getURL(), includeFile), "ShaderCode.relativeOf "); + final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL )); + nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf "); if (nextConn == null) { // Try relative of class and absolute - nextConn = IOUtil.getResource(context, includeFile); + nextConn = IOUtil.getResource(context, includeFile); } if (nextConn == null) { // Fail @@ -732,6 +849,8 @@ public class ShaderCode { result.append(line + "\n"); } } + } catch (final URISyntaxException e) { + throw new IOException(e); } finally { IOUtil.close(reader, false); } @@ -739,16 +858,16 @@ public class ShaderCode { } /** - * + * * @param context * @param conn * @param result * @throws IOException */ - public static void readShaderSource(Class<?> context, URLConnection conn, StringBuilder result) throws IOException { + public static void readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result) throws IOException { readShaderSource(context, conn, result, 0); } - + /** * Reads shader source located in <code>path</code>, * either relative to the <code>context</code> class or absolute <i>as-is</i>. @@ -756,44 +875,44 @@ public class ShaderCode { * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. * </p> - * + * * @param context class used to help resolve the source location * @param path location of shader source * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * @throws IOException - * + * @throws IOException + * * @see IOUtil#getResource(Class, String) - */ - public static CharSequence readShaderSource(Class<?> context, String path, boolean mutableStringBuilder) throws IOException { - URLConnection conn = IOUtil.getResource(context, path); + */ + public static CharSequence readShaderSource(final Class<?> context, final String path, final boolean mutableStringBuilder) throws IOException { + final URLConnection conn = IOUtil.getResource(context, path); if (conn == null) { return null; } - StringBuilder result = new StringBuilder(); + final StringBuilder result = new StringBuilder(); readShaderSource(context, conn, result); return mutableStringBuilder ? result : result.toString(); } /** - * Reads shader binary located in <code>path</code>, + * Reads shader binary located in <code>path</code>, * either relative to the <code>context</code> class or absolute <i>as-is</i>. * <p> * Final location lookup is perfomed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. * </p> - * + * * @param context class used to help resolve the source location * @param path location of shader binary - * @throws IOException - * + * @throws IOException + * * @see IOUtil#getResource(Class, String) */ - public static ByteBuffer readShaderBinary(Class<?> context, String path) throws IOException { + public static ByteBuffer readShaderBinary(final Class<?> context, final String path) throws IOException { final URLConnection conn = IOUtil.getResource(context, path); if (conn == null) { return null; @@ -806,6 +925,192 @@ public class ShaderCode { } } + // Shall we use: #ifdef GL_FRAGMENT_PRECISION_HIGH .. #endif for using highp in fragment shader if avail ? + /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es2_default_precision_vp} */ + public static final String es2_default_precision_vp = "\nprecision highp float;\nprecision highp int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n"; + /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es2_default_precision_fp} */ + public static final String es2_default_precision_fp = "\nprecision mediump float;\nprecision mediump int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n"; + + /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */ + public static final String es3_default_precision_vp = es2_default_precision_vp; + /** + * Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp}, + * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp}, + * due to ES 3.x requirements of using same precision for uniforms! + */ + public static final String es3_default_precision_fp = es3_default_precision_vp; + + /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader} or {@link GL3#GL_GEOMETRY_SHADER geometry-shader}: {@value #gl3_default_precision_vp_gp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ + public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n"; + /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #gl3_default_precision_fp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ + public static final String gl3_default_precision_fp = "\nprecision highp float;\nprecision mediump int;\n/*precision mediump sampler2D;*/\n"; + + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String REQUIRE = "require"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String ENABLE = "enable"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String DISABLE = "disable"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String WARN = "warn"; + + /** + * Creates a GLSL extension directive. + * <p> + * Prefer {@link #ENABLE} over {@link #REQUIRE}, since the latter will force a failure if not supported. + * </p> + * + * @param extensionName + * @param behavior shall be either {@link #REQUIRE}, {@link #ENABLE}, {@link #DISABLE} or {@link #WARN} + * @return the complete extension directive + */ + public static String createExtensionDirective(final String extensionName, final String behavior) { + return "#extension " + extensionName + " : " + behavior + "\n"; + } + + /** + * Add GLSL version at the head of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addGLSLVersion(final GL2ES2 gl) { + return insertShaderSource(0, 0, gl.getContext().getGLSLVersionString()); + } + + /** + * Adds default precision to source code at given position if required, i.e. + * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp}, + * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none, + * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param pos position within this mutable shader source. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addDefaultShaderPrecision(final GL2ES2 gl, int pos) { + final String defaultPrecision; + if( gl.isGLES3() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es3_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( gl.isGLES2() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es2_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es2_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( requiresGL3DefaultPrecision(gl) ) { + // GLSL [ 1.30 .. 1.50 [ needs at least fragement float default precision! + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: + defaultPrecision = gl3_default_precision_vp_gp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = gl3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else { + defaultPrecision = null; + } + if( null != defaultPrecision ) { + pos = insertShaderSource(0, pos, defaultPrecision); + } + return pos; + } + + /** Returns true, if GLSL version requires default precision, i.e. ES2 or GLSL [1.30 .. 1.50[. */ + public static final boolean requiresDefaultPrecision(final GL2ES2 gl) { + if( gl.isGLES() ) { + return true; + } + return requiresGL3DefaultPrecision(gl); + } + + /** Returns true, if GL3 GLSL version requires default precision, i.e. GLSL [1.30 .. 1.50[. */ + public static final boolean requiresGL3DefaultPrecision(final GL2ES2 gl) { + if( gl.isGL3() ) { + final VersionNumber glslVersion = gl.getContext().getGLSLVersionNumber(); + return glslVersion.compareTo(GLContext.Version130) >= 0 && glslVersion.compareTo(GLContext.Version150) < 0 ; + } else { + return false; + } + } + + /** + * Default customization of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param addDefaultPrecision if <code>true</code> default precision source code line(s) are added, i.e. + * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp}, + * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none, + * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + * @see #addGLSLVersion(GL2ES2) + * @see #addDefaultShaderPrecision(GL2ES2, int) + */ + public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final boolean addDefaultPrecision) { + int pos; + if( preludeVersion ) { + pos = addGLSLVersion(gl); + } else { + pos = 0; + } + if( addDefaultPrecision ) { + pos = addDefaultShaderPrecision(gl, pos); + } + return pos; + } + + /** + * Default customization of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param esDefaultPrecision optional default precision source code line(s) preluded if not null and if {@link GL#isGLES()}. + * You may use {@link #es2_default_precision_fp} for fragment shader and {@link #es2_default_precision_vp} for vertex shader. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + * @see #addGLSLVersion(GL2ES2) + * @see #addDefaultShaderPrecision(GL2ES2, int) + */ + public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final String esDefaultPrecision) { + int pos; + if( preludeVersion ) { + pos = addGLSLVersion(gl); + } else { + pos = 0; + } + if( gl.isGLES() && null != esDefaultPrecision ) { + pos = insertShaderSource(0, pos, esDefaultPrecision); + } else { + pos = addDefaultShaderPrecision(gl, pos); + } + return pos; + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java index 14ea7d2b8..a9b0bddc6 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java @@ -37,7 +37,7 @@ import java.util.Iterator; import java.io.PrintStream; public class ShaderProgram { - + public ShaderProgram() { id = getNextID(); } @@ -50,12 +50,13 @@ public class ShaderProgram { return programInUse; } + /** Returns the shader program name, which is non zero if valid. */ public int program() { return shaderProgram; } /** * returns the uniq shader id as an integer */ - public int id() { return id; } + public int id() { return id; } /** * Detaches all shader codes and deletes the program. @@ -64,7 +65,7 @@ public class ShaderProgram { * * @see #release(GL2ES2, boolean) */ - public synchronized void destroy(GL2ES2 gl) { + public synchronized void destroy(final GL2ES2 gl) { release(gl, true); } @@ -75,7 +76,7 @@ public class ShaderProgram { * * @see #release(GL2ES2, boolean) */ - public synchronized void release(GL2ES2 gl) { + public synchronized void release(final GL2ES2 gl) { release(gl, false); } @@ -83,10 +84,12 @@ public class ShaderProgram { * Detaches all shader codes and deletes the program. * If <code>destroyShaderCode</code> is true it destroys the shader codes as well. */ - public synchronized void release(GL2ES2 gl, boolean destroyShaderCode) { - useProgram(gl, false); - for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { - ShaderCode shaderCode = iter.next(); + public synchronized void release(final GL2ES2 gl, final boolean destroyShaderCode) { + if( programLinked ) { + useProgram(gl, false); + } + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + final ShaderCode shaderCode = iter.next(); if(attachedShaderCode.remove(shaderCode)) { ShaderUtil.detachShader(gl, shaderProgram, shaderCode.shader()); } @@ -96,9 +99,9 @@ public class ShaderProgram { } allShaderCode.clear(); attachedShaderCode.clear(); - if(0<=shaderProgram) { + if( 0 != shaderProgram ) { gl.glDeleteProgram(shaderProgram); - shaderProgram=-1; + shaderProgram=0; } } @@ -108,26 +111,26 @@ public class ShaderProgram { /** * Adds a new shader to this program. - * + * * <p>This command does not compile and attach the shader, * use {@link #add(GL2ES2, ShaderCode)} for this purpose.</p> */ - public synchronized void add(ShaderCode shaderCode) throws GLException { + public synchronized void add(final ShaderCode shaderCode) throws GLException { allShaderCode.add(shaderCode); } - public synchronized boolean contains(ShaderCode shaderCode) { + public synchronized boolean contains(final ShaderCode shaderCode) { return allShaderCode.contains(shaderCode); } - + /** * Warning slow O(n) operation .. * @param id * @return */ - public synchronized ShaderCode getShader(int id) { - for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { - ShaderCode shaderCode = iter.next(); + public synchronized ShaderCode getShader(final int id) { + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + final ShaderCode shaderCode = iter.next(); if(shaderCode.id() == id) { return shaderCode; } @@ -140,30 +143,33 @@ public class ShaderProgram { // /** - * Creates the empty GL program object using {@link GL2ES2#glCreateProgram()} - * + * Creates the empty GL program object using {@link GL2ES2#glCreateProgram()}, + * if not already created. + * * @param gl + * @return true if shader program is valid, i.e. not zero */ - public synchronized final void init(GL2ES2 gl) { - if(0>shaderProgram) { + public synchronized final boolean init(final GL2ES2 gl) { + if( 0 == shaderProgram ) { shaderProgram = gl.glCreateProgram(); } + return 0 != shaderProgram; } - + /** * Adds a new shader to a this non running program. * * <p>Compiles and attaches the shader, if not done yet.</p> - * + * * @return true if the shader was successfully added, false if compilation failed. */ - public synchronized boolean add(GL2ES2 gl, ShaderCode shaderCode, PrintStream verboseOut) { - init(gl); + public synchronized boolean add(final GL2ES2 gl, final ShaderCode shaderCode, final PrintStream verboseOut) { + if( !init(gl) ) { return false; } if( allShaderCode.add(shaderCode) ) { - if(!shaderCode.compile(gl, verboseOut)) { + if( !shaderCode.compile(gl, verboseOut) ) { return false; } - if(attachedShaderCode.add(shaderCode)) { + if( attachedShaderCode.add(shaderCode) ) { ShaderUtil.attachShader(gl, shaderProgram, shaderCode.shader()); } } @@ -173,11 +179,11 @@ public class ShaderProgram { /** * Replace a shader in a program and re-links the program. * - * @param gl + * @param gl * @param oldShader the to be replace Shader * @param newShader the new ShaderCode * @param verboseOut the optional verbose output stream - * + * * @return true if all steps are valid, shader compilation, attachment and linking; otherwise false. * * @see ShaderState#glEnableVertexAttribArray @@ -189,32 +195,30 @@ public class ShaderProgram { * @see ShaderState#glResetAllVertexAttributes * @see ShaderState#glResetAllVertexAttributes */ - public synchronized boolean replaceShader(GL2ES2 gl, ShaderCode oldShader, ShaderCode newShader, PrintStream verboseOut) { - init(gl); - - if(!newShader.compile(gl, verboseOut)) { + public synchronized boolean replaceShader(final GL2ES2 gl, final ShaderCode oldShader, final ShaderCode newShader, final PrintStream verboseOut) { + if(!init(gl) || !newShader.compile(gl, verboseOut)) { return false; } - - boolean shaderWasInUse = inUse(); + + final boolean shaderWasInUse = inUse(); if(shaderWasInUse) { useProgram(gl, false); } - + if(null != oldShader && allShaderCode.remove(oldShader)) { if(attachedShaderCode.remove(oldShader)) { ShaderUtil.detachShader(gl, shaderProgram, oldShader.shader()); } } - + add(newShader); if(attachedShaderCode.add(newShader)) { ShaderUtil.attachShader(gl, shaderProgram, newShader.shader()); } - + gl.glLinkProgram(shaderProgram); - - programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, System.err); + + programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut); if ( programLinked && shaderWasInUse ) { useProgram(gl, true); } @@ -223,23 +227,27 @@ public class ShaderProgram { /** * Links the shader code to the program. - * + * * <p>Compiles and attaches the shader code to the program if not done by yet</p> - * + * * <p>Within this process, all GL resources (shader and program objects) are created if necessary.</p> - * + * * @param gl * @param verboseOut * @return true if program was successfully linked and is valid, otherwise false - * + * * @see #init(GL2ES2) */ - public synchronized boolean link(GL2ES2 gl, PrintStream verboseOut) { - init(gl); + public synchronized boolean link(final GL2ES2 gl, final PrintStream verboseOut) { + if( !init(gl) ) { + programLinked = false; // mark unlinked due to user attempt to [re]link + return false; + } - for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { final ShaderCode shaderCode = iter.next(); if(!shaderCode.compile(gl, verboseOut)) { + programLinked = false; // mark unlinked due to user attempt to [re]link return false; } if(attachedShaderCode.add(shaderCode)) { @@ -250,12 +258,13 @@ public class ShaderProgram { // Link the program gl.glLinkProgram(shaderProgram); - programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, System.err); + programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut); return programLinked; } - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { if(this == obj) { return true; } if(obj instanceof ShaderProgram) { return id()==((ShaderProgram)obj).id(); @@ -263,6 +272,7 @@ public class ShaderProgram { return false; } + @Override public int hashCode() { return id; } @@ -273,13 +283,14 @@ public class ShaderProgram { } sb.append("ShaderProgram[id=").append(id); sb.append(", linked="+programLinked+", inUse="+programInUse+", program: "+shaderProgram+","); - for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { sb.append(Platform.getNewline()).append(" ").append(iter.next()); } sb.append("]"); return sb; } - + + @Override public String toString() { return toString(null).toString(); } @@ -288,27 +299,33 @@ public class ShaderProgram { * Performs {@link GL2ES2#glValidateProgram(int)} via {@link ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream)}. * @see ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream) **/ - public synchronized boolean validateProgram(GL2ES2 gl, PrintStream verboseOut) { + public synchronized boolean validateProgram(final GL2ES2 gl, final PrintStream verboseOut) { return ShaderUtil.isProgramExecStatusValid(gl, shaderProgram, verboseOut); } - - public synchronized void useProgram(GL2ES2 gl, boolean on) { + + public synchronized void useProgram(final GL2ES2 gl, boolean on) { if(!programLinked) { throw new GLException("Program is not linked"); } if(programInUse==on) { return; } - gl.glUseProgram(on?shaderProgram:0); + if( 0 == shaderProgram ) { + on = false; + } + gl.glUseProgram( on ? shaderProgram : 0 ); programInUse = on; } + public synchronized void notifyNotInUse() { + programInUse = false; + } - protected boolean programLinked = false; - protected boolean programInUse = false; - protected int shaderProgram=-1; - protected HashSet<ShaderCode> allShaderCode = new HashSet<ShaderCode>(); - protected HashSet<ShaderCode> attachedShaderCode = new HashSet<ShaderCode>(); - protected int id = -1; + private boolean programLinked = false; + private boolean programInUse = false; + private int shaderProgram = 0; // non zero is valid! + private final HashSet<ShaderCode> allShaderCode = new HashSet<ShaderCode>(); + private final HashSet<ShaderCode> attachedShaderCode = new HashSet<ShaderCode>(); + private final int id; private static synchronized int getNextID() { return nextID++; } - protected static int nextID = 1; + private static int nextID = 1; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java index 62082aacd..d758fc121 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java @@ -35,134 +35,85 @@ import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLArrayData; -import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLUniformData; import jogamp.opengl.Debug; import com.jogamp.common.os.Platform; -import com.jogamp.common.util.IntObjectHashMap; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.util.GLArrayDataEditable; +/** + * ShaderState allows to sharing data between shader programs, + * while updating the attribute and uniform locations when switching. + * <p> + * This allows seamless switching of programs using <i>almost</i> same data + * but performing different artifacts. + * </p> + * <p> + * A {@link #useProgram(GL2ES2, boolean) used} ShaderState is attached to the current GL context + * and can be retrieved via {@link #getShaderState(GL)}. + * </p> + */ public class ShaderState { - public static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.GLSLState", true); - private static final String currentStateKey = "jogamp.opengl.glsl.ShaderState" ; - - public ShaderState() { - } - - public boolean verbose() { return verbose; } + public static final boolean DEBUG; - public void setVerbose(boolean v) { verbose=v; } - - /** - * Fetches the current shader state from this thread (TLS) current GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public static ShaderState getCurrentShaderState() { - return getShaderState(GLContext.getCurrentGL()); + static { + Debug.initSingleton(); + DEBUG = PropertyAccess.isPropertyDefined("jogl.debug.GLSLState", true); } - /** - * Gets the shader state attached to the GL object's GLContext - * - * @param gl the GL object referencing the GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public static ShaderState getShaderState(GL gl) { - return (ShaderState) gl.getContext().getAttachedObject(currentStateKey); + public ShaderState() { } - /** - * Attaches the shader state to the GL object's GLContext - * - * @param gl the GL object referencing the GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public final ShaderState setShaderState(GL gl) { - return (ShaderState) gl.getContext().attachObject(currentStateKey, this); - } + public boolean verbose() { return verbose; } + + public void setVerbose(final boolean v) { verbose = DEBUG || v; } /** * Returns the attached user object for the given name to this ShaderState. */ - public final Object getAttachedObject(String name) { + public final Object getAttachedObject(final String name) { return attachedObjectsByString.get(name); } /** * Attach user object for the given name to this ShaderState. * Returns the previously set object or null. - * + * * @return the previous mapped object or null if none */ - public final Object attachObject(String name, Object obj) { + public final Object attachObject(final String name, final Object obj) { return attachedObjectsByString.put(name, obj); } /** * @param name name of the mapped object to detach - * + * * @return the previous mapped object or null if none */ - public final Object detachObject(String name) { + public final Object detachObject(final String name) { return attachedObjectsByString.remove(name); - } - - /** - * Returns the attached user object for the given name to this ShaderState. - */ - public final Object getAttachedObject(int name) { - return attachedObjectsByInt.get(name); } /** - * Attach user object for the given name to this ShaderState. - * Returns the previously set object or null. - */ - public final Object attachObject(int name, Object obj) { - return attachedObjectsByInt.put(name, obj); - } - - public final Object detachObject(int name) { - return attachedObjectsByInt.remove(name); - } - - /** * Turns the shader program on or off.<br> - * Puts this ShaderState to to the thread local storage (TLS), - * if <code>on</code> is <code>true</code>. * * @throws GLException if no program is attached * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ - public synchronized void useProgram(GL2ES2 gl, boolean on) throws GLException { - if(null==shaderProgram) { throw new GLException("No program is attached"); } + public synchronized void useProgram(final GL2ES2 gl, final boolean on) throws GLException { + if(null==shaderProgram) { throw new GLException("No program is attached"); } if(on) { - setShaderState(gl); if(shaderProgram.linked()) { shaderProgram.useProgram(gl, true); if(resetAllShaderData) { resetAllAttributes(gl); resetAllUniforms(gl); } - } else { + } else { if(resetAllShaderData) { setAllAttributes(gl); } @@ -174,7 +125,7 @@ public class ShaderState { resetAllUniforms(gl); } } - resetAllShaderData = false; + resetAllShaderData = false; } else { shaderProgram.useProgram(gl, false); } @@ -191,39 +142,42 @@ public class ShaderState { /** * Attach or switch a shader program * - * <p>Attaching a shader program the first time, + * <p>Attaching a shader program the first time, * as well as switching to another program on the fly, * while managing all attribute and uniform data.</p> - * + * * <p>[Re]sets all data and use program in case of a program switch.</p> - * + * * <p>Use program, {@link #useProgram(GL2ES2, boolean)}, * if <code>enable</code> is <code>true</code>.</p> - * + * + * @return true if shader program was attached, otherwise false (already attached) + * * @throws GLException if program was not linked and linking fails */ - public synchronized void attachShaderProgram(GL2ES2 gl, ShaderProgram prog, boolean enable) throws GLException { - if(DEBUG) { - int curId = (null!=shaderProgram)?shaderProgram.id():-1; - int newId = (null!=prog)?prog.id():-1; - System.err.println("Info: attachShaderProgram: "+curId+" -> "+newId+" (enable: "+enable+")\n\t"+shaderProgram+"\n\t"+prog); - if(verbose) { - Throwable tX = new Throwable("Info: attachShaderProgram: Trace"); - tX.printStackTrace(); + public synchronized boolean attachShaderProgram(final GL2ES2 gl, final ShaderProgram prog, final boolean enable) throws GLException { + if(verbose) { + final int curId = (null!=shaderProgram)?shaderProgram.id():-1; + final int newId = (null!=prog)?prog.id():-1; + System.err.println("ShaderState: attachShaderProgram: "+curId+" -> "+newId+" (enable: "+enable+")\n\t"+shaderProgram+"\n\t"+prog); + if(DEBUG) { + Thread.dumpStack(); } } if(null!=shaderProgram) { if(shaderProgram.equals(prog)) { - // nothing to do .. - if(DEBUG) { - System.err.println("Info: attachShaderProgram: NOP: equal id: "+shaderProgram.id()); + if(enable) { + useProgram(gl, true); + } + // nothing else to do .. + if(verbose) { + System.err.println("ShaderState: attachShaderProgram: No switch, equal id: "+shaderProgram.id()+", enabling "+enable); } - return; + return false; } if(shaderProgram.inUse()) { if(null != prog && enable) { - // new program will issue glUseProgram(..) - shaderProgram.programInUse = false; + shaderProgram.notifyNotInUse(); } else { // no new 'enabled' program - disable useProgram(gl, false); @@ -236,7 +190,7 @@ public class ShaderState { shaderProgram = prog; if(null!=shaderProgram) { - // [re]set all data and use program if switching program, + // [re]set all data and use program if switching program, // or use program if program is linked if(resetAllShaderData || enable) { useProgram(gl, true); // may reset all data @@ -248,6 +202,7 @@ public class ShaderState { if(DEBUG) { System.err.println("Info: attachShaderProgram: END"); } + return true; } public ShaderProgram shaderProgram() { return shaderProgram; } @@ -259,10 +214,9 @@ public class ShaderState { * @see #glReleaseAllUniforms * @see #release(GL2ES2, boolean, boolean, boolean) */ - public synchronized void destroy(GL2ES2 gl) { + public synchronized void destroy(final GL2ES2 gl) { release(gl, true, true, true); - attachedObjectsByString.clear(); - attachedObjectsByInt.clear(); + attachedObjectsByString.clear(); } /** @@ -272,7 +226,7 @@ public class ShaderState { * @see #glReleaseAllUniforms * @see #release(GL2ES2, boolean, boolean, boolean) */ - public synchronized void releaseAllData(GL2ES2 gl) { + public synchronized void releaseAllData(final GL2ES2 gl) { release(gl, false, false, false); } @@ -281,14 +235,14 @@ public class ShaderState { * @see #glReleaseAllUniforms * @see ShaderProgram#release(GL2ES2, boolean) */ - public synchronized void release(GL2ES2 gl, boolean destroyBoundAttributes, boolean destroyShaderProgram, boolean destroyShaderCode) { - if(null!=shaderProgram) { + public synchronized void release(final GL2ES2 gl, final boolean destroyBoundAttributes, final boolean destroyShaderProgram, final boolean destroyShaderCode) { + if(null!=shaderProgram && shaderProgram.linked() ) { shaderProgram.useProgram(gl, false); } if(destroyBoundAttributes) { - for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { + for(final Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { iter.next().destroy(gl); - } + } } releaseAllAttributes(gl); releaseAllUniforms(gl); @@ -304,7 +258,7 @@ public class ShaderState { /** * Gets the cached location of a shader attribute. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 * * @see #bindAttribLocation(GL2ES2, int, String) @@ -312,11 +266,11 @@ public class ShaderState { * @see #getAttribLocation(GL2ES2, String) * @see GL2ES2#glGetAttribLocation(int, String) */ - public int getCachedAttribLocation(String name) { - Integer idx = activeAttribLocationMap.get(name); + public int getCachedAttribLocation(final String name) { + final Integer idx = activeAttribLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } - + /** * Get the previous cached vertex attribute data. * @@ -332,28 +286,35 @@ public class ShaderState { * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ - public GLArrayData getAttribute(String name) { + public GLArrayData getAttribute(final String name) { return activeAttribDataMap.get(name); } - + + public boolean isActiveAttribute(final GLArrayData attribute) { + return attribute == activeAttribDataMap.get(attribute.getName()); + } + /** * Binds or unbinds the {@link GLArrayData} lifecycle to this ShaderState. - * + * * <p>If an attribute location is cached (ie {@link #bindAttribLocation(GL2ES2, int, String)}) * it is promoted to the {@link GLArrayData} instance.</p> - * - * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} + * + * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.</p> - * + * * <p>The data will not be transfered to the GPU, use {@link #vertexAttribPointer(GL2ES2, GLArrayData)} additionally.</p> - * + * + * <p>The data will also be {@link GLArrayData#associate(Object, boolean) associated} with this ShaderState.</p> + * * @param attribute the {@link GLArrayData} which lifecycle shall be managed * @param own true if <i>owning</i> shall be performs, false if <i>disowning</i>. - * + * * @see #bindAttribLocation(GL2ES2, int, String) * @see #getAttribute(String) + * @see GLArrayData#associate(Object, boolean) */ - public void ownAttribute(GLArrayData attribute, boolean own) { + public void ownAttribute(final GLArrayData attribute, final boolean own) { if(own) { final int location = getCachedAttribLocation(attribute.getName()); if(0<=location) { @@ -363,12 +324,13 @@ public class ShaderState { } else { managedAttributes.remove(attribute); } + attribute.associate(this, own); } - - public boolean ownsAttribute(GLArrayData attribute) { + + public boolean ownsAttribute(final GLArrayData attribute) { return managedAttributes.contains(attribute); } - + /** * Binds a shader attribute to a location. * Multiple names can be bound to one location. @@ -377,16 +339,15 @@ public class ShaderState { * * @throws GLException if no program is attached * @throws GLException if the program is already linked - * + * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) */ - public void bindAttribLocation(GL2ES2 gl, int location, String name) { + public void bindAttribLocation(final GL2ES2 gl, final int location, final String name) { if(null==shaderProgram) throw new GLException("No program is attached"); - if(shaderProgram.linked()) throw new GLException("Program is already linked"); - final Integer loc = new Integer(location); - activeAttribLocationMap.put(name, loc); + if(shaderProgram.linked()) throw new GLException("Program is already linked"); + activeAttribLocationMap.put(name, Integer.valueOf(location)); gl.glBindAttribLocation(shaderProgram.program(), location, name); } @@ -399,25 +360,28 @@ public class ShaderState { * * @throws GLException if no program is attached * @throws GLException if the program is already linked - * + * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) * @see #getAttribute(String) */ - public void bindAttribLocation(GL2ES2 gl, int location, GLArrayData data) { - bindAttribLocation(gl, location, data.getName()); - data.setLocation(location); + public void bindAttribLocation(final GL2ES2 gl, final int location, final GLArrayData data) { + if(null==shaderProgram) throw new GLException("No program is attached"); + if(shaderProgram.linked()) throw new GLException("Program is already linked"); + final String name = data.getName(); + activeAttribLocationMap.put(name, Integer.valueOf(location)); + data.setLocation(gl, shaderProgram.program(), location); activeAttribDataMap.put(data.getName(), data); } /** - * Gets the location of a shader attribute.<br> + * Gets the location of a shader attribute with given <code>name</code>.<br> * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br> * The location will be cached. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. @@ -427,21 +391,22 @@ public class ShaderState { * @see #bindAttribLocation(GL2ES2, int, String) * @see GL2ES2#glGetAttribLocation(int, String) */ - public int getAttribLocation(GL2ES2 gl, String name) { + public int getAttribLocation(final GL2ES2 gl, final String name) { if(null==shaderProgram) throw new GLException("No program is attached"); int location = getCachedAttribLocation(name); if(0>location) { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); location = gl.glGetAttribLocation(shaderProgram.program(), name); if(0<=location) { - Integer idx = new Integer(location); - activeAttribLocationMap.put(name, idx); + activeAttribLocationMap.put(name, Integer.valueOf(location)); if(DEBUG) { - System.err.println("Info: glGetAttribLocation: "+name+", loc: "+location); + System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location); } } else if(verbose) { - Throwable tX = new Throwable("Info: glGetAttribLocation failed, no location for: "+name+", loc: "+location); - tX.printStackTrace(); + System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location); + if(DEBUG) { + Thread.dumpStack(); + } } } return location; @@ -449,14 +414,14 @@ public class ShaderState { /** * Validates and returns the location of a shader attribute.<br> - * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, + * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br> - * The location will be cached and set in the + * The location will be cached and set in the * {@link GLArrayData} object. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 - * + * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * @@ -466,13 +431,31 @@ public class ShaderState { * @see GL2ES2#glGetAttribLocation(int, String) * @see #getAttribute(String) */ - public int getAttribLocation(GL2ES2 gl, GLArrayData data) { - int location = getAttribLocation(gl, data.getName()); - data.setLocation(location); + public int getAttribLocation(final GL2ES2 gl, final GLArrayData data) { + if(null==shaderProgram) throw new GLException("No program is attached"); + final String name = data.getName(); + int location = getCachedAttribLocation(name); + if(0<=location) { + data.setLocation(location); + } else { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); + location = data.setLocation(gl, shaderProgram.program()); + if(0<=location) { + activeAttribLocationMap.put(name, Integer.valueOf(location)); + if(DEBUG) { + System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location); + } + } else if(verbose) { + System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location); + if(DEBUG) { + Thread.dumpStack(); + } + } + } activeAttribDataMap.put(data.getName(), data); return location; } - + // // Enabled Vertex Arrays and its data // @@ -480,42 +463,44 @@ public class ShaderState { /** * @return true if the named attribute is enable */ - public final boolean isVertexAttribArrayEnabled(String name) { + public final boolean isVertexAttribArrayEnabled(final String name) { final Boolean v = activedAttribEnabledMap.get(name); return null != v && v.booleanValue(); } - + /** * @return true if the {@link GLArrayData} attribute is enable */ - public final boolean isVertexAttribArrayEnabled(GLArrayData data) { + public final boolean isVertexAttribArrayEnabled(final GLArrayData data) { return isVertexAttribArrayEnabled(data.getName()); } - - private boolean enableVertexAttribArray(GL2ES2 gl, String name, int location) { + + private boolean enableVertexAttribArray(final GL2ES2 gl, final String name, int location) { activedAttribEnabledMap.put(name, Boolean.TRUE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { - Throwable tX = new Throwable("Info: glEnableVertexAttribArray failed, no index for: "+name); - tX.printStackTrace(); + System.err.println("ShaderState: glEnableVertexAttribArray failed, no index for: "+name); + if(DEBUG) { + Thread.dumpStack(); + } } return false; } } if(DEBUG) { - System.err.println("Info: glEnableVertexAttribArray: "+name+", loc: "+location); + System.err.println("ShaderState: glEnableVertexAttribArray: "+name+", loc: "+location); } gl.glEnableVertexAttribArray(location); return true; } - + /** * Enables a vertex attribute array. - * + * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} - * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. + * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. @@ -523,16 +508,16 @@ public class ShaderState { * @return false, if the name is not found, otherwise true * * @throws GLException if the program is not linked and no location was cached. - * + * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ - public boolean enableVertexAttribArray(GL2ES2 gl, String name) { + public boolean enableVertexAttribArray(final GL2ES2 gl, final String name) { return enableVertexAttribArray(gl, name, -1); } - + /** * Enables a vertex attribute array, usually invoked by {@link GLArrayDataEditable#enableBuffer(GL, boolean)}. @@ -541,7 +526,7 @@ public class ShaderState { * and is the preferred alternative to {@link #enableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. - * + * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. * @@ -555,41 +540,43 @@ public class ShaderState { * @see #getVertexAttribPointer * @see GLArrayDataEditable#enableBuffer(GL, boolean) */ - public boolean enableVertexAttribArray(GL2ES2 gl, GLArrayData data) { + public boolean enableVertexAttribArray(final GL2ES2 gl, final GLArrayData data) { if(0 > data.getLocation()) { getAttribLocation(gl, data); } else { // ensure data is the current bound one - activeAttribDataMap.put(data.getName(), data); + activeAttribDataMap.put(data.getName(), data); } return enableVertexAttribArray(gl, data.getName(), data.getLocation()); } - - private boolean disableVertexAttribArray(GL2ES2 gl, String name, int location) { + + private boolean disableVertexAttribArray(final GL2ES2 gl, final String name, int location) { activedAttribEnabledMap.put(name, Boolean.FALSE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { - Throwable tX = new Throwable("Info: glDisableVertexAttribArray failed, no index for: "+name); - tX.printStackTrace(); + System.err.println("ShaderState: glDisableVertexAttribArray failed, no index for: "+name); + if(DEBUG) { + Thread.dumpStack(); + } } return false; } } if(DEBUG) { - System.err.println("Info: glDisableVertexAttribArray: "+name); + System.err.println("ShaderState: glDisableVertexAttribArray: "+name); } gl.glDisableVertexAttribArray(location); return true; } - + /** * Disables a vertex attribute array * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} * hence {@link #disableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. - * + * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * @@ -603,7 +590,7 @@ public class ShaderState { * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ - public boolean disableVertexAttribArray(GL2ES2 gl, String name) { + public boolean disableVertexAttribArray(final GL2ES2 gl, final String name) { return disableVertexAttribArray(gl, name, -1); } @@ -614,7 +601,7 @@ public class ShaderState { * and is the preferred alternative to {@link #disableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. - * + * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * @@ -628,39 +615,40 @@ public class ShaderState { * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ - public boolean disableVertexAttribArray(GL2ES2 gl, GLArrayData data) { + public boolean disableVertexAttribArray(final GL2ES2 gl, final GLArrayData data) { if(0 > data.getLocation()) { getAttribLocation(gl, data); } return disableVertexAttribArray(gl, data.getName(), data.getLocation()); } - + /** - * Set the {@link GLArrayData} vertex attribute data. - * - * This method uses the {@link GLArrayData}'s location if set. - * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)}, set - * and cached in this state. - * + * Set the {@link GLArrayData} vertex attribute data, if it's location is valid, i.e. ≥ 0. + * <p> + * This method uses the {@link GLArrayData}'s location if valid, i.e. ≥ 0.<br/> + * If data's location is invalid, it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)}, + * set and cached in this state. + * </p> + * * @return false, if the location could not be determined, otherwise true * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. - * + * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer * @see #getVertexAttribPointer */ - public boolean vertexAttribPointer(GL2ES2 gl, GLArrayData data) { + public boolean vertexAttribPointer(final GL2ES2 gl, final GLArrayData data) { int location = data.getLocation(); if(0 > location) { location = getAttribLocation(gl, data); - } + } if(0 <= location) { // only pass the data, if the attribute exists in the current shader if(DEBUG) { - System.err.println("Info: glVertexAttribPointer: "+data); + System.err.println("ShaderState: glVertexAttribPointer: "+data); } gl.glVertexAttribPointer(data); return true; @@ -681,28 +669,28 @@ public class ShaderState { * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ - public void releaseAllAttributes(GL2ES2 gl) { + public void releaseAllAttributes(final GL2ES2 gl) { if(null!=shaderProgram) { - for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { + for(final Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { disableVertexAttribArray(gl, iter.next()); } - for(Iterator<String> iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { + for(final Iterator<String> iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { disableVertexAttribArray(gl, iter.next()); } } activeAttribDataMap.clear(); activedAttribEnabledMap.clear(); activeAttribLocationMap.clear(); - managedAttributes.clear(); + managedAttributes.clear(); } - + /** * Disables all vertex attribute arrays. * * Their enabled stated will be removed from this state only * if 'removeFromState' is true. * - * This method purpose is more for debugging. + * This method purpose is more for debugging. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray @@ -713,8 +701,8 @@ public class ShaderState { * @see #glResetAllVertexAttributes * @see ShaderProgram#glReplaceShader */ - public void disableAllVertexAttributeArrays(GL2ES2 gl, boolean removeFromState) { - for(Iterator<String> iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { + public void disableAllVertexAttributeArrays(final GL2ES2 gl, final boolean removeFromState) { + for(final Iterator<String> iter = activedAttribEnabledMap.keySet().iterator(); iter.hasNext(); ) { final String name = iter.next(); if(removeFromState) { activedAttribEnabledMap.remove(name); @@ -726,78 +714,91 @@ public class ShaderState { } } - private final void relocateAttribute(GL2ES2 gl, GLArrayData attribute) { - // get new location .. + private final void relocateAttribute(final GL2ES2 gl, final GLArrayData attribute) { + // get new location .. note: 'activeAttribLocationMap' is cleared before final String name = attribute.getName(); - final int loc = getAttribLocation(gl, name); - attribute.setLocation(loc); - + final int loc = attribute.setLocation(gl, shaderProgram.program()); if(0<=loc) { + activeAttribLocationMap.put(name, Integer.valueOf(loc)); + if(DEBUG) { + System.err.println("ShaderState: relocateAttribute: "+name+", loc: "+loc); + } if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } - + if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); - } - - gl.glVertexAttribPointer(attribute); + gl.glVertexAttribPointer(attribute); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } else { + gl.glVertexAttribPointer(attribute); + } } } - + /** * Reset all previously enabled mapped vertex attribute data. - * - * <p>Attribute data is bound to the GL state</p> - * <p>Attribute location is bound to the program</p> - * - * <p>However, since binding an attribute to a location via {@link #bindAttribLocation(GL2ES2, int, GLArrayData)} - * <i>must</i> happen before linking <b>and</b> we try to promote the attributes to the new program, - * we have to gather the probably new location etc.</p> + * + * <p> + * Attribute data is bound to the GL state, i.e. VBO data itself will not be updated. + * </p> + * + * <p> + * Attribute location and it's data assignment is bound to the program, + * hence both are updated. + * </p> + * + * <p> + * Note: Such update could only be prevented, + * if tracking am attribute/program dirty flag. + * </p> * * @throws GLException is the program is not linked * * @see #attachShaderProgram(GL2ES2, ShaderProgram) */ - private final void resetAllAttributes(GL2ES2 gl) { + private final void resetAllAttributes(final GL2ES2 gl) { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); activeAttribLocationMap.clear(); - - for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { - iter.next().setLocation(-1); + + for(int i=0; i<managedAttributes.size(); i++) { + managedAttributes.get(i).setLocation(-1); } - for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { + for(final Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { relocateAttribute(gl, iter.next()); } } - private final void setAttribute(GL2ES2 gl, GLArrayData attribute) { + private final void setAttribute(final GL2ES2 gl, final GLArrayData attribute) { // get new location .. final String name = attribute.getName(); final int loc = attribute.getLocation(); if(0<=loc) { - this.bindAttribLocation(gl, loc, name); - + bindAttribLocation(gl, loc, name); + if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } - + if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); - } - - gl.glVertexAttribPointer(attribute); + gl.glVertexAttribPointer(attribute); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } else { + gl.glVertexAttribPointer(attribute); + } } } - + /** * preserves the attribute location .. (program not linked) */ - private final void setAllAttributes(GL2ES2 gl) { - for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { + private final void setAllAttributes(final GL2ES2 gl) { + for(final Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { setAttribute(gl, iter.next()); } } @@ -809,48 +810,48 @@ public class ShaderState { /** * Gets the cached location of the shader uniform. * - * @return -1 if there is no such uniform available, + * @return -1 if there is no such uniform available, * otherwise >= 0 */ - public final int getCachedUniformLocation(String name) { - Integer idx = (Integer) activeUniformLocationMap.get(name); + public final int getCachedUniformLocation(final String name) { + final Integer idx = activeUniformLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } /** * Bind the {@link GLUniform} lifecycle to this ShaderState. - * + * * <p>If a uniform location is cached it is promoted to the {@link GLUniformData} instance.</p> - * - * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} + * + * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.</p> - * + * * <p>The data will not be transfered to the GPU, use {@link #uniform(GL2ES2, GLUniformData)} additionally.</p> - * + * * @param uniform the {@link GLUniformData} which lifecycle shall be managed - * + * * @see #getUniform(String) */ - public void ownUniform(GLUniformData uniform) { + public void ownUniform(final GLUniformData uniform) { final int location = getCachedUniformLocation(uniform.getName()); if(0<=location) { uniform.setLocation(location); - } + } activeUniformDataMap.put(uniform.getName(), uniform); - managedUniforms.add(uniform); + managedUniforms.add(uniform); } - - public boolean ownsUniform(GLUniformData uniform) { + + public boolean ownsUniform(final GLUniformData uniform) { return managedUniforms.contains(uniform); } - + /** - * Gets the location of a shader uniform.<br> + * Gets the location of a shader uniform with given <code>name</code>.<br> * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br> * The location will be cached. * <p> - * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) + * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p> * * @return -1 if there is no such attribute available, @@ -863,17 +864,19 @@ public class ShaderState { * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ - public final int getUniformLocation(GL2ES2 gl, String name) { + public final int getUniformLocation(final GL2ES2 gl, final String name) { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); int location = getCachedUniformLocation(name); if(0>location) { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); location = gl.glGetUniformLocation(shaderProgram.program(), name); if(0<=location) { - Integer idx = new Integer(location); - activeUniformLocationMap.put(name, idx); + activeUniformLocationMap.put(name, Integer.valueOf(location)); } else if(verbose) { - Throwable tX = new Throwable("Info: glUniform failed, no location for: "+name+", index: "+location); - tX.printStackTrace(); + System.err.println("ShaderState: glUniform failed, no location for: "+name+", index: "+location); + if(DEBUG) { + Thread.dumpStack(); + } } } return location; @@ -883,10 +886,10 @@ public class ShaderState { * Validates and returns the location of a shader uniform.<br> * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br> - * The location will be cached and set in the + * The location will be cached and set in the * {@link GLUniformData} object. * <p> - * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) + * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p> * * @return -1 if there is no such attribute available, @@ -899,27 +902,37 @@ public class ShaderState { * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ - public int getUniformLocation(GL2ES2 gl, GLUniformData data) { - int location = getUniformLocation(gl, data.getName()); - data.setLocation(location); - activeUniformDataMap.put(data.getName(), data); + public int getUniformLocation(final GL2ES2 gl, final GLUniformData data) { + if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); + final String name = data.getName(); + int location = getCachedUniformLocation(name); + if(0<=location) { + data.setLocation(location); + } else { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); + location = data.setLocation(gl, shaderProgram.program()); + if(0<=location) { + activeUniformLocationMap.put(name, Integer.valueOf(location)); + } else if(verbose) { + System.err.println("ShaderState: glUniform failed, no location for: "+name+", index: "+location); + if(DEBUG) { + Thread.dumpStack(); + } + } + } + activeUniformDataMap.put(name, data); return location; } - + /** - * Set the uniform data. - * - * Even if the uniform is not found in the current shader, - * it is stored in this state. - * - * @param data the GLUniforms's name must match the uniform one, - * it's index will be set with the uniforms's location, - * if found. - * - * - * @return false, if the name is not found, otherwise true + * Set the uniform data, if it's location is valid, i.e. ≥ 0. + * <p> + * This method uses the {@link GLUniformData}'s location if valid, i.e. ≥ 0.<br/> + * If data's location is invalid, it will be retrieved via {@link #getUniformLocation(GL2ES2, GLUniformData)}, + * set and cached in this state. + * </p> * - * @throws GLException if the program is not in use + * @return false, if the location could not be determined, otherwise true * * @see #glGetUniformLocation * @see javax.media.opengl.GL2ES2#glGetUniformLocation @@ -927,7 +940,7 @@ public class ShaderState { * @see #getUniformLocation * @see ShaderProgram#glReplaceShader */ - public boolean uniform(GL2ES2 gl, GLUniformData data) { + public boolean uniform(final GL2ES2 gl, final GLUniformData data) { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); int location = data.getLocation(); if(0>location) { @@ -936,11 +949,12 @@ public class ShaderState { if(0<=location) { // only pass the data, if the uniform exists in the current shader if(DEBUG) { - System.err.println("Info: glUniform: "+data); + System.err.println("ShaderState: glUniform: "+data); } gl.glUniform(data); + return true; } - return true; + return false; } /** @@ -948,7 +962,7 @@ public class ShaderState { * * @return the GLUniformData object, null if not previously set. */ - public GLUniformData getUniform(String name) { + public GLUniformData getUniform(final String name) { return activeUniformDataMap.get(name); } @@ -956,42 +970,54 @@ public class ShaderState { * Releases all mapped uniform data * and loses all indices */ - public void releaseAllUniforms(GL2ES2 gl) { + public void releaseAllUniforms(final GL2ES2 gl) { activeUniformDataMap.clear(); activeUniformLocationMap.clear(); managedUniforms.clear(); } - + /** * Reset all previously mapped uniform data - * + * <p> * Uniform data and location is bound to the program, - * hence both are updated here + * hence both are updated. + * </p> + * <p> + * Note: Such update could only be prevented, + * if tracking a uniform/program dirty flag. + * </p> * * @throws GLException is the program is not in use - * + * * @see #attachShaderProgram(GL2ES2, ShaderProgram) */ - private final void resetAllUniforms(GL2ES2 gl) { - if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); + private final void resetAllUniforms(final GL2ES2 gl) { + if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); activeUniformLocationMap.clear(); - for(Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { + for(final Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { iter.next().setLocation(-1); - } - for(Iterator<GLUniformData> iter = activeUniformDataMap.values().iterator(); iter.hasNext(); ) { - final GLUniformData uniform = iter.next(); - uniform.setLocation(-1); - uniform(gl, uniform); + } + for(final Iterator<GLUniformData> iter = activeUniformDataMap.values().iterator(); iter.hasNext(); ) { + final GLUniformData data = iter.next(); + final int loc = data.setLocation(gl, shaderProgram.program()); + if( 0 <= loc ) { + // only pass the data, if the uniform exists in the current shader + activeUniformLocationMap.put(data.getName(), Integer.valueOf(loc)); + if(DEBUG) { + System.err.println("ShaderState: resetAllUniforms: "+data); + } + gl.glUniform(data); + } } } - public StringBuilder toString(StringBuilder sb) { + public StringBuilder toString(StringBuilder sb, final boolean alsoUnlocated) { if(null==sb) { sb = new StringBuilder(); } - + sb.append("ShaderState[ "); - + sb.append(Platform.getNewline()).append(" "); if(null != shaderProgram) { shaderProgram.toString(sb); @@ -1000,51 +1026,62 @@ public class ShaderState { } sb.append(Platform.getNewline()).append(" enabledAttributes ["); { - Iterator<String> names = activedAttribEnabledMap.keySet().iterator(); - Iterator<Boolean> values = activedAttribEnabledMap.values().iterator(); + final Iterator<String> names = activedAttribEnabledMap.keySet().iterator(); + final Iterator<Boolean> values = activedAttribEnabledMap.values().iterator(); while( names.hasNext() ) { sb.append(Platform.getNewline()).append(" ").append(names.next()).append(": ").append(values.next()); } } sb.append(Platform.getNewline()).append(" ],").append(" activeAttributes ["); - for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + for(final Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { + final GLArrayData ad = iter.next(); + if( alsoUnlocated || 0 <= ad.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ad); + } } sb.append(Platform.getNewline()).append(" ],").append(" managedAttributes ["); - for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + for(final Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { + final GLArrayData ad = iter.next(); + if( alsoUnlocated || 0 <= ad.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ad); + } } sb.append(Platform.getNewline()).append(" ],").append(" activeUniforms ["); - for(Iterator<GLUniformData> iter=activeUniformDataMap.values().iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + for(final Iterator<GLUniformData> iter=activeUniformDataMap.values().iterator(); iter.hasNext(); ) { + final GLUniformData ud = iter.next(); + if( alsoUnlocated || 0 <= ud.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ud); + } } sb.append(Platform.getNewline()).append(" ],").append(" managedUniforms ["); - for(Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + for(final Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { + final GLUniformData ud = iter.next(); + if( alsoUnlocated || 0 <= ud.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ud); + } } sb.append(Platform.getNewline()).append(" ]").append(Platform.getNewline()).append("]"); return sb; } - + @Override public String toString() { - return toString(null).toString(); + return toString(null, DEBUG).toString(); } - - private boolean verbose = DEBUG ? true : false; + + private boolean verbose = DEBUG; private ShaderProgram shaderProgram=null; - - private HashMap<String, Boolean> activedAttribEnabledMap = new HashMap<String, Boolean>(); - private HashMap<String, Integer> activeAttribLocationMap = new HashMap<String, Integer>(); - private HashMap<String, GLArrayData> activeAttribDataMap = new HashMap<String, GLArrayData>(); - private ArrayList<GLArrayData> managedAttributes = new ArrayList<GLArrayData>(); - - private HashMap<String, Integer> activeUniformLocationMap = new HashMap<String, Integer>(); - private HashMap<String, GLUniformData> activeUniformDataMap = new HashMap<String, GLUniformData>(); - private ArrayList<GLUniformData> managedUniforms = new ArrayList<GLUniformData>(); - - private HashMap<String, Object> attachedObjectsByString = new HashMap<String, Object>(); - private IntObjectHashMap attachedObjectsByInt = new IntObjectHashMap(); + + private final HashMap<String, Boolean> activedAttribEnabledMap = new HashMap<String, Boolean>(); + private final HashMap<String, Integer> activeAttribLocationMap = new HashMap<String, Integer>(); + private final HashMap<String, GLArrayData> activeAttribDataMap = new HashMap<String, GLArrayData>(); + private final ArrayList<GLArrayData> managedAttributes = new ArrayList<GLArrayData>(); + + private final HashMap<String, Integer> activeUniformLocationMap = new HashMap<String, Integer>(); + private final HashMap<String, GLUniformData> activeUniformDataMap = new HashMap<String, GLUniformData>(); + private final ArrayList<GLUniformData> managedUniforms = new ArrayList<GLUniformData>(); + + private final HashMap<String, Object> attachedObjectsByString = new HashMap<String, Object>(); private boolean resetAllShaderData = false; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java index 40c052498..06f7d9268 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +28,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ package com.jogamp.opengl.util.glsl; @@ -40,51 +40,52 @@ import java.util.*; import javax.media.opengl.*; import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.GLExtensions; public class ShaderUtil { - public static String getShaderInfoLog(GL _gl, int shaderObj) { + public static String getShaderInfoLog(final GL _gl, final int shaderObj) { final GL2ES2 gl = _gl.getGL2ES2(); - int[] infoLogLength=new int[1]; + final int[] infoLogLength=new int[1]; gl.glGetShaderiv(shaderObj, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); if(infoLogLength[0]==0) { return "(no info log)"; } - int[] charsWritten=new int[1]; - byte[] infoLogBytes = new byte[infoLogLength[0]]; + final int[] charsWritten=new int[1]; + final byte[] infoLogBytes = new byte[infoLogLength[0]]; gl.glGetShaderInfoLog(shaderObj, infoLogLength[0], charsWritten, 0, infoLogBytes, 0); return new String(infoLogBytes, 0, charsWritten[0]); } - public static String getProgramInfoLog(GL _gl, int programObj) { + public static String getProgramInfoLog(final GL _gl, final int programObj) { final GL2ES2 gl = _gl.getGL2ES2(); - int[] infoLogLength=new int[1]; + final int[] infoLogLength=new int[1]; gl.glGetProgramiv(programObj, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); if(infoLogLength[0]==0) { return "(no info log)"; } - int[] charsWritten=new int[1]; - byte[] infoLogBytes = new byte[infoLogLength[0]]; + final int[] charsWritten=new int[1]; + final byte[] infoLogBytes = new byte[infoLogLength[0]]; gl.glGetProgramInfoLog(programObj, infoLogLength[0], charsWritten, 0, infoLogBytes, 0); return new String(infoLogBytes, 0, charsWritten[0]); } - public static boolean isShaderStatusValid(GL _gl, int shaderObj, int name, PrintStream verboseOut) { + public static boolean isShaderStatusValid(final GL _gl, final int shaderObj, final int name, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); - int[] ires = new int[1]; + final int[] ires = new int[1]; gl.glGetShaderiv(shaderObj, name, ires, 0); - boolean res = ires[0]==1; + final boolean res = ires[0]==1; if(!res && null!=verboseOut) { verboseOut.println("Shader status invalid: "+ getShaderInfoLog(gl, shaderObj)); } return res; } - public static boolean isShaderStatusValid(GL _gl, IntBuffer shaders, int name, PrintStream verboseOut) { + public static boolean isShaderStatusValid(final GL _gl, final IntBuffer shaders, final int name, final PrintStream verboseOut) { boolean res = true; for (int i = shaders.position(); i < shaders.limit(); i++) { res = isShaderStatusValid(_gl, shaders.get(i), name, verboseOut) && res; @@ -92,15 +93,15 @@ public class ShaderUtil { return res; } - public static boolean isProgramStatusValid(GL _gl, int programObj, int name) { + public static boolean isProgramStatusValid(final GL _gl, final int programObj, final int name) { final GL2ES2 gl = _gl.getGL2ES2(); - int[] ires = new int[1]; + final int[] ires = new int[1]; gl.glGetProgramiv(programObj, name, ires, 0); return ires[0]==1; } - public static boolean isProgramLinkStatusValid(GL _gl, int programObj, PrintStream verboseOut) { + public static boolean isProgramLinkStatusValid(final GL _gl, final int programObj, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); if(!gl.glIsProgram(programObj)) { if(null!=verboseOut) { @@ -116,11 +117,11 @@ public class ShaderUtil { } return true; } - + /** * Performs {@link GL2ES2#glValidateProgram(int)} * <p> - * One shall only call this method while debugging and only if all required + * One shall only call this method while debugging and only if all required * resources by the shader are set. * </p> * <p> @@ -129,7 +130,7 @@ public class ShaderUtil { * </p> * @see GL2ES2#glValidateProgram(int) **/ - public static boolean isProgramExecStatusValid(GL _gl, int programObj, PrintStream verboseOut) { + public static boolean isProgramExecStatusValid(final GL _gl, final int programObj, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); gl.glValidateProgram(programObj); if(!isProgramStatusValid(gl, programObj, GL2ES2.GL_VALIDATE_STATUS)) { @@ -141,7 +142,7 @@ public class ShaderUtil { return true; } - public static void createShader(GL _gl, int type, IntBuffer shaders) { + public static void createShader(final GL _gl, final int type, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { shaders.put(i, gl.glCreateShader(type)); @@ -149,25 +150,31 @@ public class ShaderUtil { } /** - * If supported, queries the natively supported shader binary formats using + * If supported, queries the natively supported shader binary formats using * {@link GL2ES2#GL_NUM_SHADER_BINARY_FORMATS} and {@link GL2ES2#GL_SHADER_BINARY_FORMATS} * via {@link GL2ES2#glGetIntegerv(int, int[], int)}. */ - public static Set<Integer> getShaderBinaryFormats(GL _gl) { + public static Set<Integer> getShaderBinaryFormats(final GL _gl) { final GL2ES2 gl = _gl.getGL2ES2(); final ProfileInformation info = getProfileInformation(gl); if(null == info.shaderBinaryFormats) { info.shaderBinaryFormats = new HashSet<Integer>(); if (gl.isGLES2Compatible()) { - final int[] param = new int[1]; - gl.glGetIntegerv(GL2ES2.GL_NUM_SHADER_BINARY_FORMATS, param, 0); - int numFormats = param[0]; - if(numFormats>0) { - int[] formats = new int[numFormats]; - gl.glGetIntegerv(GL2ES2.GL_SHADER_BINARY_FORMATS, formats, 0); - for(int i=0; i<numFormats; i++) { - info.shaderBinaryFormats.add(new Integer(formats[i])); + try { + final int[] param = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_NUM_SHADER_BINARY_FORMATS, param, 0); + final int err = gl.glGetError(); + final int numFormats = GL.GL_NO_ERROR == err ? param[0] : 0; + if(numFormats>0) { + final int[] formats = new int[numFormats]; + gl.glGetIntegerv(GL2ES2.GL_SHADER_BINARY_FORMATS, formats, 0); + for(int i=0; i<numFormats; i++) { + info.shaderBinaryFormats.add(Integer.valueOf(formats[i])); + } } + } catch (final GLException gle) { + System.err.println("Caught exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); } } } @@ -175,22 +182,33 @@ public class ShaderUtil { } /** Returns true if a hader compiler is available, otherwise false. */ - public static boolean isShaderCompilerAvailable(GL _gl) { + public static boolean isShaderCompilerAvailable(final GL _gl) { final GL2ES2 gl = _gl.getGL2ES2(); final ProfileInformation info = getProfileInformation(gl); if(null==info.shaderCompilerAvailable) { if(gl.isGLES2()) { - final byte[] param = new byte[1]; - gl.glGetBooleanv(GL2ES2.GL_SHADER_COMPILER, param, 0); - boolean v = param[0]!=(byte)0x00; - if(!v) { - final Set<Integer> bfs = getShaderBinaryFormats(gl); - if(bfs.size()==0) { - // no supported binary formats, hence a compiler must be available! - v = true; + boolean queryOK = false; + try { + final byte[] param = new byte[1]; + gl.glGetBooleanv(GL2ES2.GL_SHADER_COMPILER, param, 0); + final int err = gl.glGetError(); + boolean v = GL.GL_NO_ERROR == err && param[0]!=(byte)0x00; + if(!v) { + final Set<Integer> bfs = getShaderBinaryFormats(gl); + if(bfs.size()==0) { + // no supported binary formats, hence a compiler must be available! + v = true; + } } + info.shaderCompilerAvailable = Boolean.valueOf(v); + queryOK = true; + } catch (final GLException gle) { + System.err.println("Caught exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); + } + if(!queryOK) { + info.shaderCompilerAvailable = Boolean.valueOf(true); } - info.shaderCompilerAvailable = new Boolean(v); } else if( gl.isGL2ES2() ) { info.shaderCompilerAvailable = new Boolean(true); } else { @@ -200,22 +218,29 @@ public class ShaderUtil { return info.shaderCompilerAvailable.booleanValue(); } - public static void shaderSource(GL _gl, int shader, CharSequence[] source) + /** Returns true if GeometryShader is supported, i.e. whether GLContext is ≥ 3.2 or ARB_geometry_shader4 extension is available. */ + public static boolean isGeometryShaderSupported(final GL _gl) { + final GLContext ctx = _gl.getContext(); + return ctx.getGLVersionNumber().compareTo(GLContext.Version320) >= 0 || + ctx.isExtensionAvailable(GLExtensions.ARB_geometry_shader4); + } + + public static void shaderSource(final GL _gl, final int shader, final CharSequence[] source) { final GL2ES2 gl = _gl.getGL2ES2(); if(!isShaderCompilerAvailable(_gl)) { throw new GLException("No compiler is available"); } - int count = (null!=source)?source.length:0; + final int count = (null!=source)?source.length:0; if(count==0) { throw new GLException("No sources specified"); } - IntBuffer lengths = Buffers.newDirectIntBuffer(count); + final IntBuffer lengths = Buffers.newDirectIntBuffer(count); for(int i=0; i<count; i++) { lengths.put(i, source[i].length()); - } + } if(source instanceof String[]) { // rare case .. gl.glShaderSource(shader, count, (String[])source, lengths); @@ -224,10 +249,10 @@ public class ShaderUtil { for(int i = source.length - 1; i>=0; i--) { final CharSequence csq = source[i]; if(csq instanceof String) { - // if ShaderCode.create(.. mutableStringBuffer == false ) + // if ShaderCode.create(.. mutableStringBuilder == false ) tmp[i] = (String) csq; } else { - // if ShaderCode.create(.. mutableStringBuffer == true ) + // if ShaderCode.create(.. mutableStringBuilder == true ) tmp[i] = source[i].toString(); } } @@ -235,10 +260,10 @@ public class ShaderUtil { } } - public static void shaderSource(GL _gl, IntBuffer shaders, CharSequence[][] sources) + public static void shaderSource(final GL _gl, final IntBuffer shaders, final CharSequence[][] sources) { - int sourceNum = (null!=sources)?sources.length:0; - int shaderNum = (null!=shaders)?shaders.remaining():0; + final int sourceNum = (null!=sources)?sources.length:0; + final int shaderNum = (null!=shaders)?shaders.remaining():0; if(shaderNum<=0 || sourceNum<=0 || shaderNum!=sourceNum) { throw new GLException("Invalid number of shaders and/or sources: shaders="+ shaderNum+", sources="+sourceNum); @@ -248,28 +273,28 @@ public class ShaderUtil { } } - public static void shaderBinary(GL _gl, IntBuffer shaders, int binFormat, java.nio.Buffer bin) + public static void shaderBinary(final GL _gl, final IntBuffer shaders, final int binFormat, final java.nio.Buffer bin) { final GL2ES2 gl = _gl.getGL2ES2(); if(getShaderBinaryFormats(gl).size()<=0) { throw new GLException("No binary formats are supported"); } - int shaderNum = shaders.remaining(); + final int shaderNum = shaders.remaining(); if(shaderNum<=0) { throw new GLException("No shaders specified"); } if(null==bin) { throw new GLException("Null shader binary"); } - int binLength = bin.remaining(); + final int binLength = bin.remaining(); if(0>=binLength) { throw new GLException("Empty shader binary (remaining == 0)"); } gl.glShaderBinary(shaderNum, shaders, binFormat, bin, binLength); } - public static void compileShader(GL _gl, IntBuffer shaders) + public static void compileShader(final GL _gl, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { @@ -277,7 +302,7 @@ public class ShaderUtil { } } - public static void attachShader(GL _gl, int program, IntBuffer shaders) + public static void attachShader(final GL _gl, final int program, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { @@ -285,7 +310,7 @@ public class ShaderUtil { } } - public static void detachShader(GL _gl, int program, IntBuffer shaders) + public static void detachShader(final GL _gl, final int program, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { @@ -293,7 +318,7 @@ public class ShaderUtil { } } - public static void deleteShader(GL _gl, IntBuffer shaders) + public static void deleteShader(final GL _gl, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { @@ -302,9 +327,9 @@ public class ShaderUtil { } } - public static boolean createAndLoadShader(GL _gl, IntBuffer shader, int shaderType, - int binFormat, java.nio.Buffer bin, - PrintStream verboseOut) + public static boolean createAndLoadShader(final GL _gl, final IntBuffer shader, final int shaderType, + final int binFormat, final java.nio.Buffer bin, + final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); int err = gl.glGetError(); // flush previous errors .. @@ -313,7 +338,7 @@ public class ShaderUtil { } createShader(gl, shaderType, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndLoadShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } @@ -327,9 +352,9 @@ public class ShaderUtil { return err == GL.GL_NO_ERROR; } - public static boolean createAndCompileShader(GL _gl, IntBuffer shader, int shaderType, - CharSequence[][] sources, - PrintStream verboseOut) + public static boolean createAndCompileShader(final GL _gl, final IntBuffer shader, final int shaderType, + final CharSequence[][] sources, + final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); int err = gl.glGetError(); // flush previous errors .. @@ -338,34 +363,34 @@ public class ShaderUtil { } createShader(gl, shaderType, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } shaderSource(gl, shader, sources); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: ShaderSource failed, GL Error: 0x"+Integer.toHexString(err)); } compileShader(gl, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndCompileShader: CompileShader failed, GL Error: 0x"+Integer.toHexString(err)); } return isShaderStatusValid(gl, shader, GL2ES2.GL_COMPILE_STATUS, verboseOut) && err == GL.GL_NO_ERROR; } - + private static final String implObjectKey = "com.jogamp.opengl.util.glsl.ShaderUtil" ; - + private static class ProfileInformation { Boolean shaderCompilerAvailable = null; Set<Integer> shaderBinaryFormats = null; - } + } - private static ProfileInformation getProfileInformation(GL gl) { + private static ProfileInformation getProfileInformation(final GL gl) { final GLContext context = gl.getContext(); context.validateCurrent(); ProfileInformation data = (ProfileInformation) context.getAttachedObject(implObjectKey); diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java index d92a7aa22..1ed875f27 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java @@ -4,26 +4,45 @@ package com.jogamp.opengl.util.glsl.fixedfunc; -import javax.media.opengl.*; -import javax.media.opengl.fixedfunc.*; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.fixedfunc.GLPointerFuncUtil; + +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncHook; +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncImpl; +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncPipeline; + +import com.jogamp.opengl.util.PMVMatrix; -import jogamp.opengl.util.glsl.fixedfunc.*; /** * Tool to pipeline GL2ES2 into a fixed function emulation implementing GL2ES1. */ public class FixedFuncUtil { /** + * @param gl + * @param mode one of the {@link ShaderSelectionMode}s + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} * @return If gl is a GL2ES1 and force is false, return the type cast object, * otherwise create a fixed function emulation pipeline using the given GL2ES2 impl * and hook it to the GLContext via {@link GLContext#setGL(GL)}. * @throws GLException if the GL object is neither GL2ES1 nor GL2ES2 + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX */ - public static final GL2ES1 wrapFixedFuncEmul(GL gl, boolean force) { + public static final GL2ES1 wrapFixedFuncEmul(final GL gl, final ShaderSelectionMode mode, final PMVMatrix pmvMatrix, final boolean force, final boolean verbose) { if(gl.isGL2ES2() && ( !gl.isGL2ES1() || force ) ) { - GL2ES2 es2 = gl.getGL2ES2(); - FixedFuncHook hook = new FixedFuncHook(es2); - FixedFuncImpl impl = new FixedFuncImpl(es2, hook); + final GL2ES2 es2 = gl.getGL2ES2(); + final FixedFuncHook hook = new FixedFuncHook(es2, mode, pmvMatrix); + hook.setVerbose(verbose); + final FixedFuncImpl impl = new FixedFuncImpl(es2, hook); gl.getContext().setGL(impl); return impl; } else if(gl.isGL2ES1()) { @@ -33,21 +52,30 @@ public class FixedFuncUtil { } /** + * @param gl + * @param mode one of the {@link ShaderSelectionMode}s + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} * @return If gl is a GL2ES1, return the type cast object, * otherwise create a fixed function emulation pipeline using the GL2ES2 impl. * and hook it to the GLContext via {@link GLContext#setGL(GL)}. * @throws GLException if the GL object is neither GL2ES1 nor GL2ES2 + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX */ - public static final GL2ES1 wrapFixedFuncEmul(GL gl) { - return wrapFixedFuncEmul(gl, false); + public static final GL2ES1 wrapFixedFuncEmul(final GL gl, final ShaderSelectionMode mode, final PMVMatrix pmvMatrix) { + return wrapFixedFuncEmul(gl, mode, null, false, false); } /** - * Mapping fixed function (client) array indices to + * Mapping fixed function (client) array indices to * GLSL array attribute names. * * Useful for uniq mapping of canonical array index names as listed. - * + * * @see #mgl_Vertex * @see javax.media.opengl.fixedfunc.GLPointerFunc#GL_VERTEX_ARRAY * @see #mgl_Normal @@ -62,7 +90,7 @@ public class FixedFuncUtil { * @see javax.media.opengl.fixedfunc.GLPointerFunc#glNormalPointer * @see javax.media.opengl.fixedfunc.GLPointerFunc#glTexCoordPointer */ - public static String getPredefinedArrayIndexName(int glArrayIndex) { + public static String getPredefinedArrayIndexName(final int glArrayIndex) { return GLPointerFuncUtil.getPredefinedArrayIndexName(glArrayIndex); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java new file mode 100644 index 000000000..426fb0d85 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java @@ -0,0 +1,27 @@ +package com.jogamp.opengl.util.glsl.fixedfunc; + +/** + * Shader selection mode + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX + */ +public enum ShaderSelectionMode { + /** Auto shader selection, based upon FFP states. */ + AUTO, + /** Fixed shader selection: Simple color. */ + COLOR, + /** Fixed shader selection: Multi-Textured color. 2 texture units. */ + COLOR_TEXTURE2, + /** Fixed shader selection: Multi-Textured color. 4 texture units. */ + COLOR_TEXTURE4, + /** Fixed shader selection: Multi-Textured color. 8 texture units. */ + COLOR_TEXTURE8, + /** Fixed shader selection: Color with vertex-lighting. */ + COLOR_LIGHT_PER_VERTEX, + /** Fixed shader selection: Multi-Textured color with vertex-lighting. 8 texture units.*/ + COLOR_TEXTURE8_LIGHT_PER_VERTEX +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java index a5b1c6687..1d629131e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java @@ -30,7 +30,7 @@ public abstract class CompileShader { public abstract String getFragmentShaderCompiler(); - public void processOneShader(String resourceName) + public void processOneShader(final String resourceName) throws IOException, UnsupportedEncodingException, InterruptedException { int type = -1; @@ -49,31 +49,31 @@ public abstract class CompileShader { suffixLen = 4; type = GL2ES2.GL_VERTEX_SHADER; } - String justName = basename(resourceName); + final String justName = basename(resourceName); outName = justName.substring(0, justName.length() - suffixLen) + ShaderCode.getFileSuffix(true, type); - URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); - String dirName = dirname(resourceURL.getPath()); + final URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); + final String dirName = dirname(resourceURL.getPath()); - outName = dirName + File.separator + "bin" + File.separator + - ShaderCode.getBinarySubPath(getBinaryFormat()) + File.separator + + outName = dirName + File.separator + "bin" + File.separator + + ShaderCode.getBinarySubPath(getBinaryFormat()) + File.separator + outName; processOneShader(resourceName, outName, type); } - public void processOneShader(String resourceName, String outName, int type) + public void processOneShader(final String resourceName, final String outName, final int type) throws IOException, UnsupportedEncodingException, InterruptedException { - URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); - String dirName = dirname(resourceURL.getPath()); + final URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); + final String dirName = dirname(resourceURL.getPath()); - CharSequence shader = ShaderCode.readShaderSource(null, resourceName, false); + final CharSequence shader = ShaderCode.readShaderSource(null, resourceName, false); if(null==shader) { System.err.println("Can't find shader source " + resourceName + " - ignored"); return; } System.err.println("Preprocessing: "+ resourceName+", in dir: "+dirName); - String justName = basename(resourceName); + final String justName = basename(resourceName); String processor; switch (type) { case GL2ES2.GL_VERTEX_SHADER: @@ -85,38 +85,37 @@ public abstract class CompileShader { default: throw new GLException("Unknown shader type: "+type); } - File outputFile = new File(outName); + final File outputFile = new File(outName); // Write shader to a file in java.io.tmpdir - File tmpDir = new File(dirName+File.separator+"tmp"); + final File tmpDir = new File(dirName+File.separator+"tmp"); tmpDir.mkdirs(); - File tmpFile = new File(tmpDir, justName); - Writer writer = new BufferedWriter(new FileWriter(tmpFile)); + final File tmpFile = new File(tmpDir, justName); + final Writer writer = new BufferedWriter(new FileWriter(tmpFile)); writer.write(shader.toString(), 0, shader.length()); writer.flush(); writer.close(); System.err.println("Preprocessed: "+ tmpFile.getAbsolutePath()); - File processorDir = getSDKCompilerDir(); + final File processorDir = getSDKCompilerDir(); System.err.println("SDK: "+ processorDir.getAbsolutePath() + ", compiler: "+processor); System.err.println("Output: "+ outputFile.getAbsolutePath()); // Run the tool - Process process = Runtime.getRuntime().exec(new String[] { + final Process process = Runtime.getRuntime().exec(new String[] { processorDir.getAbsolutePath() + File.separator + processor, tmpFile.getAbsolutePath(), outputFile.getAbsolutePath() }); // , null, processorDir); - new StreamMonitor(process.getInputStream()); - new StreamMonitor(process.getErrorStream()); + new IOUtil.StreamMonitor( new InputStream[] { process.getInputStream(), process.getErrorStream() }, System.out, null ); process.waitFor(); // Delete the temporary file // tmpFile.delete(); } - protected static String basename(String path) { + protected static String basename(final String path) { int lastSlash = path.lastIndexOf("/"); if (lastSlash < 0) { lastSlash = path.lastIndexOf("\\"); @@ -130,57 +129,27 @@ public abstract class CompileShader { return basename; } - protected static String dirname(String path) { + protected static String dirname(final String path) { int lastSlash = path.lastIndexOf("/"); if (lastSlash < 0) { lastSlash = path.lastIndexOf("\\"); } String dirname; if (lastSlash < 0) { - dirname = new String(); + dirname = ""; } else { dirname = path.substring(0, lastSlash + 1); } return dirname; } - public void run(String[] args) { + public void run(final String[] args) { try { for (int i = 0; i < args.length; i++) { processOneShader(args[i]); } - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); } } - - private static class StreamMonitor implements Runnable { - private InputStream istream; - public StreamMonitor(InputStream stream) { - istream = stream; - new Thread(this, "Output Reader Thread").start(); - } - - public void run() - { - byte[] buffer = new byte[4096]; - try { - int numRead = 0; - do { - numRead = istream.read(buffer); - if (numRead > 0) { - System.out.write(buffer, 0, numRead); - System.out.flush(); - } - } while (numRead >= 0); - } - catch (IOException e) { - try { - istream.close(); - } catch (IOException e2) { - } - // Should allow clean exit when process shuts down - } - } - } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java index 8eb9ef579..9ff37c7ee 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java @@ -13,7 +13,7 @@ public class CompileShaderNVidia extends CompileShader { private static final String NVAPSDK; static { - String nvapSDKProp = System.getProperty("NVAPSDK"); + final String nvapSDKProp = System.getProperty("NVAPSDK"); if (nvapSDKProp != null) { NVAPSDK = nvapSDKProp; } else { @@ -21,10 +21,12 @@ public class CompileShaderNVidia extends CompileShader { } } + @Override public int getBinaryFormat() { return GLES2.GL_NVIDIA_PLATFORM_BINARY_NV; } + @Override public File getSDKCompilerDir() { File compilerDir = new File( NVAPSDK + File.separator + "tools" + File.separator ); File compilerFile = new File( compilerDir, getVertexShaderCompiler()); @@ -39,15 +41,17 @@ public class CompileShaderNVidia extends CompileShader { return compilerDir; } + @Override public String getVertexShaderCompiler() { return "glslv.bat"; } + @Override public String getFragmentShaderCompiler() { return "glslf.bat"; } - public static void main(String[] args) { + public static void main(final String[] args) { new CompileShaderNVidia().run(args); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/BackingStoreManager.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/BackingStoreManager.java index 7b6a1b479..c1b5025f8 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/BackingStoreManager.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/BackingStoreManager.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/Level.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/Level.java index 5ba3f7330..e694f1b33 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/Level.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/Level.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,29 +42,29 @@ package com.jogamp.opengl.util.packrect; import java.util.*; public class Level { - private int width; + private final int width; private int height; - private int yPos; - private LevelSet holder; + private final int yPos; + private final LevelSet holder; - private List/*<Rect>*/ rects = new ArrayList/*<Rect>*/(); - private List/*<Rect>*/ freeList; + private final List<Rect> rects = new ArrayList<Rect>(); + private List<Rect> freeList; private int nextAddX; - static class RectXComparator implements Comparator { - public int compare(Object o1, Object o2) { - Rect r1 = (Rect) o1; - Rect r2 = (Rect) o2; + static class RectXComparator implements Comparator<Rect> { + @Override + public int compare(final Rect r1, final Rect r2) { return r1.x() - r2.x(); } - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { return this == obj; } } - private static final Comparator rectXComparator = new RectXComparator(); + private static final Comparator<Rect> rectXComparator = new RectXComparator(); - public Level(int width, int height, int yPos, LevelSet holder) { + public Level(final int width, final int height, final int yPos, final LevelSet holder) { this.width = width; this.height = height; this.yPos = yPos; @@ -80,7 +80,7 @@ public class Level { in the RectanglePacker and allocation from the free list. More disruptive changes like compaction of the level must be requested explicitly. */ - public boolean add(Rect rect) { + public boolean add(final Rect rect) { if (rect.h() > height) { // See whether it's worth trying to expand vertically if (nextAddX + rect.w() > width) { @@ -108,8 +108,8 @@ public class Level { // See whether we can add from the free list if (freeList != null) { Rect candidate = null; - for (Iterator iter = freeList.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (final Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + final Rect cur = iter.next(); if (cur.canContain(rect)) { candidate = cur; break; @@ -128,7 +128,7 @@ public class Level { candidate.setSize(candidate.w() - rect.w(), height); freeList.add(candidate); } - + coalesceFreeList(); return true; @@ -139,7 +139,7 @@ public class Level { } /** Removes the given Rect from this Level. */ - public boolean remove(Rect rect) { + public boolean remove(final Rect rect) { if (!rects.remove(rect)) return false; @@ -149,7 +149,7 @@ public class Level { nextAddX -= rect.w(); } else { if (freeList == null) { - freeList = new ArrayList/*<Rect>*/(); + freeList = new ArrayList<Rect>(); } freeList.add(new Rect(rect.x(), rect.y(), rect.w(), height, null)); coalesceFreeList(); @@ -165,14 +165,14 @@ public class Level { /** Indicates whether this Level could satisfy an allocation request if it were compacted. */ - public boolean couldAllocateIfCompacted(Rect rect) { + public boolean couldAllocateIfCompacted(final Rect rect) { if (rect.h() > height) return false; if (freeList == null) return false; int freeListWidth = 0; - for (Iterator iter = freeList.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (final Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + final Rect cur = iter.next(); freeListWidth += cur.w(); } // Add on the remaining space at the end @@ -180,12 +180,12 @@ public class Level { return (freeListWidth >= rect.w()); } - public void compact(Object backingStore, BackingStoreManager manager) { + public void compact(final Object backingStore, final BackingStoreManager manager) { Collections.sort(rects, rectXComparator); int nextCompactionDest = 0; manager.beginMovement(backingStore, backingStore); - for (Iterator iter = rects.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (final Iterator<Rect> iter = rects.iterator(); iter.hasNext(); ) { + final Rect cur = iter.next(); if (cur.x() != nextCompactionDest) { manager.move(backingStore, cur, backingStore, new Rect(nextCompactionDest, cur.y(), cur.w(), cur.h(), null)); @@ -198,14 +198,14 @@ public class Level { manager.endMovement(backingStore, backingStore); } - public Iterator iterator() { + public Iterator<Rect> iterator() { return rects.iterator(); } /** Visits all Rects contained in this Level. */ - public void visit(RectVisitor visitor) { - for (Iterator iter = rects.iterator(); iter.hasNext(); ) { - Rect rect = (Rect) iter.next(); + public void visit(final RectVisitor visitor) { + for (final Iterator<Rect> iter = rects.iterator(); iter.hasNext(); ) { + final Rect rect = iter.next(); visitor.visit(rect); } } @@ -216,8 +216,8 @@ public class Level { original Rects. */ public void updateRectangleReferences() { for (int i = 0; i < rects.size(); i++) { - Rect cur = (Rect) rects.get(i); - Rect next = cur.getNextLocation(); + final Rect cur = rects.get(i); + final Rect next = cur.getNextLocation(); next.setPosition(cur.x(), cur.y()); if (cur.w() != next.w() || cur.h() != next.h()) throw new RuntimeException("Unexpected disparity in rectangle sizes during updateRectangleReferences"); @@ -235,8 +235,8 @@ public class Level { Collections.sort(freeList, rectXComparator); int i = 0; while (i < freeList.size() - 1) { - Rect r1 = (Rect) freeList.get(i); - Rect r2 = (Rect) freeList.get(i+1); + final Rect r1 = freeList.get(i); + final Rect r2 = freeList.get(i+1); if (r1.maxX() + 1 == r2.x()) { // Coalesce r1 and r2 into one block freeList.remove(i+1); @@ -246,7 +246,7 @@ public class Level { } } // See whether the last block bumps up against the addition point - Rect last = (Rect) freeList.get(freeList.size() - 1); + final Rect last = freeList.get(freeList.size() - 1); if (last.maxX() + 1 == nextAddX) { nextAddX -= last.w(); freeList.remove(freeList.size() - 1); @@ -262,8 +262,8 @@ public class Level { public void dumpFreeSpace() { int freeListWidth = 0; - for (Iterator iter = freeList.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (final Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + final Rect cur = iter.next(); System.err.println(" Free rectangle at " + cur); freeListWidth += cur.w(); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/LevelSet.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/LevelSet.java index 6783aec3b..803ca28cf 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/LevelSet.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/LevelSet.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -47,14 +47,14 @@ import java.util.*; public class LevelSet { // Maintained in sorted order by increasing Y coordinate - private List/*<Level>*/ levels = new ArrayList/*<Level>*/(); + private final List<Level> levels = new ArrayList<Level>(); private int nextAddY; - private int w; + private final int w; private int h; /** A LevelSet manages all of the backing store for a region of a specified width and height. */ - public LevelSet(int w, int h) { + public LevelSet(final int w, final int h) { this.w = w; this.h = h; } @@ -66,14 +66,14 @@ public class LevelSet { the LevelSet given its current dimensions, false if not. Caller is responsible for performing compaction, expansion, etc. as a consequence. */ - public boolean add(Rect rect) { + public boolean add(final Rect rect) { if (rect.w() > w) return false; // Go in reverse order through the levels seeing whether we can // trivially satisfy the allocation request for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + final Level level = levels.get(i); if (level.add(rect)) return true; } @@ -82,7 +82,7 @@ public class LevelSet { // increases the computational complexity of the addition process, // but prevents us from expanding unnecessarily. for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + final Level level = levels.get(i); if (level.couldAllocateIfCompacted(rect)) return false; } @@ -92,23 +92,23 @@ public class LevelSet { if (nextAddY + rect.h() > h) return false; - Level newLevel = new Level(w, rect.h(), nextAddY, this); + final Level newLevel = new Level(w, rect.h(), nextAddY, this); levels.add(newLevel); nextAddY += rect.h(); - boolean res = newLevel.add(rect); + final boolean res = newLevel.add(rect); if (!res) throw new RuntimeException("Unexpected failure in addition to new Level"); return true; } /** Removes the given Rect from this LevelSet. */ - public boolean remove(Rect rect) { + public boolean remove(final Rect rect) { for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + final Level level = levels.get(i); if (level.remove(rect)) return true; } - + return false; } @@ -116,14 +116,14 @@ public class LevelSet { if necessary. This is the correct fallback path to {@link #add(Rect)} above. Returns true if allocated successfully, false otherwise (indicating the need to expand the backing store). */ - public boolean compactAndAdd(Rect rect, - Object backingStore, - BackingStoreManager manager) { + public boolean compactAndAdd(final Rect rect, + final Object backingStore, + final BackingStoreManager manager) { for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + final Level level = levels.get(i); if (level.couldAllocateIfCompacted(rect)) { level.compact(backingStore, manager); - boolean res = level.add(rect); + final boolean res = level.add(rect); if (!res) throw new RuntimeException("Unexpected failure to add after compaction"); return true; @@ -136,7 +136,7 @@ public class LevelSet { /** Indicates whether it's legal to trivially increase the height of the given Level. This is only possible if it's the last Level added and there's enough room in the backing store. */ - public boolean canExpand(Level level, int height) { + public boolean canExpand(final Level level, final int height) { if (levels.isEmpty()) return false; // Should not happen if (levels.get(levels.size() - 1) == level && @@ -145,7 +145,7 @@ public class LevelSet { return false; } - public void expand(Level level, int oldHeight, int newHeight) { + public void expand(final Level level, final int oldHeight, final int newHeight) { nextAddY += (newHeight - oldHeight); } @@ -156,7 +156,7 @@ public class LevelSet { /** Sets the height of this LevelSet. It is only legal to reduce the height to greater than or equal to the currently used height. */ - public void setHeight(int height) throws IllegalArgumentException { + public void setHeight(final int height) throws IllegalArgumentException { if (height < getUsedHeight()) { throw new IllegalArgumentException("May not reduce height below currently used height"); } @@ -170,11 +170,11 @@ public class LevelSet { it may be profitable to perform a compaction. */ public float verticalFragmentationRatio() { int freeHeight = 0; - int usedHeight = getUsedHeight(); + final int usedHeight = getUsedHeight(); if (usedHeight == 0) return 0.0f; - for (Iterator iter = iterator(); iter.hasNext(); ) { - Level level = (Level) iter.next(); + for (final Iterator<Level> iter = iterator(); iter.hasNext(); ) { + final Level level = iter.next(); if (level.isEmpty()) { freeHeight += level.h(); } @@ -182,14 +182,14 @@ public class LevelSet { return (float) freeHeight / (float) usedHeight; } - public Iterator iterator() { + public Iterator<Level> iterator() { return levels.iterator(); } /** Visits all Rects contained in this LevelSet. */ - public void visit(RectVisitor visitor) { - for (Iterator iter = levels.iterator(); iter.hasNext(); ) { - Level level = (Level) iter.next(); + public void visit(final RectVisitor visitor) { + for (final Iterator<Level> iter = levels.iterator(); iter.hasNext(); ) { + final Level level = iter.next(); level.visit(visitor); } } @@ -199,8 +199,8 @@ public class LevelSet { update the new Rects in a newly laid-out LevelSet with the original Rects. */ public void updateRectangleReferences() { - for (Iterator iter = levels.iterator(); iter.hasNext(); ) { - Level level = (Level) iter.next(); + for (final Iterator<Level> iter = levels.iterator(); iter.hasNext(); ) { + final Level level = iter.next(); level.updateRectangleReferences(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/Rect.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/Rect.java index 6206c4a11..cbf6b933c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/Rect.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/Rect.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -72,16 +72,16 @@ public class Rect { // there is no room left due either to fragmentation or just being // out of space) private Rect nextLocation; - + public Rect() { this(null); } - public Rect(Object userData) { + public Rect(final Object userData) { this(0, 0, 0, 0, userData); } - public Rect(int x, int y, int w, int h, Object userData) { + public Rect(final int x, final int y, final int w, final int h, final Object userData) { setPosition(x, y); setSize(w, h); setUserData(userData); @@ -94,7 +94,7 @@ public class Rect { public Object getUserData() { return userData; } public Rect getNextLocation() { return nextLocation; } - public void setPosition(int x, int y) { + public void setPosition(final int x, final int y) { if (x < 0) throw new IllegalArgumentException("Negative x"); if (y < 0) @@ -103,7 +103,7 @@ public class Rect { this.y = y; } - public void setSize(int w, int h) throws IllegalArgumentException { + public void setSize(final int w, final int h) throws IllegalArgumentException { if (w < 0) throw new IllegalArgumentException("Negative width"); if (h < 0) @@ -112,8 +112,8 @@ public class Rect { this.h = h; } - public void setUserData(Object obj) { userData = obj; } - public void setNextLocation(Rect nextLocation) { this.nextLocation = nextLocation; } + public void setUserData(final Object obj) { userData = obj; } + public void setNextLocation(final Rect nextLocation) { this.nextLocation = nextLocation; } // Helpers for computations. @@ -139,11 +139,12 @@ public class Rect { return y() + h() - 1; } - public boolean canContain(Rect other) { + public boolean canContain(final Rect other) { return (w() >= other.w() && h() >= other.h()); } + @Override public String toString() { return "[Rect x: " + x() + " y: " + y() + " w: " + w() + " h: " + h() + "]"; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/RectVisitor.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/RectVisitor.java index 49cfc82e6..5db216742 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/RectVisitor.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/RectVisitor.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/packrect/RectanglePacker.java b/src/jogl/classes/com/jogamp/opengl/util/packrect/RectanglePacker.java index 1496a04a6..794ae4493 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/packrect/RectanglePacker.java +++ b/src/jogl/classes/com/jogamp/opengl/util/packrect/RectanglePacker.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -48,34 +48,34 @@ import java.util.*; backing store, when necessary. */ public class RectanglePacker { - private BackingStoreManager manager; + private final BackingStoreManager manager; private Object backingStore; private LevelSet levels; - private float EXPANSION_FACTOR = 0.5f; - private float SHRINK_FACTOR = 0.3f; + private static final float EXPANSION_FACTOR = 0.5f; + private static final float SHRINK_FACTOR = 0.3f; - private int initialWidth; - private int initialHeight; + private final int initialWidth; + private final int initialHeight; private int maxWidth = -1; private int maxHeight = -1; - static class RectHComparator implements Comparator { - public int compare(Object o1, Object o2) { - Rect r1 = (Rect) o1; - Rect r2 = (Rect) o2; + static class RectHComparator implements Comparator<Rect> { + @Override + public int compare(final Rect r1, final Rect r2) { return r2.h() - r1.h(); } - public boolean equals(Object obj) { + @Override + public boolean equals(final Object obj) { return this == obj; } } - private static final Comparator rectHComparator = new RectHComparator(); + private static final Comparator<Rect> rectHComparator = new RectHComparator(); - public RectanglePacker(BackingStoreManager manager, - int initialWidth, - int initialHeight) { + public RectanglePacker(final BackingStoreManager manager, + final int initialWidth, + final int initialHeight) { this.manager = manager; levels = new LevelSet(initialWidth, initialHeight); this.initialWidth = initialWidth; @@ -95,7 +95,7 @@ public class RectanglePacker { necessary. Setting up a maximum width and height introduces the possibility that additions will fail; these are handled with the BackingStoreManager's allocationFailed notification. */ - public void setMaxSize(int maxWidth, int maxHeight) { + public void setMaxSize(final int maxWidth, final int maxHeight) { this.maxWidth = maxWidth; this.maxHeight = maxHeight; } @@ -107,7 +107,7 @@ public class RectanglePacker { BackingStoreManager#preExpand BackingStoreManager.preExpand} does not clear enough space for the incoming rectangle, then this method will throw a RuntimeException. */ - public void add(Rect rect) throws RuntimeException { + public void add(final Rect rect) throws RuntimeException { // Allocate backing store if we don't have any yet if (backingStore == null) backingStore = manager.allocateBackingStore(levels.w(), levels.h()); @@ -143,12 +143,12 @@ public class RectanglePacker { } /** Removes the given rectangle from this RectanglePacker. */ - public void remove(Rect rect) { + public void remove(final Rect rect) { levels.remove(rect); } /** Visits all Rects contained in this RectanglePacker. */ - public void visit(RectVisitor visitor) { + public void visit(final RectVisitor visitor) { levels.visit(visitor); } @@ -168,7 +168,7 @@ public class RectanglePacker { } // The "cause" rect may be null - private void compactImpl(Rect cause) { + private void compactImpl(final Rect cause) { // Have to either expand, compact or both. Need to figure out what // direction to go. Prefer to expand vertically. Expand // horizontally only if rectangle being added is too wide. FIXME: @@ -203,14 +203,14 @@ public class RectanglePacker { } nextLevelSet = new LevelSet(newWidth, newHeight); - + // Make copies of all existing rectangles - List/*<Rect>*/ newRects = new ArrayList/*<Rect>*/(); - for (Iterator i1 = levels.iterator(); i1.hasNext(); ) { - Level level = (Level) i1.next(); - for (Iterator i2 = level.iterator(); i2.hasNext(); ) { - Rect cur = (Rect) i2.next(); - Rect newRect = new Rect(0, 0, cur.w(), cur.h(), null); + final List<Rect> newRects = new ArrayList<Rect>(); + for (final Iterator<Level> i1 = levels.iterator(); i1.hasNext(); ) { + final Level level = i1.next(); + for (final Iterator<Rect> i2 = level.iterator(); i2.hasNext(); ) { + final Rect cur = i2.next(); + final Rect newRect = new Rect(0, 0, cur.w(), cur.h(), null); cur.setNextLocation(newRect); // Hook up the reverse mapping too for easier replacement newRect.setNextLocation(cur); @@ -222,8 +222,8 @@ public class RectanglePacker { Collections.sort(newRects, rectHComparator); // Try putting all of these rectangles into the new level set done = true; - for (Iterator iter = newRects.iterator(); iter.hasNext(); ) { - if (!nextLevelSet.add((Rect) iter.next())) { + for (final Iterator<Rect> iter = newRects.iterator(); iter.hasNext(); ) { + if (!nextLevelSet.add(iter.next())) { done = false; break; } @@ -268,13 +268,13 @@ public class RectanglePacker { // new locations of rectangles on the backing store. Allocate a // new backing store, move the contents over and deallocate the // old one. - Object newBackingStore = manager.allocateBackingStore(nextLevelSet.w(), + final Object newBackingStore = manager.allocateBackingStore(nextLevelSet.w(), nextLevelSet.h()); manager.beginMovement(backingStore, newBackingStore); - for (Iterator i1 = levels.iterator(); i1.hasNext(); ) { - Level level = (Level) i1.next(); - for (Iterator i2 = level.iterator(); i2.hasNext(); ) { - Rect cur = (Rect) i2.next(); + for (final Iterator<Level> i1 = levels.iterator(); i1.hasNext(); ) { + final Level level = i1.next(); + for (final Iterator<Rect> i2 = level.iterator(); i2.hasNext(); ) { + final Rect cur = i2.next(); manager.move(backingStore, cur, newBackingStore, cur.getNextLocation()); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java new file mode 100644 index 000000000..075da340b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java @@ -0,0 +1,68 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import com.jogamp.opengl.math.FovHVHalves; + +/** + * Constant parameter for one eye. + */ +public final class EyeParameter { + /** Eye number, <code>0</code> for the left eye and <code>1</code> for the right eye. */ + public final int number; + + /** float[3] eye position vector used to define eye height in meter relative to <i>actor</i>. */ + public final float[] positionOffset; + + /** Field of view in both directions, may not be centered, either in radians or tangent. */ + public final FovHVHalves fovhv; + + /** IPD related horizontal distance from nose to pupil in meter. */ + public final float distNoseToPupilX; + + /** Vertical distance from middle-line to pupil in meter. */ + public final float distMiddleToPupilY; + + /** Z-axis eye relief in meter. */ + public final float eyeReliefZ; + + public EyeParameter(final int number, final float[] positionOffset, final FovHVHalves fovhv, + final float distNoseToPupil, final float verticalDelta, final float eyeRelief) { + this.number = number; + this.positionOffset = new float[3]; + System.arraycopy(positionOffset, 0, this.positionOffset, 0, 3); + this.fovhv = fovhv; + this.distNoseToPupilX = distNoseToPupil; + this.distMiddleToPupilY = verticalDelta; + this.eyeReliefZ = eyeRelief; + } + public final String toString() { + return "EyeParam[num "+number+", posOff["+positionOffset[0]+", "+positionOffset[1]+", "+positionOffset[2]+"], "+fovhv+ + ", distPupil[noseX "+distNoseToPupilX+", middleY "+distMiddleToPupilY+", reliefZ "+eyeReliefZ+"]]"; + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java new file mode 100644 index 000000000..aa64ff130 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyePose.java @@ -0,0 +1,69 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import com.jogamp.opengl.math.Quaternion; + +/** + * Position and orientation of one eye. + */ +public final class EyePose { + /** Eye number, <code>0</code> for the left eye and <code>1</code> for the right eye. */ + public final int number; + + /** float[3] eye position vector. */ + public final float[] position; + + /** Eye orientation */ + public final Quaternion orientation; + + public EyePose(final int number) { + this.number = number; + this.position = new float[3]; + this.orientation = new Quaternion(); + } + public EyePose(final int number, final float[] position, final Quaternion orientation) { + this(number); + set(position, orientation); + } + + /** Set position and orientation of this instance. */ + public final void set(final float[] position, final Quaternion orientation) { + System.arraycopy(position, 0, this.position, 0, 3); + this.orientation.set(orientation); + } + /** Set position and orientation of this instance. */ + public final void setPosition(final float posX, final float posY, final float posZ) { + position[0] = posX; + position[1] = posY; + position[2] = posZ; + } + public final String toString() { + return "EyePose[num "+number+", pos["+position[0]+", "+position[1]+", "+position[2]+"], "+orientation+"]"; + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java new file mode 100644 index 000000000..f70ebf928 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java @@ -0,0 +1,262 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +import jogamp.opengl.GLDrawableHelper; +import jogamp.opengl.GLDrawableHelper.GLEventListenerAction; + +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.FBObject.Attachment; +import com.jogamp.opengl.FBObject.TextureAttachment; +import com.jogamp.opengl.FBObject.Attachment.Type; +import com.jogamp.opengl.util.CustomGLEventListener; + +/** + * {@link StereoClientRenderer} utilizing {@link StereoDeviceRenderer} + * implementing {@link GLEventListener} for convenience. + * <p> + * See {@link StereoDeviceRenderer} notes about <a href="StereoDeviceRenderer.html#asymFOVRendering">Correct Asymmetric FOV Rendering</a>. + * <p> + * Implementation renders {@link StereoGLEventListener} + * using one or more {@link FBObject} according to {@link StereoDeviceRenderer#getTextureCount()}. + * </p> + */ +public class StereoClientRenderer implements GLEventListener { + private final GLDrawableHelper helper; + private final StereoDeviceRenderer deviceRenderer; + private final boolean ownsDevice; + private final FBObject[] fbos; + private final int magFilter; + private final int minFilter; + + private int numSamples; + private final TextureAttachment[] fboTexs; + + public StereoClientRenderer(final StereoDeviceRenderer deviceRenderer, final boolean ownsDevice, + final int magFilter, final int minFilter, final int numSamples) { + final int fboCount = deviceRenderer.getTextureCount(); + if( 0 > fboCount || 2 < fboCount ) { + throw new IllegalArgumentException("fboCount must be within [0..2], has "+fboCount+", due to "+deviceRenderer); + } + this.helper = new GLDrawableHelper(); + this.deviceRenderer = deviceRenderer; + this.ownsDevice = ownsDevice; + this.magFilter = magFilter; + this.minFilter = minFilter; + + this.numSamples = numSamples; + + this.fbos = new FBObject[fboCount]; + for(int i=0; i<fboCount; i++) { + this.fbos[i] = new FBObject(); + } + this.fboTexs = new TextureAttachment[fboCount]; + } + + private void initFBOs(final GL gl, final DimensionImmutable size) { + for(int i=0; i<fbos.length; i++) { + fbos[i].init(gl, size.getWidth(), size.getHeight(), numSamples); + if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { + throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); + } + numSamples = fbos[i].getNumSamples(); + + if(numSamples>0) { + fbos[i].attachColorbuffer(gl, 0, true); // MSAA requires alpha + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); + final FBObject ssink = new FBObject(); + { + ssink.init(gl, size.getWidth(), size.getHeight(), 0); + ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); + } + fbos[i].setSamplingSink(ssink); + fbos[i].resetSamplingSink(gl); // validate + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); + } else { + fboTexs[i] = fbos[i].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); + } + fbos[i].unbind(gl); + System.err.println("FBO["+i+"]: "+fbos[i]); + } + + } + + @SuppressWarnings("unused") + private void resetFBOs(final GL gl, final DimensionImmutable size) { + for(int i=0; i<fbos.length; i++) { + fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples); + if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { + throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); + } + numSamples = fbos[i].getNumSamples(); + if(numSamples>0) { + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); + } else { + fboTexs[i] = fbos[i].getColorbuffer(0).getTextureAttachment(); + } + } + } + + public final StereoDeviceRenderer getStereoDeviceRenderer() { return deviceRenderer; } + + public final void addGLEventListener(final StereoGLEventListener l) { + helper.addGLEventListener(l); + } + public final void removeGLEventListener(final StereoGLEventListener l) { + helper.removeGLEventListener(l); + } + + @Override + public void init(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + deviceRenderer.init(gl); + + // We will do some offscreen rendering, setup FBO... + final DimensionImmutable textureSize = deviceRenderer.getTextureCount() > 1 ? deviceRenderer.getSingleSurfaceSize() : deviceRenderer.getTotalSurfaceSize(); + initFBOs(gl, textureSize); + helper.init(drawable, false); + + gl.setSwapInterval(1); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + helper.disposeAllGLEventListener(drawable, false); + for(int i=0; i<fbos.length; i++) { + fbos[i].destroy(gl); + fboTexs[i] = null; + } + if( ownsDevice ) { + deviceRenderer.dispose(gl); + } + } + + @Override + public void display(final GLAutoDrawable drawable) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + + deviceRenderer.beginFrame(gl); + + if(0 < numSamples) { + gl.glEnable(GL.GL_MULTISAMPLE); + } + + final int fboCount = fbos.length; + final int displayRepeatFlags; + if( 1 >= fboCount ) { + displayRepeatFlags = CustomGLEventListener.DISPLAY_DONTCLEAR; + } else { + displayRepeatFlags = 0; + } + + final int[] eyeOrder = deviceRenderer.getDevice().getEyeRenderOrder(); + final int eyeCount = eyeOrder.length; + + // Update eye pos upfront to have same (almost) results + for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) { + deviceRenderer.updateEyePose(eyeNum); + } + + if( 1 == fboCount ) { + fbos[0].bind(gl); + } + + for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) { + final int eyeName = eyeOrder[eyeNum]; + if( 1 < fboCount ) { + fbos[eyeName].bind(gl); + } + + final StereoDeviceRenderer.Eye eye = deviceRenderer.getEye(eyeName); + final RectangleImmutable viewport = eye.getViewport(); + gl.glViewport(viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight()); + + final int displayFlags = eyeNum > 0 ? CustomGLEventListener.DISPLAY_REPEAT | displayRepeatFlags : 0; + final GLEventListenerAction reshapeDisplayAction = new GLEventListenerAction() { + public void run(final GLAutoDrawable drawable, final GLEventListener listener) { + final StereoGLEventListener sl = (StereoGLEventListener) listener; + sl.reshapeForEye(drawable, viewport.getX(), viewport.getY(), viewport.getWidth(), viewport.getHeight(), + eye.getEyeParameter(), eye.getLastEyePose()); + sl.display(drawable, displayFlags); + } }; + helper.runForAllGLEventListener(drawable, reshapeDisplayAction); + + if( 1 < fboCount ) { + fbos[eyeName].unbind(gl); + } + } + + if( 1 == fboCount ) { + fbos[0].unbind(gl); + } + // restore viewport + gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + + if( deviceRenderer.ppAvailable() ) { + deviceRenderer.ppBegin(gl); + if( 1 == fboCount ) { + fbos[0].use(gl, fboTexs[0]); + for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) { + deviceRenderer.ppOneEye(gl, eyeOrder[eyeNum]); + } + fbos[0].unuse(gl); + } else { + for(int eyeNum=0; eyeNum<eyeCount; eyeNum++) { + final int eyeName = eyeOrder[eyeNum]; + fbos[eyeName].use(gl, fboTexs[eyeName]); + deviceRenderer.ppOneEye(gl, eyeName); + fbos[eyeName].unuse(gl); + } + } + deviceRenderer.ppEnd(gl); + } + + if( !drawable.getAutoSwapBufferMode() ) { + drawable.swapBuffers(); + } + deviceRenderer.endFrame(gl); + } + + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + if( !drawable.getAutoSwapBufferMode() ) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + gl.glViewport(0, 0, width, height); + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java new file mode 100644 index 000000000..d32c981a3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -0,0 +1,160 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PointImmutable; + +import jogamp.opengl.Debug; + +import com.jogamp.opengl.math.FovHVHalves; + +/** + * Interface describing a native stereoscopic device + */ +public interface StereoDevice { + public static final boolean DEBUG = Debug.debug("StereoDevice"); + public static final boolean DUMP_DATA = Debug.isPropertyDefined("jogl.debug.StereoDevice.DumpData", true); + + /** Merely a class providing a type-tag for extensions */ + public static class Config { + // NOP + } + + /** Return the factory used to create this device. */ + public StereoDeviceFactory getFactory(); + + /** Disposes this {@link StereoDevice}. */ + public void dispose(); + + /** + * If operation within a device spanning virtual desktop, + * returns the device position. + * <p> + * Otherwise simply 0/0. + * </p> + */ + public PointImmutable getPosition(); + + /** + * Returns the required surface size in pixel. + */ + public DimensionImmutable getSurfaceSize(); + + /** + * Return the device default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}. + * <p> + * Result is an array of float values for + * <ul> + * <li><i>right</i> (positive)</li> + * <li><i>up</i> (positive)</li> + * <li><i>forward</i> (negative)</li> + * </ul> + * </p> + * @return + */ + public float[] getDefaultEyePositionOffset(); + + /** + * Returns the device default {@link FovHVHalves} for all supported eyes + * in natural order, i.e. left and right if supported. + * <p> + * Monoscopic devices return an array length of one, without the value for the right-eye! + * </p> + */ + public FovHVHalves[] getDefaultFOV(); + + /** Start or stop sensors. Returns true if action was successful, otherwise false. */ + public boolean startSensors(boolean start); + + /** Return true if sensors have been started, false otherwise */ + public boolean getSensorsStarted(); + + /** + * Returns an array of the preferred eye rendering order. + * The array length reflects the supported eye count. + * <p> + * Monoscopic devices only support one eye, where stereoscopic device two eyes. + * </p> + */ + public int[] getEyeRenderOrder(); + + /** + * Returns the supported distortion compensation by the {@link StereoDeviceRenderer}, + * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, {@link StereoDeviceRenderer#DISTORTION_CHROMATIC}, etc. + * @see StereoDeviceRenderer#getDistortionBits() + * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + * @see #getRecommendedDistortionBits() + * @see #getMinimumDistortionBits() + */ + public int getSupportedDistortionBits(); + + /** + * Returns the recommended distortion compensation bits for the {@link StereoDeviceRenderer}, + * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, {@link StereoDeviceRenderer#DISTORTION_CHROMATIC} + * {@link StereoDeviceRenderer#DISTORTION_VIGNETTE}. + * <p> + * User shall use the recommended distortion compensation to achieve a distortion free view. + * </p> + * @see StereoDeviceRenderer#getDistortionBits() + * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + * @see #getSupportedDistortionBits() + * @see #getMinimumDistortionBits() + */ + public int getRecommendedDistortionBits(); + + /** + * Returns the minimum distortion compensation bits as required by the {@link StereoDeviceRenderer}, + * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL} in case the stereoscopic display uses [a]spherical lenses. + * <p> + * Minimum distortion compensation bits are being enforced by the {@link StereoDeviceRenderer}. + * </p> + * @see #getSupportedDistortionBits() + * @see #getRecommendedDistortionBits() + * @see StereoDeviceRenderer#getDistortionBits() + * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + */ + public int getMinimumDistortionBits(); + + /** + * Create a new {@link StereoDeviceRenderer} instance. + * + * @param distortionBits {@link StereoDeviceRenderer} distortion bits, e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, etc, + * see {@link #getRecommendedDistortionBits()}. + * @param textureCount desired texture count for post-processing, see {@link StereoDeviceRenderer#getTextureCount()} and {@link StereoDeviceRenderer#ppAvailable()} + * @param eyePositionOffset eye position offset, e.g. {@link #getDefaultEyePositionOffset()}. + * @param eyeFov FovHVHalves[] field-of-view per eye, e.g. {@link #getDefaultFOV()}. May contain only one value for monoscopic devices, + * see {@link #getEyeRenderOrder()}. + * @param pixelsPerDisplayPixel + * @param textureUnit + * @return + */ + public StereoDeviceRenderer createRenderer(final int distortionBits, + final int textureCount, final float[] eyePositionOffset, + final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, final int textureUnit); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java new file mode 100644 index 000000000..c4180585c --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java @@ -0,0 +1,99 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import com.jogamp.common.util.ReflectionUtil; + +/** + * Platform agnostic {@link StereoDevice} factory. + * <p> + * To implement a new {@link StereoDevice}, the following interfaces/classes must be implemented: + * <ul> + * <li>{@link StereoDeviceFactory}</li> + * <li>{@link StereoDevice}</li> + * <li>{@link StereoDeviceRenderer}</li> + * </ul> + * </p> + */ +public abstract class StereoDeviceFactory { + private static final String OVRStereoDeviceClazzName = "jogamp.opengl.oculusvr.OVRStereoDeviceFactory"; + private static final String GenericStereoDeviceClazzName = "jogamp.opengl.util.stereo.GenericStereoDeviceFactory"; + private static final String isAvailableMethodName = "isAvailable"; + + /** {@link StereoDevice} type used for {@link StereoDeviceFactory#createFactory(DeviceType) createFactory(type)}. */ + public static enum DeviceType { + /** + * Auto selection of device in the following order: + * <ol> + * <li>{@link DeviceType#OculusVR}</li> + * <li>{@link DeviceType#Generic}</li> + * </ol> + */ + Default, + /** + * Generic software implementation. + */ + Generic, + /** + * OculusVR implementation. + */ + OculusVR + }; + + public static StereoDeviceFactory createDefaultFactory() { + final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); + StereoDeviceFactory sink = createFactory(cl, OVRStereoDeviceClazzName); + if( null == sink ) { + sink = createFactory(cl, GenericStereoDeviceClazzName); + } + return sink; + } + + public static StereoDeviceFactory createFactory(final DeviceType type) { + final String className; + switch( type ) { + case Default: return createDefaultFactory(); + case Generic: className = GenericStereoDeviceClazzName; break; + case OculusVR: className = OVRStereoDeviceClazzName; break; + default: throw new InternalError("XXX"); + } + final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); + return createFactory(cl, className); + } + + public static StereoDeviceFactory createFactory(final ClassLoader cl, final String implName) { + try { + if(((Boolean)ReflectionUtil.callStaticMethod(implName, isAvailableMethodName, null, null, cl)).booleanValue()) { + return (StereoDeviceFactory) ReflectionUtil.createInstance(implName, cl); + } + } catch (final Throwable t) { if(StereoDevice.DEBUG) { System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); t.printStackTrace(); } } + return null; + } + + public abstract StereoDevice createDevice(final int deviceIndex, final StereoDevice.Config config, final boolean verbose); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java new file mode 100644 index 000000000..ab9ba99a1 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceRenderer.java @@ -0,0 +1,243 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.RectangleImmutable; +import javax.media.opengl.GL; + +import com.jogamp.opengl.math.FovHVHalves; + +/** + * Stereoscopic device rendering interface. + * <p> + * The following pseudo-code describes how to implement a renderer + * using a {@link StereoDeviceRenderer}. + * See {@link StereoClientRenderer} which implements the following: + * <ul> + * <li>device.{@link #beginFrame(GL)}</li> + * <li>For both eyes:<ul> + * <li>device.{@link #updateEyePose(int)}</li> + * <li>if device.{@link #ppAvailable()}: Set the render target, e.g. FBO</li> + * <li>Set the viewport using {@link Eye#getViewport()}</li> + * <li>{@link StereoGLEventListener#reshapeForEye(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream.reshapeEye(..)}</li> + * <li>{@link StereoGLEventListener#display(javax.media.opengl.GLAutoDrawable, int) upstream.display(..)}.</li> + * </ul></li> + * <li>Reset the viewport</li> + * <li>If device.{@link #ppAvailable()}:<ul> + * <li>device.{@link #ppBegin(GL)}</li> + * <li>Use render target, e.g. FBO's texture</li> + * <li>device.{@link #ppBothEyes(GL)} or device.{@link #ppOneEye(GL, int)} for both eyes</li> + * <li>device.{@link #ppEnd(GL)}</li> + * </ul></li> + * <li>device.{@link #endFrame(GL)}</li> + * </ul> + * </p> + * <a name="asymFOVRendering"><h5>Correct {@link FovHVHalves Asymmetric FOV} Rendering</h5></a> + * <p> + * The {@link StereoClientRenderer} shall render both images for each eye correctly <i>Off-axis</i> + * utilizing an asymmetric camera frustum, i.e. by using {@link StereoDevice StereoDevice}'s {@link StereoDevice#getDefaultFOV() default} {@link FovHVHalves}.<br> + * + * Some references: + * <ul> + * <li><a href="https://en.wikipedia.org/wiki/Binocular_vision">Wiki: Binocular Vision</a></li> + * <li><a href="http://paulbourke.net/stereographics/stereorender/">Paul Burke: Stereo Graphics - Stereo Renderer</a></li> + * <li><a href="https://en.wikipedia.org/wiki/Distortion_%28optics%29">Wiki: Distortion (Optics)</a></li> + * </ul> + * </p> + */ +public interface StereoDeviceRenderer { + /** + * Distortion Bit: Barrel distortion compensating lens pincushion distortion + */ + public static final int DISTORTION_BARREL = 1 << 0; + + /** + * Distortion Bit: Chromatic distortion compensating lens chromatic aberration. + */ + public static final int DISTORTION_CHROMATIC = 1 << 1; + + /** + * Distortion Bit: Vignette distortion compensating lens chromatic aberration. + */ + public static final int DISTORTION_VIGNETTE = 1 << 2; + + /** + * Distortion Bit: Timewarp distortion technique to predict + * {@link EyePose} movement to reduce latency. + * <p> + * FIXME: Explanation needs refinement! + * </p> + */ + public static final int DISTORTION_TIMEWARP = 1 << 3; + + + /** Returns the {@link StereoDevice} of this {@link StereoDeviceRenderer} instance. */ + public StereoDevice getDevice(); + + /** + * Interface describing one eye of the stereoscopic device, + * see {@link StereoDeviceRenderer#getEye(int)}. + */ + public static interface Eye { + /** + * Returns the viewport for this eye. + */ + public RectangleImmutable getViewport(); + /** + * Returns the {@link EyeParameter} of this eye. + */ + public EyeParameter getEyeParameter(); + /** + * Returns the last {@link EyePose} of this eye. + */ + public EyePose getLastEyePose(); + } + + /** + * Returns the {@link Eye} instance for the denoted <code>eyeNum</code>. + */ + public Eye getEye(final int eyeNum); + + /** + * Updates the {@link Eye#getLastEyePose()} + * for the denoted <code>eyeNum</code>. + */ + public EyePose updateEyePose(final int eyeNum); + + /** + * Returns used distortion compensation bits, e.g. {@link #DISTORTION_BARREL}, + * in case the stereoscopic display requires such, i.e. in case lenses are utilized. + * <p> + * Distortion requires {@link #ppAvailable() post-processing}. + * </p> + */ + public int getDistortionBits(); + + /** + * Method returns <code>true</code> if using <i>side-by-side</i> (SBS) + * stereoscopic images, otherwise <code>false</code>. + * <p> + * SBS requires that both eye's images are presented + * <i>side-by-side</i> in the final framebuffer. + * </p> + * <p> + * Either the renderer presents the images <i>side-by-side</i> according to the {@link Eye#getViewport() eye's viewport}, + * or {@link #ppAvailable() post-processing} is utilized to merge {@link #getTextureCount() textures} + * to a <i>side-by-side</i> configuration. + * </p> + */ + public boolean usesSideBySideStereo(); + + /** + * Returns the unified surface size of one eye's a single image in pixel units. + */ + public DimensionImmutable getSingleSurfaceSize(); + + /** + * Returns the total surface size required for the complete images in pixel units. + * <p> + * If {@link #usesSideBySideStereo()} the total size spans over both {@link #getSingleSurfaceSize()}, side-by-side. + * </p> + * <p> + * Otherwise the size is equal to {@link #getSingleSurfaceSize()}. + * </p> + */ + public DimensionImmutable getTotalSurfaceSize(); + + /** + * Returns the used texture-image count for post-processing, see {@link #ppAvailable()}. + * <p> + * In case the renderer does not support multiple textures for post-processing, + * or no post-processing at all, method returns zero despite the request + * from {@link StereoDevice#createRenderer(int, int, float[], com.jogamp.opengl.math.FovHVHalves[], float)}. + * </p> + */ + public int getTextureCount(); + + /** Returns the desired texture-image unit for post-processing, see {@link #ppAvailable()}. */ + public int getTextureUnit(); + + /** Initialize OpenGL related resources */ + public void init(final GL gl); + + /** Release all OpenGL related resources */ + public void dispose(final GL gl); + + /** Notifying that a new frame is about to start. */ + public void beginFrame(final GL gl); + + /** Notifying that the frame has been rendered completely. */ + public void endFrame(final GL gl); + + /** + * Returns <code>true</code> if stereoscopic post-processing is required and available, + * otherwise <code>false</code>. + * <p> + * Stereoscopic post-processing is available if: + * <ul> + * <li>one of the <i>distortion</i> bits are set, see {@link #getDistortionBits()}</li> + * </ul> + * </p> + * <p> + * If stereoscopic post-processing is used + * the following post-processing methods must be called to before {@link #endFrame()}: + * <ul> + * <li>{@link #ppBegin(GL)}</li> + * <li>{@link #ppOneEye(GL, int)} for both eyes</li> + * <li>{@link #ppEnd(GL)}</li> + * </ul> + * </p> + */ + public boolean ppAvailable(); + + /** + * Begin stereoscopic post-processing, see {@link #ppAvailable()}. + * <p> + * {@link #updateEyePose(int)} for both eyes must be called upfront + * when rendering upstream {@link StereoGLEventListener}. + * </p> + * + * @param gl + */ + public void ppBegin(final GL gl); + + /** + * Performs stereoscopic post-processing for one eye, see {@link #ppAvailable()}. + * @param gl + * @param eyeNum + */ + public void ppOneEye(final GL gl, final int eyeNum); + + /** + * End stereoscopic post-processing, see {@link #ppAvailable()}. + * @param gl + */ + public void ppEnd(final GL gl); + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java new file mode 100644 index 000000000..ec580cbf9 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoGLEventListener.java @@ -0,0 +1,74 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.util.CustomGLEventListener; + +/** + * Extended {@link GLEventListener} and {@link CustomGLEventListener} interface + * supporting stereoscopic client rendering. + */ +public interface StereoGLEventListener extends CustomGLEventListener { + /** + * Stereo capable specialization of {@link #reshape(GLAutoDrawable, int, int, int, int)} + * for one {@link StereoDeviceRenderer.Eye}. + * <p> + * Called by the stereo renderer before each {@link #display(GLAutoDrawable)} + * or {@link #display(GLAutoDrawable, int)} call. + * </p> + * <p> + * The client can update it's viewport associated data + * and view volume of the window appropriately. + * </p> + * <p> + * The client shall also update it's projection- and modelview matrices according + * to the given {@link EyeParameter} and {@link EyePose}. + * </p> + * <p> + * For efficiency the GL viewport has already been updated + * via <code>glViewport(x, y, width, height)</code> when this method is called. + * </p> + * + * @param drawable the triggering {@link GLAutoDrawable} + * @param x viewport x-coord in pixel units + * @param y viewport y-coord in pixel units + * @param width viewport width in pixel units + * @param height viewport height in pixel units + * @param eyeParam constant eye parameter, i.e. FOV and IPD + * @param eyePose current eye position and orientation + * @see FloatUtil#makePerspective(float[], int, boolean, com.jogamp.opengl.math.FloatUtil.FovHVHalves, float, float) + */ + public void reshapeForEye(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height, + final EyeParameter eyeParam, final EyePose eyePose); + + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java new file mode 100644 index 000000000..3031013b8 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java @@ -0,0 +1,125 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.stereo; + +import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.util.CustomGLEventListener; +import com.jogamp.opengl.util.stereo.StereoDeviceRenderer.Eye; + +public class StereoUtil { + + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesBarrelDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_BARREL ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesTimewarpDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_TIMEWARP ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesChromaticDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_CHROMATIC ) ; } + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static boolean usesVignetteDistortion(final int distortionBits) { return 0 != ( distortionBits & StereoDeviceRenderer.DISTORTION_VIGNETTE ) ; } + + /** See {@link StereoDeviceRenderer#getDistortionBits()}. */ + public static String distortionBitsToString(final int distortionBits) { + boolean appendComma = false; + final StringBuilder sb = new StringBuilder(); + if( usesBarrelDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("barrel"); appendComma=true; + } + if( usesVignetteDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("vignette"); appendComma=true; + } + if( usesChromaticDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("chroma"); appendComma=true; + } + if( usesTimewarpDistortion(distortionBits) ) { + if( appendComma ) { sb.append(", "); }; + sb.append("timewarp"); appendComma=true; + } + return sb.toString(); + } + + /** + * Calculates the <i>Side By Side</i>, SBS, projection- and modelview matrix for one eye. + * <p> + * {@link #updateEyePose(int)} must be called upfront. + * </p> + * <p> + * This method merely exist as an example implementation to compute the matrices, + * which shall be adopted by the + * {@link CustomGLEventListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int, EyeParameter, EyePose) upstream client code}. + * </p> + * @param eyeNum eye denominator + * @param zNear frustum near value + * @param zFar frustum far value + * @param mat4Projection float[16] projection matrix result + * @param mat4Modelview float[16] modelview matrix result + */ + public static void getSBSUpstreamPMV(final Eye eye, final float zNear, final float zFar, + final float[] mat4Projection, final float[] mat4Modelview) { + final float[] mat4Tmp1 = new float[16]; + final float[] mat4Tmp2 = new float[16]; + final float[] vec3Tmp1 = new float[3]; + final float[] vec3Tmp2 = new float[3]; + final float[] vec3Tmp3 = new float[3]; + + final EyeParameter eyeParam = eye.getEyeParameter(); + final EyePose eyePose = eye.getLastEyePose(); + + // + // Projection + // + FloatUtil.makePerspective(mat4Projection, 0, true, eyeParam.fovhv, zNear, zFar); + + // + // Modelview + // + final Quaternion rollPitchYaw = new Quaternion(); + // private final float eyeYaw = FloatUtil.PI; // 180 degrees in radians + // rollPitchYaw.rotateByAngleY(eyeYaw); + final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, eyePose.position, 0); + VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeParam.positionOffset); + + rollPitchYaw.mult(eyePose.orientation); + final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VectorUtil.VEC3_UNIT_Y, 0); + final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VectorUtil.VEC3_UNIT_Z_NEG, 0); + final float[] center = VectorUtil.addVec3(forward, shiftedEyePos, forward); + + final float[] mLookAt = FloatUtil.makeLookAt(mat4Tmp2, 0, shiftedEyePos, 0, center, 0, up, 0, mat4Tmp1); + final float[] mViewAdjust = FloatUtil.makeTranslation(mat4Modelview, true, + eyeParam.distNoseToPupilX, + eyeParam.distMiddleToPupilY, + eyeParam.eyeReliefZ); + + /* mat4Modelview = */ FloatUtil.multMatrix(mViewAdjust, mLookAt); + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java b/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java new file mode 100644 index 000000000..f5ef5672f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java @@ -0,0 +1,190 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.texture; + +import java.io.IOException; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +import javax.media.opengl.GL; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import com.jogamp.common.util.IOUtil; + +/** + * Simple {@link TextureSequence} implementation + * allowing {@link #addFrame(GL, Texture) existing textures} + * or {@link #addFrame(GL, Class, String, String) image streams} + * to be used and <i>replayed</i> as {@link TextureSequence.TextureFrame frames}. + */ +public class ImageSequence implements TextureSequence { + private final int textureUnit; + private final boolean useBuildInTexLookup; + private final List<TextureSequence.TextureFrame> frames = new ArrayList<TextureSequence.TextureFrame>(); + private final int[] texMinMagFilter = { GL.GL_NEAREST, GL.GL_NEAREST }; + private final int[] texWrapST = { GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE }; + private volatile int frameIdx = 0; + private volatile boolean manualStepping = false; + private int textureFragmentShaderHashCode = 0; + + public ImageSequence(final int textureUnit, final boolean useBuildInTexLookup) { + this.textureUnit = textureUnit; + this.useBuildInTexLookup = useBuildInTexLookup; + } + + public void setParams(final int magFilter, final int minFilter, final int wrapS, final int wrapT) { + texMinMagFilter[0] = minFilter; + texMinMagFilter[1] = magFilter; + texWrapST[0] = wrapS; + texWrapST[1] = wrapT; + } + + public final void addFrame(final GL gl, final Texture tex) { + final TextureSequence.TextureFrame frame = new TextureSequence.TextureFrame(tex); + frames.add(frame); + tex.bind(gl); + gl.glTexParameteri(getTextureTarget(), GL.GL_TEXTURE_MIN_FILTER, texMinMagFilter[0]); + gl.glTexParameteri(getTextureTarget(), GL.GL_TEXTURE_MAG_FILTER, texMinMagFilter[1]); + gl.glTexParameteri(getTextureTarget(), GL.GL_TEXTURE_WRAP_S, texWrapST[0]); + gl.glTexParameteri(getTextureTarget(), GL.GL_TEXTURE_WRAP_T, texWrapST[1]); + } + + public final void addFrame(final GL gl, final Class<?> context, final String imageResourcePath, final String imageSuffix) throws IOException { + final URLConnection urlConn = IOUtil.getResource(context, imageResourcePath); + if(null != urlConn) { + final TextureData texData = TextureIO.newTextureData(GLProfile.getGL2ES2(), urlConn.getInputStream(), false, imageSuffix); + final Texture tex = new Texture(getTextureTarget()); + tex.updateImage(gl, texData); + addFrame(gl, tex); + } + } + public final int getFrameCount() { return frames.size(); } + public final int getCurrentIdx() { return frameIdx; } + public final void setCurrentIdx(final int idx) throws IndexOutOfBoundsException { + if( 0 > idx || idx >= frames.size() ) { + throw new IndexOutOfBoundsException("idx shall be within 0 <= "+idx+" < "+frames.size()); + } + frameIdx=idx; + } + public final void setManualStepping(final boolean v) { manualStepping = v; } + public final boolean getManualStepping() { return manualStepping; } + public final TextureSequence.TextureFrame getFrame(final int idx) { return frames.get(idx); } + + public void destroy(final GL gl) throws GLException { + for(int i=frames.size()-1; i>=0; i--) { + frames.get(i).getTexture().destroy(gl); + } + frames.clear(); + } + + @Override + public int getTextureTarget() { + return GL.GL_TEXTURE_2D; + } + + @Override + public int getTextureUnit() { + return textureUnit; + } + + @Override + public int[] getTextureMinMagFilter() { + return texMinMagFilter; + } + + @Override + public int[] getTextureWrapST() { + return texWrapST; + } + + @Override + public boolean isTextureAvailable() { return frames.size() > 0; } + + @Override + public TextureSequence.TextureFrame getLastTexture() throws IllegalStateException { + return frames.get(frameIdx); // may return null + } + + @Override + public TextureSequence.TextureFrame getNextTexture(final GL gl) throws IllegalStateException { + if( !manualStepping ) { + frameIdx = ( frameIdx + 1 ) % frames.size(); + } + return frames.get(frameIdx); + } + + @Override + public String getRequiredExtensionsShaderStub() throws IllegalStateException { + return "// TextTextureSequence: No extensions required\n"; + } + + @Override + public String getTextureSampler2DType() throws IllegalStateException { + return "sampler2D" ; + } + + private String textureLookupFunctionName = "myTexture2D"; + + @Override + public String getTextureLookupFunctionName(final String desiredFuncName) throws IllegalStateException { + if(useBuildInTexLookup) { + return "texture2D"; + } + if(null != desiredFuncName && desiredFuncName.length()>0) { + textureLookupFunctionName = desiredFuncName; + } + return textureLookupFunctionName; + } + + @Override + public String getTextureLookupFragmentShaderImpl() throws IllegalStateException { + if(useBuildInTexLookup) { + return ""; + } + return + "\n"+ + "vec4 "+textureLookupFunctionName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+ + " return texture2D(image, texCoord);\n"+ + "}\n\n"; + } + + @Override + public int getTextureFragmentShaderHashCode() { + if( !isTextureAvailable() ) { + textureFragmentShaderHashCode = 0; + return 0; + } else if( 0 == textureFragmentShaderHashCode ) { + int hash = 31 + getTextureLookupFragmentShaderImpl().hashCode(); + hash = ((hash << 5) - hash) + getTextureSampler2DType().hashCode(); + textureFragmentShaderHashCode = hash; + } + return textureFragmentShaderHashCode; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java b/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java index 15dd19ea9..e7693f4a6 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -51,18 +52,32 @@ import com.jogamp.opengl.util.texture.spi.*; * Represents an OpenGL texture object. Contains convenience routines * for enabling/disabling OpenGL texture state, binding this texture, * and computing texture coordinates for both the entire image as well - * as a sub-image. - * + * as a sub-image. + * + * <a name="textureCallOrder"><h5>Order of Texture Commands</h5></a> + * <p> + * Due to many confusions w/ texture usage, following list described the order + * and semantics of texture unit selection, binding and enabling. + * <ul> + * <li><i>Optional:</i> Set active textureUnit via <code>gl.glActiveTexture(GL.GL_TEXTURE0 + textureUnit)</code>, <code>0</code> is default.</li> + * <li>Bind <code>textureId</code> -> active <code>textureUnit</code>'s <code>textureTarget</code> via <code>gl.glBindTexture(textureTarget, textureId)</code></li> + * <li><i>Compatible Context Only:</i> Enable active <code>textureUnit</code>'s <code>textureTarget</code> via <code>glEnable(textureTarget)</code>. + * <li><i>Optional:</i> Fiddle with the texture parameters and/or environment settings.</li> + * <li>GLSL: Use <code>textureUnit</code> in your shader program, enable shader program.</li> + * <li>Issue draw commands</li> + * </ul> + * </p> + * * <p><a name="nonpow2"><b>Non-power-of-two restrictions</b></a> * <br> When creating an OpenGL texture object, the Texture class will - * attempt to leverage the <a - * href="http://www.opengl.org/registry/specs/ARB/texture_non_power_of_two.txt">GL_ARB_texture_non_power_of_two</a> - * and <a - * href="http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt">GL_ARB_texture_rectangle</a> - * extensions (in that order) whenever possible. If neither extension - * is available, the Texture class will simply upload a non-pow2-sized + * attempt to use <i>non-power-of-two textures</i> (NPOT) if available, see {@link GL#isNPOTTextureAvailable()}. + * Further more, + * <a href="http://www.opengl.org/registry/specs/ARB/texture_rectangle.txt">GL_ARB_texture_rectangle</a> + * (RECT) will be attempted on OSX w/ ATI drivers. + * If NPOT is not available or RECT not chosen, the Texture class will simply upload a non-pow2-sized * image into a standard pow2-sized texture (without any special - * scaling). Since the choice of extension (or whether one is used at + * scaling). + * Since the choice of extension (or whether one is used at * all) depends on the user's machine configuration, developers are * recommended to use {@link #getImageTexCoords} and {@link * #getSubImageTexCoords}, as those methods will calculate the @@ -90,16 +105,24 @@ import com.jogamp.opengl.util.texture.spi.*; * when switching between textures it is necessary to call {@link * #bind}, but when drawing many triangles all using the same texture, * for best performance only one call to {@link #bind} should be made. + * User may also utilize multiple texture units, + * see <a href="#textureCallOrder"> order of texture commands above</a>. * * <p><a name="premult"><b>Alpha premultiplication and blending</b></a> - * <br> The mathematically correct way to perform blending in OpenGL - * (with the SrcOver "source over destination" mode, or any other - * Porter-Duff rule) is to use "premultiplied color components", which - * means the R/G/ B color components have already been multiplied by - * the alpha value. To make things easier for developers, the Texture - * class will automatically convert non-premultiplied image data into - * premultiplied data when storing it into an OpenGL texture. As a - * result, it is important to use the correct blending function; for + * <p> + * <i>Disclaimer: Consider performing alpha premultiplication in shader code, if really desired! Otherwise use RGBA.</i><br/> + * </p> + * <p> + * The Texture class does not convert RGBA image data into + * premultiplied data when storing it into an OpenGL texture. + * </p> + * <p> + * The mathematically correct way to perform blending in OpenGL + * with the SrcOver "source over destination" mode, or any other + * Porter-Duff rule, is to use <i>premultiplied color components</i>, + * which means the R/G/ B color components must have been multiplied by + * the alpha value. If using <i>premultiplied color components</i> + * it is important to use the correct blending function; for * example, the SrcOver rule is expressed as: <pre> gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); @@ -114,7 +137,7 @@ import com.jogamp.opengl.util.texture.spi.*; float g = g * a; float b = b * a; gl.glColor4f(r, g, b, a); -</pre> +</pre> * * For reference, here is a list of the Porter-Duff compositing rules * and the associated OpenGL blend functions (source and destination @@ -137,9 +160,7 @@ import com.jogamp.opengl.util.texture.spi.*; <TR> <TD> AlphaXor <TD> GL_ONE_MINUS_DST_ALPHA <TD> GL_ONE_MINUS_SRC_ALPHA </TABLE> </CENTER> - * - * @author Chris Campbell - * @author Kenneth Russell + * @author Chris Campbell, Kenneth Russell, et.al. */ public class Texture { /** The GL target type. */ @@ -167,6 +188,13 @@ public class Texture { /** The texture coordinates corresponding to the entire image. */ private TextureCoords coords; + @Override + public String toString() { + return "Texture[target 0x"+Integer.toHexString(target)+", name "+texID+", "+ + imgWidth+"/"+texWidth+" x "+imgHeight+"/"+texHeight+", y-flip "+mustFlipVertically+ + ", "+estimatedMemorySize+" bytes]"; + } + /** An estimate of the amount of texture memory this texture consumes. */ private int estimatedMemorySize; @@ -177,22 +205,49 @@ public class Texture { private static final boolean disableNPOT = Debug.isPropertyDefined("jogl.texture.nonpot", true); private static final boolean disableTexRect = Debug.isPropertyDefined("jogl.texture.notexrect", true); - public Texture(GL gl, TextureData data) throws GLException { + public Texture(final GL gl, final TextureData data) throws GLException { texID = 0; updateImage(gl, data); } - // Constructor for use when creating e.g. cube maps, where there is - // no initial texture data - public Texture(int target) { + /** + * Constructor for use when creating e.g. cube maps, where there is + * no initial texture data + * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D, + * GL2.GL_TEXTURE_RECTANGLE + */ + public Texture(final int target) { texID = 0; this.target = target; } - // Package-private constructor for creating a texture object which wraps - // an existing texture ID from another package - Texture(int textureID, int target, int texWidth, int texHeight, int imgWidth, int imgHeight, - boolean mustFlipVertically) { + /** + * Constructor to wrap an OpenGL texture ID from an external library and allows + * some of the base methods from the Texture class, such as + * binding and querying of texture coordinates, to be used with + * it. Attempts to update such textures' contents will yield + * undefined results. + * + * @param textureID the OpenGL texture object to wrap + * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D, + * GL2.GL_TEXTURE_RECTANGLE + * @param texWidth the width of the texture in pixels + * @param texHeight the height of the texture in pixels + * @param imgWidth the width of the image within the texture in + * pixels (if the content is a sub-rectangle in the upper + * left corner); otherwise, pass in texWidth + * @param imgHeight the height of the image within the texture in + * pixels (if the content is a sub-rectangle in the upper + * left corner); otherwise, pass in texHeight + * @param mustFlipVertically indicates whether the texture + * coordinates must be flipped vertically + * in order to properly display the + * texture + */ + public Texture(final int textureID, final int target, + final int texWidth, final int texHeight, + final int imgWidth, final int imgHeight, + final boolean mustFlipVertically) { this.texID = textureID; this.target = target; this.mustFlipVertically = mustFlipVertically; @@ -206,12 +261,13 @@ public class Texture { * Enables this texture's target (e.g., GL_TEXTURE_2D) in the * given GL context's state. This method is a shorthand equivalent * of the following OpenGL code: - <pre> - gl.glEnable(texture.getTarget()); - </pre> - * + * <pre> + * gl.glEnable(texture.getTarget()); + * </pre> * <p> - * Call is ignored if {@link #getTarget()} is {@link GLES2#GL_TEXTURE_EXTERNAL_OES}. + * Call is ignored if the {@link GL} object's context + * is using a core profile, see {@link GL#isGLcore()}, + * or if {@link #getTarget()} is {@link GLES2#GL_TEXTURE_EXTERNAL_OES}. * </p> * <p> * See the <a href="#perftips">performance tips</a> above for hints @@ -222,22 +278,23 @@ public class Texture { * @throws GLException if no OpenGL context was current or if any * OpenGL-related errors occurred */ - public void enable(GL gl) throws GLException { - if(GLES2.GL_TEXTURE_EXTERNAL_OES != target) { + public void enable(final GL gl) throws GLException { + if( !gl.isGLcore() && GLES2.GL_TEXTURE_EXTERNAL_OES != target) { gl.glEnable(target); } } - + /** * Disables this texture's target (e.g., GL_TEXTURE_2D) in the * given GL state. This method is a shorthand equivalent * of the following OpenGL code: - <pre> - gl.glDisable(texture.getTarget()); - </pre> - * + * <pre> + * gl.glDisable(texture.getTarget()); + * </pre> * <p> - * Call is ignored if {@link #getTarget()} is {@link GLES2#GL_TEXTURE_EXTERNAL_OES}. + * Call is ignored if the {@link GL} object's context + * is using a core profile, see {@link GL#isGLcore()}, + * or if {@link #getTarget()} is {@link GLES2#GL_TEXTURE_EXTERNAL_OES}. * </p> * <p> * See the <a href="#perftips">performance tips</a> above for hints @@ -248,12 +305,12 @@ public class Texture { * @throws GLException if no OpenGL context was current or if any * OpenGL-related errors occurred */ - public void disable(GL gl) throws GLException { - if(GLES2.GL_TEXTURE_EXTERNAL_OES != target) { + public void disable(final GL gl) throws GLException { + if( !gl.isGLcore() && GLES2.GL_TEXTURE_EXTERNAL_OES != target ) { gl.glDisable(target); } } - + /** * Binds this texture to the given GL context. This method is a * shorthand equivalent of the following OpenGL code: @@ -263,28 +320,22 @@ public class Texture { * * See the <a href="#perftips">performance tips</a> above for hints * on how to maximize performance when using many Texture objects. - * + * * @param gl the current GL context * @throws GLException if no OpenGL context was current or if any * OpenGL-related errors occurred */ - public void bind(GL gl) throws GLException { + public void bind(final GL gl) throws GLException { validateTexID(gl, true); - gl.glBindTexture(target, texID); - } - - /** - * @deprecated use {@link #destroy(GL)} - */ - public final void dispose(GL gl) throws GLException { - destroy(gl); + gl.glBindTexture(target, texID); } + /** * Destroys the native resources used by this texture object. * * @throws GLException if any OpenGL-related errors occurred */ - public void destroy(GL gl) throws GLException { + public void destroy(final GL gl) throws GLException { if(0!=texID) { gl.glDeleteTextures(1, new int[] {texID}, 0); texID = 0; @@ -312,7 +363,7 @@ public class Texture { public int getWidth() { return texWidth; } - + /** * Returns the height of the allocated OpenGL texture in pixels. * Note that the texture height will be greater than or equal to the @@ -322,9 +373,9 @@ public class Texture { */ public int getHeight() { return texHeight; - } - - /** + } + + /** * Returns the width of the image contained within this texture. * Note that for non-power-of-two textures in particular this may * not be equal to the result of {@link #getWidth}. It is @@ -366,7 +417,7 @@ public class Texture { * entire image. If the TextureData indicated that the texture * coordinates must be flipped vertically, the returned * TextureCoords will take that into account. - * + * * @return the texture coordinates corresponding to the entire image */ public TextureCoords getImageTexCoords() { @@ -383,10 +434,10 @@ public class Texture { * flipped vertically, the returned TextureCoords will take that * into account; this should not be handled by the end user in the * specification of the y1 and y2 coordinates. - * + * * @return the texture coordinates corresponding to the specified sub-image */ - public TextureCoords getSubImageTexCoords(int x1, int y1, int x2, int y2) { + public TextureCoords getSubImageTexCoords(final int x1, final int y1, final int x2, final int y2) { if (target == GL2.GL_TEXTURE_RECTANGLE_ARB) { if (mustFlipVertically) { return new TextureCoords(x1, texHeight - y1, x2, texHeight - y2); @@ -394,12 +445,12 @@ public class Texture { return new TextureCoords(x1, y1, x2, y2); } } else { - float tx1 = (float)x1 / (float)texWidth; - float ty1 = (float)y1 / (float)texHeight; - float tx2 = (float)x2 / (float)texWidth; - float ty2 = (float)y2 / (float)texHeight; + final float tx1 = (float)x1 / (float)texWidth; + final float ty1 = (float)y1 / (float)texHeight; + final float tx2 = (float)x2 / (float)texWidth; + final float ty2 = (float)y2 / (float)texHeight; if (mustFlipVertically) { - float yMax = (float) imgHeight / (float) texHeight; + final float yMax = (float) imgHeight / (float) texHeight; return new TextureCoords(tx1, yMax - ty1, tx2, yMax - ty2); } else { return new TextureCoords(tx1, ty1, tx2, ty2); @@ -408,12 +459,12 @@ public class Texture { } /** - * Updates the entire content area of this texture using the data in - * the given image. - * + * Updates the entire content area incl. {@link TextureCoords} + * of this texture using the data in the given image. + * * @throws GLException if any OpenGL-related errors occurred */ - public void updateImage(GL gl, TextureData data) throws GLException { + public void updateImage(final GL gl, final TextureData data) throws GLException { updateImage(gl, data, 0); } @@ -430,13 +481,27 @@ public class Texture { } /** - * Updates the content area of the specified target of this texture + * Change whether the TextureData requires a vertical flip of + * the texture coords. + * <p> + * No-op if no change, otherwise generates new {@link TextureCoords}. + * </p> + */ + public void setMustFlipVertically(final boolean v) { + if( v != mustFlipVertically ) { + mustFlipVertically = v; + updateTexCoords(); + } + } + + /** + * Updates the content area incl. {@link TextureCoords} of the specified target of this texture * using the data in the given image. In general this is intended * for construction of cube maps. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void updateImage(GL gl, TextureData data, int targetOverride) throws GLException { + public void updateImage(final GL gl, final TextureData data, final int targetOverride) throws GLException { validateTexID(gl, true); imgWidth = data.getWidth(); @@ -457,7 +522,7 @@ public class Texture { data.setHaveGL12(gl.isExtensionAvailable(GLExtensions.VERSION_1_2)); // Indicates whether both width and height are power of two - boolean isPOT = isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight); + final boolean isPOT = isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight); // Note that automatic mipmap generation doesn't work for // GL_ARB_texture_rectangle @@ -569,7 +634,7 @@ public class Texture { } if (data.getMipmap() && !haveAutoMipmapGeneration) { - int[] align = new int[1]; + final int[] align = new int[1]; gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); @@ -579,7 +644,7 @@ public class Texture { try { // FIXME: may need check for GLUnsupportedException - GLU glu = GLU.createGLU(gl); + final GLU glu = GLU.createGLU(gl); glu.gluBuild2DMipmaps(texTarget, data.getInternalFormat(), data.getWidth(), data.getHeight(), data.getPixelFormat(), data.getPixelType(), data.getBuffer()); @@ -588,7 +653,7 @@ public class Texture { } } else { checkCompressedTextureExtensions(gl, data); - Buffer[] mipmapData = data.getMipmapData(); + final Buffer[] mipmapData = data.getMipmapData(); if (mipmapData != null) { int width = texWidth; int height = texHeight; @@ -619,7 +684,7 @@ public class Texture { texWidth, texHeight, data.getBorder(), data.getBuffer().capacity(), data.getBuffer()); } else { - ByteBuffer buf = DDSImage.allocateBlankBuffer(texWidth, + final ByteBuffer buf = DDSImage.allocateBlankBuffer(texWidth, texHeight, data.getInternalFormat()); gl.glCompressedTexImage2D(texTarget, 0, data.getInternalFormat(), @@ -645,9 +710,9 @@ public class Texture { } } - int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR); - int magFilter = GL.GL_LINEAR; - int wrapMode = (gl.isExtensionAvailable(GLExtensions.VERSION_1_2) || !gl.isGL2()) ? GL.GL_CLAMP_TO_EDGE : GL2.GL_CLAMP; + final int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR); + final int magFilter = GL.GL_LINEAR; + final int wrapMode = (gl.isExtensionAvailable(GLExtensions.VERSION_1_2) || !gl.isGL2()) ? GL.GL_CLAMP_TO_EDGE : GL2.GL_CLAMP; // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB if (texTarget != GL2.GL_TEXTURE_RECTANGLE_ARB) { @@ -655,8 +720,8 @@ public class Texture { gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter); gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_S, wrapMode); gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_T, wrapMode); - if (this.target == GL2.GL_TEXTURE_CUBE_MAP) { - gl.glTexParameteri(texParamTarget, GL2.GL_TEXTURE_WRAP_R, wrapMode); + if (this.target == GL.GL_TEXTURE_CUBE_MAP) { + gl.glTexParameteri(texParamTarget, GL2ES2.GL_TEXTURE_WRAP_R, wrapMode); } } @@ -693,7 +758,7 @@ public class Texture { * * @throws GLException if any OpenGL-related errors occurred */ - public void updateSubImage(GL gl, TextureData data, int mipmapLevel, int x, int y) throws GLException { + public void updateSubImage(final GL gl, final TextureData data, final int mipmapLevel, final int x, final int y) throws GLException { if (usingAutoMipmapGeneration && mipmapLevel != 0) { // When we're using mipmap generation via GL_GENERATE_MIPMAP, we // don't need to update other mipmap levels @@ -733,10 +798,10 @@ public class Texture { * @throws GLException if no OpenGL context was current or if any * OpenGL-related errors occurred */ - public void updateSubImage(GL gl, TextureData data, int mipmapLevel, - int dstx, int dsty, - int srcx, int srcy, - int width, int height) throws GLException { + public void updateSubImage(final GL gl, final TextureData data, final int mipmapLevel, + final int dstx, final int dsty, + final int srcx, final int srcy, + final int width, final int height) throws GLException { if (data.isDataCompressed()) { throw new GLException("updateSubImage specifying a sub-rectangle is not supported for compressed TextureData"); } @@ -754,12 +819,12 @@ public class Texture { * texture's target. This gives control over parameters such as * GL_TEXTURE_MAX_ANISOTROPY_EXT. Causes this texture to be bound to * the current texture state. - * + * * @throws GLException if no OpenGL context was current or if any * OpenGL-related errors occurred */ - public void setTexParameterf(GL gl, int parameterName, - float value) { + public void setTexParameterf(final GL gl, final int parameterName, + final float value) { bind(gl); gl.glTexParameterf(target, parameterName, value); } @@ -768,11 +833,11 @@ public class Texture { * Sets the OpenGL multi-floating-point texture parameter for the * texture's target. Causes this texture to be bound to the current * texture state. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void setTexParameterfv(GL gl, int parameterName, - FloatBuffer params) { + public void setTexParameterfv(final GL gl, final int parameterName, + final FloatBuffer params) { bind(gl); gl.glTexParameterfv(target, parameterName, params); } @@ -781,11 +846,11 @@ public class Texture { * Sets the OpenGL multi-floating-point texture parameter for the * texture's target. Causes this texture to be bound to the current * texture state. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void setTexParameterfv(GL gl, int parameterName, - float[] params, int params_offset) { + public void setTexParameterfv(final GL gl, final int parameterName, + final float[] params, final int params_offset) { bind(gl); gl.glTexParameterfv(target, parameterName, params, params_offset); } @@ -797,11 +862,11 @@ public class Texture { * to GL_CLAMP_TO_EDGE if OpenGL 1.2 is supported on the current * platform and GL_CLAMP if not. Causes this texture to be bound to * the current texture state. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void setTexParameteri(GL gl, int parameterName, - int value) { + public void setTexParameteri(final GL gl, final int parameterName, + final int value) { bind(gl); gl.glTexParameteri(target, parameterName, value); } @@ -810,11 +875,11 @@ public class Texture { * Sets the OpenGL multi-integer texture parameter for the texture's * target. Causes this texture to be bound to the current texture * state. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void setTexParameteriv(GL gl, int parameterName, - IntBuffer params) { + public void setTexParameteriv(final GL gl, final int parameterName, + final IntBuffer params) { bind(gl); gl.glTexParameteriv(target, parameterName, params); } @@ -823,25 +888,44 @@ public class Texture { * Sets the OpenGL multi-integer texture parameter for the texture's * target. Causes this texture to be bound to the current texture * state. - * + * * @throws GLException if any OpenGL-related errors occurred */ - public void setTexParameteriv(GL gl, int parameterName, - int[] params, int params_offset) { + public void setTexParameteriv(final GL gl, final int parameterName, + final int[] params, final int params_offset) { bind(gl); gl.glTexParameteriv(target, parameterName, params, params_offset); } /** - * Returns the underlying OpenGL texture object for this texture. + * Returns the underlying OpenGL texture object for this texture + * and generates it if not done yet. + * <p> * Most applications will not need to access this, since it is * handled automatically by the bind(GL) and destroy(GL) APIs. + * </p> + * @param gl required to be valid and current in case the texture object has not been generated yet, + * otherwise it may be <code>null</code>. + * @see #getTextureObject() */ - public int getTextureObject(GL gl) { + public int getTextureObject(final GL gl) { validateTexID(gl, false); return texID; } + /** + * Returns the underlying OpenGL texture object for this texture, + * maybe <code>0</code> if not yet generated. + * <p> + * Most applications will not need to access this, since it is + * handled automatically by the bind(GL) and destroy(GL) APIs. + * </p> + * @see #getTextureObject(GL) + */ + public int getTextureObject() { + return texID; + } + /** Returns an estimate of the amount of texture memory in bytes this Texture consumes. It should only be treated as an estimate; most applications should not need to query this but instead let @@ -873,7 +957,7 @@ public class Texture { * * @return true if the given value is a power of two, false otherwise */ - private static boolean isPowerOfTwo(int val) { + private static boolean isPowerOfTwo(final int val) { return ((val & (val - 1)) == 0); } @@ -885,7 +969,7 @@ public class Texture { * @param val the value * @return the next power of two */ - private static int nextPowerOfTwo(int val) { + private static int nextPowerOfTwo(final int val) { int ret = 1; while (ret < val) { ret <<= 1; @@ -897,9 +981,12 @@ public class Texture { * Updates the actual image dimensions; usually only called from * <code>updateImage</code>. */ - private void setImageSize(int width, int height, int target) { + private void setImageSize(final int width, final int height, final int target) { imgWidth = width; imgHeight = height; + updateTexCoords(); + } + private void updateTexCoords() { if (target == GL2.GL_TEXTURE_RECTANGLE_ARB) { if (mustFlipVertically) { coords = new TextureCoords(0, imgHeight, imgWidth, 0); @@ -908,13 +995,13 @@ public class Texture { } } else { if (mustFlipVertically) { - coords = new TextureCoords(0, // l + coords = new TextureCoords(0, // l (float) imgHeight / (float) texHeight, // b (float) imgWidth / (float) texWidth, // r 0 // t ); } else { - coords = new TextureCoords(0, // l + coords = new TextureCoords(0, // l 0, // b (float) imgWidth / (float) texWidth, // r (float) imgHeight / (float) texHeight // t @@ -923,7 +1010,7 @@ public class Texture { } } - private void updateSubImageImpl(GL gl, TextureData data, int newTarget, int mipmapLevel, + private void updateSubImageImpl(final GL gl, final TextureData data, final int newTarget, final int mipmapLevel, int dstx, int dsty, int srcx, int srcy, int width, int height) throws GLException { data.setHaveEXTABGR(gl.isExtensionAvailable(GLExtensions.EXT_abgr)); @@ -994,15 +1081,15 @@ public class Texture { data.getInternalFormat(), buffer.remaining(), buffer); } else { - int[] align = { 0 }; - int[] rowLength = { 0 }; - int[] skipRows = { 0 }; - int[] skipPixels = { 0 }; + final int[] align = { 0 }; + final int[] rowLength = { 0 }; + final int[] skipRows = { 0 }; + final int[] skipPixels = { 0 }; gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment if(gl.isGL2GL3()) { - gl.glGetIntegerv(GL2GL3.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length - gl.glGetIntegerv(GL2GL3.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows - gl.glGetIntegerv(GL2GL3.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels + gl.glGetIntegerv(GL2ES2.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels } gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); if (DEBUG && VERBOSE) { @@ -1015,9 +1102,9 @@ public class Texture { System.out.println("height = " + height); } if(gl.isGL2GL3()) { - gl.glPixelStorei(GL2GL3.GL_UNPACK_ROW_LENGTH, rowlen); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_ROWS, srcy); - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_PIXELS, srcx); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, rowlen); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, srcy); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, srcx); } else { if ( rowlen!=0 && rowlen!=width && srcy!=0 && srcx!=0 ) { @@ -1031,14 +1118,14 @@ public class Texture { buffer); gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment if(gl.isGL2GL3()) { - gl.glPixelStorei(GL2GL3.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows - gl.glPixelStorei(GL2GL3.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels } } } - private void checkCompressedTextureExtensions(GL gl, TextureData data) { + private void checkCompressedTextureExtensions(final GL gl, final TextureData data) { if (data.isDataCompressed()) { switch (data.getInternalFormat()) { case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: @@ -1058,10 +1145,10 @@ public class Texture { } } - private boolean validateTexID(GL gl, boolean throwException) { + private boolean validateTexID(final GL gl, final boolean throwException) { if( 0 == texID ) { if( null != gl ) { - int[] tmp = new int[1]; + final int[] tmp = new int[1]; gl.glGenTextures(1, tmp, 0); texID = tmp[0]; if ( 0 == texID && throwException ) { @@ -1075,22 +1162,22 @@ public class Texture { } // Helper routines for disabling certain codepaths - private static boolean haveNPOT(GL gl) { + private static boolean haveNPOT(final GL gl) { return !disableNPOT && gl.isNPOTTextureAvailable(); } - private static boolean haveTexRect(GL gl) { + private static boolean haveTexRect(final GL gl) { return (!disableTexRect && TextureIO.isTexRectEnabled() && gl.isExtensionAvailable(GLExtensions.ARB_texture_rectangle)); } - private static boolean preferTexRect(GL gl) { + private static boolean preferTexRect(final GL gl) { // Prefer GL_ARB_texture_rectangle on ATI hardware on Mac OS X // due to software fallbacks if (NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { - String vendor = gl.glGetString(GL.GL_VENDOR); + final String vendor = gl.glGetString(GL.GL_VENDOR); if (vendor != null && vendor.startsWith("ATI")) { return true; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureCoords.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureCoords.java index 39647039b..17fcc7016 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureCoords.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureCoords.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2012 JogAmp Community. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -47,14 +48,14 @@ package com.jogamp.opengl.util.texture; public class TextureCoords { // These represent the lower-left point - private float left; - private float bottom; + private final float left; + private final float bottom; // These represent the upper-right point - private float right; - private float top; + private final float right; + private final float top; - public TextureCoords(float left, float bottom, - float right, float top) { + public TextureCoords(final float left, final float bottom, + final float right, final float top) { this.left = left; this.bottom = bottom; this.right = right; @@ -69,14 +70,14 @@ public class TextureCoords { * right top * </pre> */ - public float[] getST_LB_RB_LT_RT(float[] d, int d_off, float ss, float ts) { + public float[] getST_LB_RB_LT_RT(final float[] d, final int d_off, final float ss, final float ts) { d[0+d_off] = left *ss; d[1+d_off] = bottom*ts; d[2+d_off] = right *ss; d[3+d_off] = bottom*ts; d[4+d_off] = left *ss; d[5+d_off] = top *ts; d[6+d_off] = right *ss; d[7+d_off] = top *ts; return d; } - + /** Returns the leftmost (x) texture coordinate of this rectangle. */ public float left() { return left; } @@ -92,6 +93,7 @@ public class TextureCoords { /** Returns the topmost (y) texture coordinate of this rectangle. */ public float top() { return top; } - + + @Override public String toString() { return "TexCoord[h: "+left+" - "+right+", v: "+bottom+" - "+top+"]"; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java index 96ee233fd..5b2e4fc00 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureData.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -40,7 +41,9 @@ import java.nio.Buffer; import javax.media.opengl.GLProfile; +import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.util.GLBuffers; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; /** * Represents the data for an OpenGL texture. This is separated from @@ -54,11 +57,13 @@ import com.jogamp.opengl.util.GLBuffers; */ public class TextureData { + /** ColorSpace of pixel data. */ + public static enum ColorSpace { RGB, YCbCr, YCCK, CMYK }; + protected int width; protected int height; private int border; - protected int pixelFormat; - protected int pixelType; + protected GLPixelAttributes pixelAttributes; protected int internalFormat; // perhaps inferred from pixelFormat? protected boolean mipmap; // indicates whether mipmaps should be generated // (ignored if mipmaps are supplied from the file) @@ -77,8 +82,9 @@ public class TextureData { protected boolean haveEXTABGR; protected boolean haveGL12; protected GLProfile glProfile; + protected ColorSpace pixelCS = ColorSpace.RGB; - /** + /** * Constructs a new TextureData object with the specified parameters * and data contained in the given Buffer. The optional Flusher can * be used to clean up native resources associated with this @@ -118,18 +124,72 @@ public class TextureData { * data were invalid, such as requesting mipmap generation for a * compressed texture */ - public TextureData(GLProfile glp, - int internalFormat, - int width, - int height, - int border, - int pixelFormat, - int pixelType, - boolean mipmap, - boolean dataIsCompressed, - boolean mustFlipVertically, - Buffer buffer, - Flusher flusher) throws IllegalArgumentException { + public TextureData(final GLProfile glp, + final int internalFormat, + final int width, + final int height, + final int border, + final int pixelFormat, + final int pixelType, + final boolean mipmap, + final boolean dataIsCompressed, + final boolean mustFlipVertically, + final Buffer buffer, + final Flusher flusher) throws IllegalArgumentException { + this(glp, internalFormat, width, height, border, new GLPixelAttributes(pixelFormat, pixelType), + mipmap, dataIsCompressed, mustFlipVertically, buffer, flusher); + } + + /** + * Constructs a new TextureData object with the specified parameters + * and data contained in the given Buffer. The optional Flusher can + * be used to clean up native resources associated with this + * TextureData when processing is complete; for example, closing of + * memory-mapped files that might otherwise require a garbage + * collection to reclaim and close. + * + * @param glp the OpenGL Profile this texture data should be + * created for. + * @param internalFormat the OpenGL internal format for the + * resulting texture; must be specified, may + * not be 0 + * @param width the width in pixels of the texture + * @param height the height in pixels of the texture + * @param border the number of pixels of border this texture + * data has (0 or 1) + * @param pixelAttributes the OpenGL pixel format and type for the + * resulting texture; must be specified, may + * not be 0 + * @param mipmap indicates whether mipmaps should be + * autogenerated (using GLU) for the resulting + * texture. Currently if mipmap is true then + * dataIsCompressed may not be true. + * @param dataIsCompressed indicates whether the texture data is in + * compressed form + * (e.g. GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + * @param mustFlipVertically indicates whether the texture + * coordinates must be flipped vertically + * in order to properly display the + * texture + * @param buffer the buffer containing the texture data + * @param flusher optional flusher to perform cleanup tasks + * upon call to flush() + * + * @throws IllegalArgumentException if any parameters of the texture + * data were invalid, such as requesting mipmap generation for a + * compressed texture + */ + public TextureData(final GLProfile glp, + final int internalFormat, + final int width, + final int height, + final int border, + final GLPixelAttributes pixelAttributes, + final boolean mipmap, + final boolean dataIsCompressed, + final boolean mustFlipVertically, + final Buffer buffer, + final Flusher flusher) throws IllegalArgumentException { if (mipmap && dataIsCompressed) { throw new IllegalArgumentException("Can not generate mipmaps for compressed textures"); } @@ -138,8 +198,7 @@ public class TextureData { this.width = width; this.height = height; this.border = border; - this.pixelFormat = pixelFormat; - this.pixelType = pixelType; + this.pixelAttributes = pixelAttributes; this.internalFormat = internalFormat; this.mipmap = mipmap; this.dataIsCompressed = dataIsCompressed; @@ -150,7 +209,7 @@ public class TextureData { estimatedMemorySize = estimatedMemorySize(buffer); } - /** + /** * Constructs a new TextureData object with the specified parameters * and data for multiple mipmap levels contained in the given array * of Buffers. The optional Flusher can be used to clean up native @@ -189,27 +248,78 @@ public class TextureData { * data were invalid, such as requesting mipmap generation for a * compressed texture */ - public TextureData(GLProfile glp, - int internalFormat, - int width, - int height, - int border, - int pixelFormat, - int pixelType, - boolean dataIsCompressed, - boolean mustFlipVertically, - Buffer[] mipmapData, - Flusher flusher) throws IllegalArgumentException { + public TextureData(final GLProfile glp, + final int internalFormat, + final int width, + final int height, + final int border, + final int pixelFormat, + final int pixelType, + final boolean dataIsCompressed, + final boolean mustFlipVertically, + final Buffer[] mipmapData, + final Flusher flusher) throws IllegalArgumentException { + this(glp, internalFormat, width, height, border, new GLPixelAttributes(pixelFormat, pixelType), + dataIsCompressed, mustFlipVertically, mipmapData, flusher); + } + + /** + * Constructs a new TextureData object with the specified parameters + * and data for multiple mipmap levels contained in the given array + * of Buffers. The optional Flusher can be used to clean up native + * resources associated with this TextureData when processing is + * complete; for example, closing of memory-mapped files that might + * otherwise require a garbage collection to reclaim and close. + * + * @param glp the OpenGL Profile this texture data should be + * created for. + * @param internalFormat the OpenGL internal format for the + * resulting texture; must be specified, may + * not be 0 + * @param width the width in pixels of the topmost mipmap + * level of the texture + * @param height the height in pixels of the topmost mipmap + * level of the texture + * @param border the number of pixels of border this texture + * data has (0 or 1) + * @param pixelAttributes the OpenGL pixel format and type for the + * resulting texture; must be specified, may + * not be 0 + * @param dataIsCompressed indicates whether the texture data is in + * compressed form + * (e.g. GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + * @param mustFlipVertically indicates whether the texture + * coordinates must be flipped vertically + * in order to properly display the + * texture + * @param mipmapData the buffers containing all mipmap levels + * of the texture's data + * @param flusher optional flusher to perform cleanup tasks + * upon call to flush() + * + * @throws IllegalArgumentException if any parameters of the texture + * data were invalid, such as requesting mipmap generation for a + * compressed texture + */ + public TextureData(final GLProfile glp, + final int internalFormat, + final int width, + final int height, + final int border, + final GLPixelAttributes pixelAttributes, + final boolean dataIsCompressed, + final boolean mustFlipVertically, + final Buffer[] mipmapData, + final Flusher flusher) throws IllegalArgumentException { this.glProfile = glp; this.width = width; this.height = height; this.border = border; - this.pixelFormat = pixelFormat; - this.pixelType = pixelType; + this.pixelAttributes = pixelAttributes; this.internalFormat = internalFormat; this.dataIsCompressed = dataIsCompressed; this.mustFlipVertically = mustFlipVertically; - this.mipmapData = (Buffer[]) mipmapData.clone(); + this.mipmapData = mipmapData.clone(); this.flusher = flusher; alignment = 1; // FIXME: is this correct enough in all situations? for (int i = 0; i < mipmapData.length; i++) { @@ -217,41 +327,57 @@ public class TextureData { } } + /** + * Returns the color space of the pixel data. + * @see #setColorSpace(ColorSpace) + */ + public ColorSpace getColorSpace() { return pixelCS; } + + /** + * Set the color space of the pixel data, which defaults to {@link ColorSpace#RGB}. + * @see #getColorSpace() + */ + public void setColorSpace(final ColorSpace cs) { pixelCS = cs; } + /** Used only by subclasses */ - protected TextureData(GLProfile glp) { this.glProfile = glp; } + protected TextureData(final GLProfile glp) { this.glProfile = glp; this.pixelAttributes = GLPixelAttributes.UNDEF; } /** Returns the width in pixels of the texture data. */ public int getWidth() { return width; } /** Returns the height in pixels of the texture data. */ public int getHeight() { return height; } /** Returns the border in pixels of the texture data. */ - public int getBorder() { - return border; + public int getBorder() { + return border; + } + /** Returns the intended OpenGL {@link GLPixelAttributes} of the texture data, i.e. format and type. */ + public GLPixelAttributes getPixelAttributes() { + return pixelAttributes; } - /** Returns the intended OpenGL pixel format of the texture data. */ + /** Returns the intended OpenGL pixel format of the texture data using {@link #getPixelAttributes()}. */ public int getPixelFormat() { - return pixelFormat; + return pixelAttributes.format; } - /** Returns the intended OpenGL pixel type of the texture data. */ + /** Returns the intended OpenGL pixel type of the texture data using {@link #getPixelAttributes()}. */ public int getPixelType() { - return pixelType; + return pixelAttributes.type; } /** Returns the intended OpenGL internal format of the texture data. */ - public int getInternalFormat() { - return internalFormat; + public int getInternalFormat() { + return internalFormat; } /** Returns whether mipmaps should be generated for the texture data. */ - public boolean getMipmap() { - return mipmap; + public boolean getMipmap() { + return mipmap; } /** Indicates whether the texture data is in compressed form. */ - public boolean isDataCompressed() { - return dataIsCompressed; + public boolean isDataCompressed() { + return dataIsCompressed; } /** Indicates whether the texture coordinates must be flipped vertically for proper display. */ - public boolean getMustFlipVertically() { - return mustFlipVertically; + public boolean getMustFlipVertically() { + return mustFlipVertically; } /** Returns the texture data, or null if it is specified as a set of mipmaps. */ public Buffer getBuffer() { @@ -259,61 +385,81 @@ public class TextureData { } /** Returns all mipmap levels for the texture data, or null if it is specified as a single image. */ - public Buffer[] getMipmapData() { - return mipmapData; + public Buffer[] getMipmapData() { + return mipmapData; } /** Returns the required byte alignment for the texture data. */ - public int getAlignment() { - return alignment; + public int getAlignment() { + return alignment; } /** Returns the row length needed for correct GL_UNPACK_ROW_LENGTH specification. This is currently only supported for non-mipmapped, non-compressed textures. */ - public int getRowLength() { - return rowLength; + public int getRowLength() { + return rowLength; } /** Sets the width in pixels of the texture data. */ - public void setWidth(int width) { this.width = width; } + public void setWidth(final int width) { this.width = width; } /** Sets the height in pixels of the texture data. */ - public void setHeight(int height) { this.height = height; } + public void setHeight(final int height) { this.height = height; } /** Sets the border in pixels of the texture data. */ - public void setBorder(int border) { this.border = border; } + public void setBorder(final int border) { this.border = border; } /** Sets the intended OpenGL pixel format of the texture data. */ - public void setPixelFormat(int pixelFormat) { this.pixelFormat = pixelFormat; } - /** Sets the intended OpenGL pixel type of the texture data. */ - public void setPixelType(int pixelType) { this.pixelType = pixelType; } + public void setPixelAttributes(final GLPixelAttributes pixelAttributes) { this.pixelAttributes = pixelAttributes; } + /** + * Sets the intended OpenGL pixel format component of {@link GLPixelAttributes} of the texture data. + * <p> + * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type. + * </p> + */ + public void setPixelFormat(final int pixelFormat) { + if( pixelAttributes.format != pixelFormat ) { + pixelAttributes = new GLPixelAttributes(pixelFormat, pixelAttributes.type); + } + } + /** + * Sets the intended OpenGL pixel type component of {@link GLPixelAttributes} of the texture data. + * <p> + * Use {@link #setPixelAttributes(GLPixelAttributes)}, if setting format and type. + * </p> + */ + public void setPixelType(final int pixelType) { + if( pixelAttributes.type != pixelType) { + pixelAttributes = new GLPixelAttributes(pixelAttributes.format, pixelType); + } + } /** Sets the intended OpenGL internal format of the texture data. */ - public void setInternalFormat(int internalFormat) { this.internalFormat = internalFormat; } + public void setInternalFormat(final int internalFormat) { this.internalFormat = internalFormat; } /** Sets whether mipmaps should be generated for the texture data. */ - public void setMipmap(boolean mipmap) { this.mipmap = mipmap; } + public void setMipmap(final boolean mipmap) { this.mipmap = mipmap; } /** Sets whether the texture data is in compressed form. */ - public void setIsDataCompressed(boolean compressed) { this.dataIsCompressed = compressed; } + public void setIsDataCompressed(final boolean compressed) { this.dataIsCompressed = compressed; } /** Sets whether the texture coordinates must be flipped vertically for proper display. */ - public void setMustFlipVertically(boolean mustFlipVertically) { this.mustFlipVertically = mustFlipVertically; } + public void setMustFlipVertically(final boolean mustFlipVertically) { this.mustFlipVertically = mustFlipVertically; } /** Sets the texture data. */ - public void setBuffer(Buffer buffer) { + public void setBuffer(final Buffer buffer) { this.buffer = buffer; estimatedMemorySize = estimatedMemorySize(buffer); } /** Sets the required byte alignment for the texture data. */ - public void setAlignment(int alignment) { this.alignment = alignment; } + public void setAlignment(final int alignment) { this.alignment = alignment; } /** Sets the row length needed for correct GL_UNPACK_ROW_LENGTH specification. This is currently only supported for non-mipmapped, non-compressed textures. */ - public void setRowLength(int rowLength) { this.rowLength = rowLength; } + public void setRowLength(final int rowLength) { this.rowLength = rowLength; } /** Indicates to this TextureData whether the GL_EXT_abgr extension is available. Used for optimization along some code paths to avoid data copies. */ - public void setHaveEXTABGR(boolean haveEXTABGR) { + public void setHaveEXTABGR(final boolean haveEXTABGR) { this.haveEXTABGR = haveEXTABGR; } /** Indicates to this TextureData whether OpenGL version 1.2 is available. If not, falls back to relatively inefficient code paths for several input data types (several kinds of packed pixel formats, in particular). */ - public void setHaveGL12(boolean haveGL12) { + public void setHaveGL12(final boolean haveGL12) { this.haveGL12 = haveGL12; } @@ -354,18 +500,20 @@ public class TextureData { public void flush(); } + @Override public String toString() { - return "TextureData["+width+"x"+height+", y-flip "+mustFlipVertically+", internFormat 0x"+Integer.toHexString(internalFormat)+", pixelFormat 0x"+Integer.toHexString(pixelFormat)+", pixelType 0x"+Integer.toHexString(pixelType)+", border "+border+", estSize "+estimatedMemorySize+", alignment "+alignment+", rowlen "+rowLength; + return "TextureData["+width+"x"+height+", y-flip "+mustFlipVertically+", internFormat 0x"+Integer.toHexString(internalFormat)+", "+ + pixelAttributes+", border "+border+", estSize "+estimatedMemorySize+", alignment "+alignment+", rowlen "+rowLength; } //---------------------------------------------------------------------- // Internals only below this point // - protected static int estimatedMemorySize(Buffer buffer) { + protected static int estimatedMemorySize(final Buffer buffer) { if (buffer == null) { return 0; } - return buffer.capacity() * GLBuffers.sizeOfBufferElem(buffer); + return buffer.capacity() * Buffers.sizeOfBufferElem(buffer); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java index b878c6002..6011afe7b 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2011 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,17 +41,23 @@ package com.jogamp.opengl.util.texture; import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.nio.Buffer; import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import javax.media.nativewindow.util.Dimension; +import javax.media.nativewindow.util.DimensionImmutable; +import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; import javax.media.opengl.GL2GL3; @@ -62,9 +68,12 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.GLPixelStorageModes; +import com.jogamp.opengl.util.PNGPixelRect; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.spi.DDSImage; +import com.jogamp.opengl.util.texture.spi.JPEGImage; import com.jogamp.opengl.util.texture.spi.NetPbmTextureWriter; -import com.jogamp.opengl.util.texture.spi.PNGImage; import com.jogamp.opengl.util.texture.spi.SGIImage; import com.jogamp.opengl.util.texture.spi.TGAImage; import com.jogamp.opengl.util.texture.spi.TextureProvider; @@ -158,11 +167,11 @@ public class TextureIO { /** Constant which can be used as a file suffix to indicate a PAM file, NetPbm magic 7 - binary RGB and RGBA. Write support only. */ public static final String PAM = "pam"; - + /** Constant which can be used as a file suffix to indicate a PAM file, NetPbm magic 6 - binary RGB. Write support only. */ public static final String PPM = "ppm"; - + private static final boolean DEBUG = Debug.debug("TextureIO"); // For manually disabling the use of the texture rectangle @@ -201,8 +210,8 @@ public class TextureIO { * registered texture providers could read the file * @throws IOException if an error occurred while reading the file */ - public static TextureData newTextureData(GLProfile glp, File file, - boolean mipmap, + public static TextureData newTextureData(final GLProfile glp, final File file, + final boolean mipmap, String fileSuffix) throws IOException { if (fileSuffix == null) { fileSuffix = IOUtil.getFileSuffix(file); @@ -231,9 +240,9 @@ public class TextureIO { * registered texture providers could read the stream * @throws IOException if an error occurred while reading the stream */ - public static TextureData newTextureData(GLProfile glp, InputStream stream, - boolean mipmap, - String fileSuffix) throws IOException { + public static TextureData newTextureData(final GLProfile glp, final InputStream stream, + final boolean mipmap, + final String fileSuffix) throws IOException { return newTextureDataImpl(glp, stream, 0, 0, mipmap, fileSuffix); } @@ -258,8 +267,8 @@ public class TextureIO { * registered texture providers could read the URL * @throws IOException if an error occurred while reading the URL */ - public static TextureData newTextureData(GLProfile glp, URL url, - boolean mipmap, + public static TextureData newTextureData(final GLProfile glp, final URL url, + final boolean mipmap, String fileSuffix) throws IOException { if (fileSuffix == null) { fileSuffix = IOUtil.getFileSuffix(url.getPath()); @@ -306,10 +315,10 @@ public class TextureIO { * pixelFormat was 0 * @throws IOException if an error occurred while reading the file */ - public static TextureData newTextureData(GLProfile glp, File file, - int internalFormat, - int pixelFormat, - boolean mipmap, + public static TextureData newTextureData(final GLProfile glp, final File file, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, String fileSuffix) throws IOException, IllegalArgumentException { if ((internalFormat == 0) || (pixelFormat == 0)) { throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); @@ -354,11 +363,11 @@ public class TextureIO { * pixelFormat was 0 * @throws IOException if an error occurred while reading the stream */ - public static TextureData newTextureData(GLProfile glp, InputStream stream, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException, IllegalArgumentException { + public static TextureData newTextureData(final GLProfile glp, final InputStream stream, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException, IllegalArgumentException { if ((internalFormat == 0) || (pixelFormat == 0)) { throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); } @@ -398,10 +407,10 @@ public class TextureIO { * pixelFormat was 0 * @throws IOException if an error occurred while reading the URL */ - public static TextureData newTextureData(GLProfile glp, URL url, - int internalFormat, - int pixelFormat, - boolean mipmap, + public static TextureData newTextureData(final GLProfile glp, final URL url, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, String fileSuffix) throws IOException, IllegalArgumentException { if ((internalFormat == 0) || (pixelFormat == 0)) { throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); @@ -418,7 +427,7 @@ public class TextureIO { // methods that *do* require a current context // - /** + /** * Creates an OpenGL texture object from the specified TextureData * using the current OpenGL context. * @@ -427,11 +436,11 @@ public class TextureIO { * OpenGL error occurred * @throws IllegalArgumentException if the passed TextureData was null */ - public static Texture newTexture(TextureData data) throws GLException, IllegalArgumentException { + public static Texture newTexture(final TextureData data) throws GLException, IllegalArgumentException { return newTexture(GLContext.getCurrentGL(), data); } - /** + /** * Creates an OpenGL texture object from the specified TextureData * using the given OpenGL context. * @@ -440,14 +449,14 @@ public class TextureIO { * OpenGL error occurred * @throws IllegalArgumentException if the passed TextureData was null */ - public static Texture newTexture(GL gl, TextureData data) throws GLException, IllegalArgumentException { + public static Texture newTexture(final GL gl, final TextureData data) throws GLException, IllegalArgumentException { if (data == null) { throw new IllegalArgumentException("Null TextureData"); } return new Texture(gl, data); } - - /** + + /** * Creates an OpenGL texture object from the specified file using * the current OpenGL context. * @@ -462,16 +471,16 @@ public class TextureIO { * @throws GLException if no OpenGL context is current or if an * OpenGL error occurred */ - public static Texture newTexture(File file, boolean mipmap) throws IOException, GLException { - GL gl = GLContext.getCurrentGL(); - GLProfile glp = gl.getGLProfile(); - TextureData data = newTextureData(glp, file, mipmap, IOUtil.getFileSuffix(file)); - Texture texture = newTexture(gl, data); + public static Texture newTexture(final File file, final boolean mipmap) throws IOException, GLException { + final GL gl = GLContext.getCurrentGL(); + final GLProfile glp = gl.getGLProfile(); + final TextureData data = newTextureData(glp, file, mipmap, IOUtil.getFileSuffix(file)); + final Texture texture = newTexture(gl, data); data.flush(); return texture; } - /** + /** * Creates an OpenGL texture object from the specified stream using * the current OpenGL context. * @@ -491,16 +500,16 @@ public class TextureIO { * @throws GLException if no OpenGL context is current or if an * OpenGL error occurred */ - public static Texture newTexture(InputStream stream, boolean mipmap, String fileSuffix) throws IOException, GLException { - GL gl = GLContext.getCurrentGL(); - GLProfile glp = gl.getGLProfile(); - TextureData data = newTextureData(glp, stream, mipmap, fileSuffix); - Texture texture = newTexture(gl, data); + public static Texture newTexture(final InputStream stream, final boolean mipmap, final String fileSuffix) throws IOException, GLException { + final GL gl = GLContext.getCurrentGL(); + final GLProfile glp = gl.getGLProfile(); + final TextureData data = newTextureData(glp, stream, mipmap, fileSuffix); + final Texture texture = newTexture(gl, data); data.flush(); return texture; } - /** + /** * Creates an OpenGL texture object from the specified URL using the * current OpenGL context. * @@ -520,71 +529,32 @@ public class TextureIO { * @throws GLException if no OpenGL context is current or if an * OpenGL error occurred */ - public static Texture newTexture(URL url, boolean mipmap, String fileSuffix) throws IOException, GLException { + public static Texture newTexture(final URL url, final boolean mipmap, String fileSuffix) throws IOException, GLException { if (fileSuffix == null) { fileSuffix = IOUtil.getFileSuffix(url.getPath()); } - GL gl = GLContext.getCurrentGL(); - GLProfile glp = gl.getGLProfile(); - TextureData data = newTextureData(glp, url, mipmap, fileSuffix); - Texture texture = newTexture(gl, data); + final GL gl = GLContext.getCurrentGL(); + final GLProfile glp = gl.getGLProfile(); + final TextureData data = newTextureData(glp, url, mipmap, fileSuffix); + final Texture texture = newTexture(gl, data); data.flush(); return texture; } - /** + /** * Creates an OpenGL texture object associated with the given OpenGL * texture target. The texture has * no initial data. This is used, for example, to construct cube * maps out of multiple TextureData objects. * - * @param target the OpenGL target type, eg GL.GL_TEXTURE_2D, + * @param target the OpenGL target type, eg GL.GL_TEXTURE_2D, * GL.GL_TEXTURE_RECTANGLE_ARB */ - public static Texture newTexture(int target) { + public static Texture newTexture(final int target) { return new Texture(target); } /** - * Wraps an OpenGL texture ID from an external library and allows - * some of the base methods from the Texture class, such as - * binding and querying of texture coordinates, to be used with - * it. Attempts to update such textures' contents will yield - * undefined results. - * - * @param textureID the OpenGL texture object to wrap - * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D, - * GL2.GL_TEXTURE_RECTANGLE - * @param texWidth the width of the texture in pixels - * @param texHeight the height of the texture in pixels - * @param imgWidth the width of the image within the texture in - * pixels (if the content is a sub-rectangle in the upper - * left corner); otherwise, pass in texWidth - * @param imgHeight the height of the image within the texture in - * pixels (if the content is a sub-rectangle in the upper - * left corner); otherwise, pass in texHeight - * @param mustFlipVertically indicates whether the texture - * coordinates must be flipped vertically - * in order to properly display the - * texture - */ - public static Texture newTexture(int textureID, - int target, - int texWidth, - int texHeight, - int imgWidth, - int imgHeight, - boolean mustFlipVertically) { - return new Texture(textureID, - target, - texWidth, - texHeight, - imgWidth, - imgHeight, - mustFlipVertically); - } - - /** * Writes the given texture to a file. The type of the file is * inferred from its suffix. An OpenGL context must be current in * order to fetch the texture data back from the OpenGL pipeline. @@ -605,36 +575,40 @@ public class TextureIO { * when it is written to disk, regardless of whether the underlying * file format supports multiple mipmaps in a given file. * + * <p> + * Method required a {@link GL2GL3} {@link GLProfile#GL2GL3 profile}. + * </p> + * * @throws IOException if an error occurred during writing or no * suitable writer was found * @throws GLException if no OpenGL context was current or an * OpenGL-related error occurred */ - public static void write(Texture texture, File file) throws IOException, GLException { + public static void write(final Texture texture, final File file) throws IOException, GLException { if (texture.getTarget() != GL.GL_TEXTURE_2D) { throw new GLException("Only GL_TEXTURE_2D textures are supported"); } // First fetch the texture data - GL _gl = GLContext.getCurrentGL(); + final GL _gl = GLContext.getCurrentGL(); if (!_gl.isGL2GL3()) { throw new GLException("Implementation only supports GL2GL3 (Use GLReadBufferUtil and the TextureData variant), have: " + _gl); } - GL2GL3 gl = _gl.getGL2(); + final GL2GL3 gl = _gl.getGL2GL3(); texture.bind(gl); - int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_INTERNAL_FORMAT); - int width = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_WIDTH); - int height = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_HEIGHT); - int border = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_BORDER); + final int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_INTERNAL_FORMAT); + final int width = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_WIDTH); + final int height = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_HEIGHT); + final int border = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_BORDER); TextureData data = null; if (internalFormat == GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT || internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { // Fetch using glGetCompressedTexImage - int size = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_COMPRESSED_IMAGE_SIZE); - ByteBuffer res = ByteBuffer.allocate(size); + final int size = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_COMPRESSED_IMAGE_SIZE); + final ByteBuffer res = ByteBuffer.allocate(size); gl.glGetCompressedTexImage(GL.GL_TEXTURE_2D, 0, res); data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, internalFormat, GL.GL_UNSIGNED_BYTE, false, true, true, res, null); @@ -643,7 +617,7 @@ public class TextureIO { int fetchedFormat = 0; switch (internalFormat) { case GL.GL_RGB: - case GL2.GL_BGR: + case GL2GL3.GL_BGR: case GL.GL_RGB8: bytesPerPixel = 3; fetchedFormat = GL.GL_RGB; @@ -660,19 +634,10 @@ public class TextureIO { } // Fetch using glGetTexImage - int packAlignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT); - int packRowLength = glGetInteger(gl, GL2.GL_PACK_ROW_LENGTH); - int packSkipRows = glGetInteger(gl, GL2.GL_PACK_SKIP_ROWS); - int packSkipPixels = glGetInteger(gl, GL2.GL_PACK_SKIP_PIXELS); - int packSwapBytes = glGetInteger(gl, GL2.GL_PACK_SWAP_BYTES); - - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, 0); - - ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) * + final GLPixelStorageModes psm = new GLPixelStorageModes(); + psm.setPackAlignment(gl, 1); + + final ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) * (height + (2 * border)) * bytesPerPixel); if (DEBUG) { @@ -681,12 +646,8 @@ public class TextureIO { } gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, packRowLength); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, packSkipRows); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, packSkipPixels); - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, packSwapBytes); - + psm.restore(gl); + data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE, false, false, false, res, null); @@ -698,10 +659,10 @@ public class TextureIO { write(data, file); } - - public static void write(TextureData data, File file) throws IOException, GLException { - for (Iterator<TextureWriter> iter = textureWriters.iterator(); iter.hasNext(); ) { - TextureWriter writer = iter.next(); + + public static void write(final TextureData data, final File file) throws IOException, GLException { + for (final Iterator<TextureWriter> iter = textureWriters.iterator(); iter.hasNext(); ) { + final TextureWriter writer = iter.next(); if (writer.write(file, data)) { return; } @@ -709,23 +670,31 @@ public class TextureIO { throw new IOException("No suitable texture writer found for "+file.getAbsolutePath()); } - + //---------------------------------------------------------------------- // SPI support // - /** Adds a TextureProvider to support reading of a new file - format. */ - public static void addTextureProvider(TextureProvider provider) { + /** + * Adds a TextureProvider to support reading of a new file format. + * <p> + * The last provider added, will be the first provider to be tested. + * </p> + */ + public static void addTextureProvider(final TextureProvider provider) { // Must always add at the front so the ImageIO provider is last, // so we don't accidentally use it instead of a user's possibly // more optimal provider textureProviders.add(0, provider); } - /** Adds a TextureWriter to support writing of a new file - format. */ - public static void addTextureWriter(TextureWriter writer) { + /** + * Adds a TextureWriter to support writing of a new file format. + * <p> + * The last provider added, will be the first provider to be tested. + * </p> + */ + public static void addTextureWriter(final TextureWriter writer) { // Must always add at the front so the ImageIO writer is last, // so we don't accidentally use it instead of a user's possibly // more optimal writer @@ -750,7 +719,7 @@ public class TextureIO { the GL_ARB_texture_rectangle extension to be turned off globally for this purpose. The default is that the use of the extension is enabled. */ - public static void setTexRectEnabled(boolean enabled) { + public static void setTexRectEnabled(final boolean enabled) { texRectEnabled = enabled; } @@ -773,10 +742,10 @@ public class TextureIO { if(GLProfile.isAWTAvailable()) { try { // Use reflection to avoid compile-time dependencies on AWT-related classes - TextureProvider provider = (TextureProvider) + final TextureProvider provider = (TextureProvider) Class.forName("com.jogamp.opengl.util.texture.spi.awt.IIOTextureProvider").newInstance(); addTextureProvider(provider); - } catch (Exception e) { + } catch (final Exception e) { if (DEBUG) { e.printStackTrace(); } @@ -787,20 +756,21 @@ public class TextureIO { addTextureProvider(new DDSTextureProvider()); addTextureProvider(new SGITextureProvider()); addTextureProvider(new TGATextureProvider()); + addTextureProvider(new JPGTextureProvider()); addTextureProvider(new PNGTextureProvider()); // ImageIO writer, the fall-back, must be the first one added if(GLProfile.isAWTAvailable()) { try { // Use reflection to avoid compile-time dependencies on AWT-related classes - TextureWriter writer = (TextureWriter) + final TextureWriter writer = (TextureWriter) Class.forName("com.jogamp.opengl.util.texture.spi.awt.IIOTextureWriter").newInstance(); addTextureWriter(writer); - } catch (Exception e) { + } catch (final Exception e) { if (DEBUG) { e.printStackTrace(); } - } catch (Error e) { + } catch (final Error e) { if (DEBUG) { e.printStackTrace(); } @@ -816,10 +786,10 @@ public class TextureIO { } // Implementation methods - private static TextureData newTextureDataImpl(GLProfile glp, File file, - int internalFormat, - int pixelFormat, - boolean mipmap, + private static TextureData newTextureDataImpl(final GLProfile glp, final File file, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, String fileSuffix) throws IOException { if (file == null) { throw new IOException("File was null"); @@ -827,9 +797,9 @@ public class TextureIO { fileSuffix = toLowerCase(fileSuffix); - for (Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { - TextureProvider provider = iter.next(); - TextureData data = provider.newTextureData(glp, file, + for (final Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { + final TextureProvider provider = iter.next(); + final TextureData data = provider.newTextureData(glp, file, internalFormat, pixelFormat, mipmap, @@ -842,10 +812,10 @@ public class TextureIO { throw new IOException("No suitable reader for given file "+file.getAbsolutePath()); } - private static TextureData newTextureDataImpl(GLProfile glp, InputStream stream, - int internalFormat, - int pixelFormat, - boolean mipmap, + private static TextureData newTextureDataImpl(final GLProfile glp, InputStream stream, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, String fileSuffix) throws IOException { if (stream == null) { throw new IOException("Stream was null"); @@ -858,9 +828,9 @@ public class TextureIO { stream = new BufferedInputStream(stream); } - for (Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { - TextureProvider provider = iter.next(); - TextureData data = provider.newTextureData(glp, stream, + for (final Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { + final TextureProvider provider = iter.next(); + final TextureData data = provider.newTextureData(glp, stream, internalFormat, pixelFormat, mipmap, @@ -873,10 +843,10 @@ public class TextureIO { throw new IOException("No suitable reader for given stream"); } - private static TextureData newTextureDataImpl(GLProfile glp, URL url, - int internalFormat, - int pixelFormat, - boolean mipmap, + private static TextureData newTextureDataImpl(final GLProfile glp, final URL url, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, String fileSuffix) throws IOException { if (url == null) { throw new IOException("URL was null"); @@ -884,9 +854,9 @@ public class TextureIO { fileSuffix = toLowerCase(fileSuffix); - for (Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { - TextureProvider provider = iter.next(); - TextureData data = provider.newTextureData(glp, url, + for (final Iterator<TextureProvider> iter = textureProviders.iterator(); iter.hasNext(); ) { + final TextureProvider provider = iter.next(); + final TextureData data = provider.newTextureData(glp, url, internalFormat, pixelFormat, mipmap, @@ -902,42 +872,45 @@ public class TextureIO { //---------------------------------------------------------------------- // DDS provider -- supports files only for now static class DDSTextureProvider implements TextureProvider { - public TextureData newTextureData(GLProfile glp, File file, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { + @Override + public TextureData newTextureData(final GLProfile glp, final File file, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { if (DDS.equals(fileSuffix) || DDS.equals(IOUtil.getFileSuffix(file))) { - DDSImage image = DDSImage.read(file); + final DDSImage image = DDSImage.read(file); return newTextureData(glp, image, internalFormat, pixelFormat, mipmap); } return null; } - public TextureData newTextureData(GLProfile glp, InputStream stream, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { if (DDS.equals(fileSuffix) || DDSImage.isDDSImage(stream)) { - byte[] data = IOUtil.copyStream2ByteArray(stream); - ByteBuffer buf = ByteBuffer.wrap(data); - DDSImage image = DDSImage.read(buf); + final byte[] data = IOUtil.copyStream2ByteArray(stream); + final ByteBuffer buf = ByteBuffer.wrap(data); + final DDSImage image = DDSImage.read(buf); return newTextureData(glp, image, internalFormat, pixelFormat, mipmap); } return null; } - public TextureData newTextureData(GLProfile glp, URL url, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - InputStream stream = new BufferedInputStream(url.openStream()); + @Override + public TextureData newTextureData(final GLProfile glp, final URL url, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final InputStream stream = new BufferedInputStream(url.openStream()); try { return newTextureData(glp, stream, internalFormat, pixelFormat, mipmap, fileSuffix); } finally { @@ -945,11 +918,11 @@ public class TextureIO { } } - private TextureData newTextureData(GLProfile glp, final DDSImage image, + private TextureData newTextureData(final GLProfile glp, final DDSImage image, int internalFormat, int pixelFormat, boolean mipmap) { - DDSImage.ImageInfo info = image.getMipMap(0); + final DDSImage.ImageInfo info = image.getMipMap(0); if (pixelFormat == 0) { switch (image.getPixelFormat()) { case DDSImage.D3DFMT_R8G8B8: @@ -986,14 +959,15 @@ public class TextureIO { break; } } - TextureData.Flusher flusher = new TextureData.Flusher() { + final TextureData.Flusher flusher = new TextureData.Flusher() { + @Override public void flush() { image.close(); } }; TextureData data; if (mipmap && image.getNumMipMaps() > 0) { - Buffer[] mipmapData = new Buffer[image.getNumMipMaps()]; + final Buffer[] mipmapData = new Buffer[image.getNumMipMaps()]; for (int i = 0; i < image.getNumMipMaps(); i++) { mipmapData[i] = image.getMipMap(i).getData(); } @@ -1030,12 +1004,13 @@ public class TextureIO { //---------------------------------------------------------------------- // Base class for SGI RGB and TGA image providers static abstract class StreamBasedTextureProvider implements TextureProvider { - public TextureData newTextureData(GLProfile glp, File file, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - InputStream inStream = new BufferedInputStream(new FileInputStream(file)); + @Override + public TextureData newTextureData(final GLProfile glp, final File file, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final InputStream inStream = new BufferedInputStream(new FileInputStream(file)); try { // The SGIImage and TGAImage implementations use InputStreams // anyway so there isn't much point in having a separate code @@ -1050,12 +1025,13 @@ public class TextureIO { } } - public TextureData newTextureData(GLProfile glp, URL url, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - InputStream stream = new BufferedInputStream(url.openStream()); + @Override + public TextureData newTextureData(final GLProfile glp, final URL url, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final InputStream stream = new BufferedInputStream(url.openStream()); try { return newTextureData(glp, stream, internalFormat, pixelFormat, mipmap, fileSuffix); } finally { @@ -1067,15 +1043,16 @@ public class TextureIO { //---------------------------------------------------------------------- // SGI RGB image provider static class SGITextureProvider extends StreamBasedTextureProvider { - public TextureData newTextureData(GLProfile glp, InputStream stream, + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, int internalFormat, int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { + final boolean mipmap, + final String fileSuffix) throws IOException { if (SGI.equals(fileSuffix) || SGI_RGB.equals(fileSuffix) || SGIImage.isSGIImage(stream)) { - SGIImage image = SGIImage.read(stream); + final SGIImage image = SGIImage.read(stream); if (pixelFormat == 0) { pixelFormat = image.getFormat(); } @@ -1102,18 +1079,19 @@ public class TextureIO { //---------------------------------------------------------------------- // TGA (Targa) image provider static class TGATextureProvider extends StreamBasedTextureProvider { - public TextureData newTextureData(GLProfile glp, InputStream stream, + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, int internalFormat, int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { + final boolean mipmap, + final String fileSuffix) throws IOException { if (TGA.equals(fileSuffix)) { - TGAImage image = TGAImage.read(glp, stream); + final TGAImage image = TGAImage.read(glp, stream); if (pixelFormat == 0) { pixelFormat = image.getGLFormat(); } if (internalFormat == 0) { - if(glp.isGL2GL3()) { + if(glp.isGL2ES3()) { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8; } else { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB; @@ -1139,18 +1117,59 @@ public class TextureIO { //---------------------------------------------------------------------- // PNG image provider static class PNGTextureProvider extends StreamBasedTextureProvider { - public TextureData newTextureData(GLProfile glp, InputStream stream, + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, int internalFormat, int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { + final boolean mipmap, + final String fileSuffix) throws IOException { if (PNG.equals(fileSuffix)) { - PNGImage image = PNGImage.read(/*glp, */ stream); + final PNGPixelRect image = PNGPixelRect.read(stream, null, true /* directBuffer */, 0 /* destMinStrideInBytes */, true /* destIsGLOriented */); + final GLPixelAttributes glpa = GLPixelAttributes.convert(image.getPixelformat(), glp); + if ( 0 == pixelFormat ) { + pixelFormat = glpa.format; + } // else FIXME: Actually not supported w/ preset pixelFormat! + if ( 0 == internalFormat ) { + final boolean hasAlpha = 4 == glpa.bytesPerPixel; + if(glp.isGL2ES3()) { + internalFormat = hasAlpha ? GL.GL_RGBA8 : GL.GL_RGB8; + } else { + internalFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; + } + } + return new TextureData(glp, internalFormat, + image.getSize().getWidth(), + image.getSize().getHeight(), + 0, + pixelFormat, + glpa.type, + mipmap, + false, + false, + image.getPixels(), + null); + } + + return null; + } + } + + //---------------------------------------------------------------------- + // JPEG image provider + static class JPGTextureProvider extends StreamBasedTextureProvider { + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, + int internalFormat, + int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + if (JPG.equals(fileSuffix)) { + final JPEGImage image = JPEGImage.read(/*glp, */ stream); if (pixelFormat == 0) { pixelFormat = image.getGLFormat(); } if (internalFormat == 0) { - if(glp.isGL2GL3()) { + if(glp.isGL2ES3()) { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8; } else { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB; @@ -1177,12 +1196,14 @@ public class TextureIO { // DDS texture writer // static class DDSTextureWriter implements TextureWriter { - public boolean write(File file, - TextureData data) throws IOException { + @Override + public boolean write(final File file, + final TextureData data) throws IOException { if (DDS.equals(IOUtil.getFileSuffix(file))) { // See whether the DDS writer can handle this TextureData - int pixelFormat = data.getPixelFormat(); - int pixelType = data.getPixelType(); + final GLPixelAttributes pixelAttribs = data.getPixelAttributes(); + final int pixelFormat = pixelAttribs.format; + final int pixelType = pixelAttribs.type; if (pixelType != GL.GL_BYTE && pixelType != GL.GL_UNSIGNED_BYTE) { throw new IOException("DDS writer only supports byte / unsigned byte textures"); @@ -1199,7 +1220,7 @@ public class TextureIO { case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: d3dFormat = DDSImage.D3DFMT_DXT5; break; default: throw new IOException("Unsupported pixel format 0x" + Integer.toHexString(pixelFormat) + " by DDS writer"); } - + ByteBuffer[] mipmaps = null; if (data.getMipmapData() != null) { mipmaps = new ByteBuffer[data.getMipmapData().length]; @@ -1210,7 +1231,7 @@ public class TextureIO { mipmaps = new ByteBuffer[] { (ByteBuffer) data.getBuffer() }; } - DDSImage image = DDSImage.createFromData(d3dFormat, + final DDSImage image = DDSImage.createFromData(d3dFormat, data.getWidth(), data.getHeight(), mipmaps); @@ -1226,19 +1247,21 @@ public class TextureIO { // SGI (rgb) texture writer // static class SGITextureWriter implements TextureWriter { - public boolean write(File file, - TextureData data) throws IOException { - String fileSuffix = IOUtil.getFileSuffix(file); + @Override + public boolean write(final File file, + final TextureData data) throws IOException { + final String fileSuffix = IOUtil.getFileSuffix(file); if (SGI.equals(fileSuffix) || SGI_RGB.equals(fileSuffix)) { // See whether the SGI writer can handle this TextureData - int pixelFormat = data.getPixelFormat(); - int pixelType = data.getPixelType(); + final GLPixelAttributes pixelAttribs = data.getPixelAttributes(); + final int pixelFormat = pixelAttribs.format; + final int pixelType = pixelAttribs.type; if ((pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA) && (pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { - ByteBuffer buf = ((data.getBuffer() != null) ? + final ByteBuffer buf = ((data.getBuffer() != null) ? (ByteBuffer) data.getBuffer() : (ByteBuffer) data.getMipmapData()[0]); byte[] bytes; @@ -1251,7 +1274,7 @@ public class TextureIO { buf.rewind(); } - SGIImage image = SGIImage.createFromData(data.getWidth(), + final SGIImage image = SGIImage.createFromData(data.getWidth(), data.getHeight(), (pixelFormat == GL.GL_RGBA), bytes); @@ -1268,39 +1291,41 @@ public class TextureIO { //---------------------------------------------------------------------- // TGA (Targa) texture writer - + static class TGATextureWriter implements TextureWriter { - public boolean write(File file, - TextureData data) throws IOException { + @Override + public boolean write(final File file, + final TextureData data) throws IOException { if (TGA.equals(IOUtil.getFileSuffix(file))) { // See whether the TGA writer can handle this TextureData - int pixelFormat = data.getPixelFormat(); - int pixelType = data.getPixelType(); + final GLPixelAttributes pixelAttribs = data.getPixelAttributes(); + final int pixelFormat = pixelAttribs.format; + final int pixelType = pixelAttribs.type; if ((pixelFormat == GL.GL_RGB || - pixelFormat == GL.GL_RGBA || - pixelFormat == GL2.GL_BGR || + pixelFormat == GL.GL_RGBA || + pixelFormat == GL2GL3.GL_BGR || pixelFormat == GL.GL_BGRA ) && (pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { - + ByteBuffer buf = (ByteBuffer) data.getBuffer(); if (null == buf) { buf = (ByteBuffer) data.getMipmapData()[0]; } buf.rewind(); - - if( pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA ) { + + if( pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA ) { // Must reverse order of red and blue channels to get correct results - int skip = ((pixelFormat == GL.GL_RGB) ? 3 : 4); + final int skip = ((pixelFormat == GL.GL_RGB) ? 3 : 4); for (int i = 0; i < buf.remaining(); i += skip) { - byte red = buf.get(i + 0); - byte blue = buf.get(i + 2); + final byte red = buf.get(i + 0); + final byte blue = buf.get(i + 2); buf.put(i + 0, blue); buf.put(i + 2, red); } } - TGAImage image = TGAImage.createFromData(data.getWidth(), + final TGAImage image = TGAImage.createFromData(data.getWidth(), data.getHeight(), (pixelFormat == GL.GL_RGBA || pixelFormat == GL.GL_BGRA), false, buf); @@ -1312,83 +1337,71 @@ public class TextureIO { } return false; - } + } } //---------------------------------------------------------------------- // PNG texture writer - + static class PNGTextureWriter implements TextureWriter { - public boolean write(File file, TextureData data) throws IOException { + @Override + public boolean write(final File file, final TextureData data) throws IOException { if (PNG.equals(IOUtil.getFileSuffix(file))) { // See whether the PNG writer can handle this TextureData - int pixelFormat = data.getPixelFormat(); - int pixelType = data.getPixelType(); - boolean reversedChannels; - int bytesPerPixel; - switch(pixelFormat) { - case GL.GL_LUMINANCE: - reversedChannels=false; - bytesPerPixel=1; - break; - case GL.GL_RGB: - reversedChannels=false; - bytesPerPixel=3; - break; - case GL.GL_RGBA: - reversedChannels=false; - bytesPerPixel=4; - break; - case GL2.GL_BGR: - reversedChannels=true; - bytesPerPixel=3; - break; - case GL.GL_BGRA: - reversedChannels=true; - bytesPerPixel=4; - break; - default: - reversedChannels=false; - bytesPerPixel=-1; - break; - } + final GLPixelAttributes pixelAttribs = data.getPixelAttributes(); + final int pixelFormat = pixelAttribs.format; + final int pixelType = pixelAttribs.type; + final int bytesPerPixel = pixelAttribs.bytesPerPixel; + final PixelFormat pixFmt = pixelAttribs.getPixelFormat(); if ( ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel) && - ( pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { - ByteBuffer buf = (ByteBuffer) data.getBuffer(); - if (null == buf) { - buf = (ByteBuffer) data.getMipmapData()[0]; + ( pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { + Buffer buf0 = data.getBuffer(); + if (null == buf0) { + buf0 = data.getMipmapData()[0]; + } + if( null == buf0 ) { + throw new IOException("Pixel storage buffer is null"); + } + final DimensionImmutable size = new Dimension(data.getWidth(), data.getHeight()); + if( buf0 instanceof ByteBuffer ) { + final ByteBuffer buf = (ByteBuffer) buf0; + buf.rewind(); + final PNGPixelRect image = new PNGPixelRect(pixFmt, size, + 0 /* stride */, !data.getMustFlipVertically() /* isGLOriented */, buf /* pixels */, + -1f, -1f); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */)); + image.write(outs, true /* close */); + return true; + } else if( buf0 instanceof IntBuffer ) { + final IntBuffer buf = (IntBuffer) buf0; + buf.rewind(); + final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(file, true /* allowOverwrite */)); + PNGPixelRect.write(pixFmt, size, + 0 /* stride */, !data.getMustFlipVertically() /* isGLOriented */, buf /* pixels */, + -1f, -1f, outs, true /* closeOutstream */); + return true; + } else { + throw new IOException("PNG writer doesn't support pixel storage buffer of type "+buf0.getClass().getName()); } - buf.rewind(); - - PNGImage image = PNGImage.createFromData(data.getWidth(), data.getHeight(), -1f, -1f, - bytesPerPixel, reversedChannels, buf); - image.write(file, true); - return true; } throw new IOException("PNG writer doesn't support this pixel format 0x"+Integer.toHexString(pixelFormat)+ " / type 0x"+Integer.toHexString(pixelFormat)+" (only GL_RGB/A, GL_BGR/A + bytes)"); } return false; - } + } } - + //---------------------------------------------------------------------- // Helper routines // - private static int glGetInteger(GL gl, int pname) { - int[] tmp = new int[1]; - gl.glGetIntegerv(pname, tmp, 0); - return tmp[0]; - } - - private static int glGetTexLevelParameteri(GL2GL3 gl, int target, int level, int pname) { - int[] tmp = new int[1]; + private static int glGetTexLevelParameteri(final GL2GL3 gl, final int target, final int level, final int pname) { + final int[] tmp = new int[1]; gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0); return tmp[0]; } - private static String toLowerCase(String arg) { + private static String toLowerCase(final String arg) { if (arg == null) { return null; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java index 6b41c0bc8..5add4f695 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. @@ -28,30 +28,35 @@ package com.jogamp.opengl.util.texture; import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLEventListener; + +import com.jogamp.opengl.util.TimeFrameI; /** * Protocol for texture sequences, like animations, movies, etc. * <p> - * Ensure to respect the texture coordinates provided by + * Ensure to respect the texture coordinates provided by * {@link TextureFrame}.{@link TextureFrame#getTexture() getTexture()}.{@link Texture#getImageTexCoords() getImageTexCoords()}. * </p> - * The user's shader shall be fitted for this implementation. + * The user's shader shall be fitted for this implementation. * Assuming we use a base shader code w/o headers using </code>ShaderCode</code>. * (Code copied from unit test / demo <code>TexCubeES2</code>) * <pre> - * + * static final String[] es2_prelude = { "#version 100\n", "precision mediump float;\n" }; static final String gl2_prelude = "#version 110\n"; static final String shaderBasename = "texsequence_xxx"; // the base shader code w/o headers - static final String myTextureLookupName = "myTexture2D"; // the desired texture lookup function - + static final String myTextureLookupName = "myTexture2D"; // the desired texture lookup function + private void initShader(GL2ES2 gl, TextureSequence texSeq) { // Create & Compile the shader objects - ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, TexCubeES2.class, + ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, TexCubeES2.class, "shader", "shader/bin", shaderBasename, true); - ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, TexCubeES2.class, + ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, TexCubeES2.class, "shader", "shader/bin", shaderBasename, true); - + // Prelude shader code w/ GLSL profile specifics [ 1. pre-proc, 2. other ] int rsFpPos; if(gl.isGLES2()) { @@ -68,25 +73,25 @@ import javax.media.opengl.GL; if(gl.isGLES2()) { // insert ES2 default precision declaration rsFpPos = rsFp.insertShaderSource(0, rsFpPos, es2_prelude[1]); - } + } // negotiate the texture lookup function name final String texLookupFuncName = texSeq.getTextureLookupFunctionName(myTextureLookupName); - - // in case a fixed lookup function is being chosen, replace the name in our code + + // in case a fixed lookup function is being chosen, replace the name in our code rsFp.replaceInShaderSource(myTextureLookupName, texLookupFuncName); - - // Cache the TextureSequence shader details in StringBuffer: + + // Cache the TextureSequence shader details in StringBuilder: final StringBuilder sFpIns = new StringBuilder(); - + // .. declaration of the texture sampler using the implementation specific type sFpIns.append("uniform ").append(texSeq.getTextureSampler2DType()).append(" mgl_ActiveTexture;\n"); - + // .. the actual texture lookup function, maybe null in case a built-in function is being used sFpIns.append(texSeq.getTextureLookupFragmentShaderImpl()); - + // Now insert the TextureShader details in our shader after the given tag: rsFp.insertShaderSource(0, "TEXTURE-SEQUENCE-CODE-BEGIN", 0, sFpIns); - + // Create & Link the shader program ShaderProgram sp = new ShaderProgram(); sp.add(rsVp); @@ -98,40 +103,70 @@ import javax.media.opengl.GL; * </pre> * The above procedure might look complicated, however, it allows most flexibility and * workarounds to also deal with GLSL bugs. - * + * */ public interface TextureSequence { - public static final String GL_OES_EGL_image_external_Required_Prelude = "#extension GL_OES_EGL_image_external : enable\n"; public static final String samplerExternalOES = "samplerExternalOES"; public static final String sampler2D = "sampler2D"; - - /** + + /** * Texture holder interface, maybe specialized by implementation - * to associated related data. + * to associated related data. */ - public static class TextureFrame { - public TextureFrame(Texture t) { + public static class TextureFrame extends TimeFrameI { + public TextureFrame(final Texture t, final int pts, final int duration) { + super(pts, duration); texture = t; } - + public TextureFrame(final Texture t) { + texture = t; + } + public final Texture getTexture() { return texture; } - + + @Override public String toString() { - return "TextureFrame[" + texture + "]"; + return "TextureFrame[pts " + pts + " ms, l " + duration + " ms, texID "+ (null != texture ? texture.getTextureObject() : 0) + "]"; } protected final Texture texture; } + /** + * Event listener to notify users of updates regarding the {@link TextureSequence}. + * <p> + * Implementations sending events down to all listeners, + * while not necessarily making the user's OpenGL context current. + * </p> + * <p> + * Events may be sent from a 3rd-party thread, possibly holding another, maybe shared, OpenGL context current.<br/> + * Hence a user shall not issue <i>any</i> OpenGL, time consuming + * or {@link TextureSequence} operations directly.<br> + * Instead, the user shall: + * <ul> + * <li>off-load complex or {@link TextureSequence} commands on another thread, or</li> + * <li>injecting {@link GLRunnable} objects via {@link GLAutoDrawable#invoke(boolean, GLRunnable)}, or</li> + * <li>simply changing a volatile state of their {@link GLEventListener} implementation.</li> + * </ul> + * </p> + * */ public interface TexSeqEventListener<T extends TextureSequence> { - /** - * Signaling listeners that {@link TextureSequence#getNextTexture(GL, boolean)} is able to deliver a new frame. - * @param ts the event source - * @param when system time in msec. + /** + * Signaling listeners that a new {@link TextureFrame} is available. + * <p> + * User shall utilize {@link TextureSequence#getNextTexture(GL)} to dequeue it to maintain + * a consistent queue. + * </p> + * @param ts the event source + * @param newFrame the newly enqueued frame + * @param when system time in msec. **/ - public void newFrameAvailable(T ts, long when); + public void newFrameAvailable(T ts, TextureFrame newFrame, long when); } - - /** Return the texture unit to be used with this frame. */ + + /** Returns the texture target used by implementation. */ + public int getTextureTarget(); + + /** Return the texture unit used to render the current frame. */ public int getTextureUnit(); public int[] getTextureMinMagFilter(); @@ -139,62 +174,68 @@ public interface TextureSequence { public int[] getTextureWrapST(); /** + * Returns true if texture source is ready <i>and</i> a texture is available + * via {@link #getNextTexture(GL)} and {@link #getLastTexture()}. + */ + public boolean isTextureAvailable(); + + /** * Returns the last updated texture. * <p> * In case the instance is just initialized, it shall return a <code>TextureFrame</code> - * object with valid attributes. The texture content may be undefined - * until the first call of {@link #getNextTexture(GL, boolean)}.<br> - * </p> + * object with valid attributes. The texture content may be undefined + * until the first call of {@link #getNextTexture(GL)}.<br> + * </p> * Not blocking. - * - * @throws IllegalStateException if instance is not initialized + * + * @throws IllegalStateException if instance is not initialized */ public TextureFrame getLastTexture() throws IllegalStateException ; /** - * Returns the next texture to be rendered. + * Returns the next texture to be rendered. * <p> - * Implementation shall block until next frame is available if <code>blocking</code> is <code>true</code>, - * otherwise it shall return the last frame in case a new frame is not available. + * Implementation shall return the next frame if available, may block if a next frame may arrive <i>soon</i>. + * Otherwise implementation shall return the last frame. * </p> * <p> - * Shall return <code>null</code> in case <i>no</i> frame is available. + * Shall return <code>null</code> in case <i>no</i> next or last frame is available. * </p> - * - * @throws IllegalStateException if instance is not initialized + * + * @throws IllegalStateException if instance is not initialized */ - public TextureFrame getNextTexture(GL gl, boolean blocking) throws IllegalStateException ; - + public TextureFrame getNextTexture(GL gl) throws IllegalStateException ; + /** - * In case a shader extension is required, based on the implementation + * In case a shader extension is required, based on the implementation * and the runtime GL profile, this method returns the preprocessor macros, e.g.: * <pre> * #extension GL_OES_EGL_image_external : enable - * </pre> - * - * @throws IllegalStateException if instance is not initialized + * </pre> + * + * @throws IllegalStateException if instance is not initialized */ public String getRequiredExtensionsShaderStub() throws IllegalStateException ; - - /** + + /** * Returns either <code>sampler2D</code> or <code>samplerExternalOES</code> - * depending on {@link #getLastTexture()}.{@link TextureFrame#getTexture() getTexture()}.{@link Texture#getTarget() getTarget()}. - * - * @throws IllegalStateException if instance is not initialized + * depending on {@link #getLastTexture()}.{@link TextureFrame#getTexture() getTexture()}.{@link Texture#getTarget() getTarget()}. + * + * @throws IllegalStateException if instance is not initialized **/ public String getTextureSampler2DType() throws IllegalStateException ; - + /** * @param desiredFuncName desired lookup function name. If <code>null</code> or ignored by the implementation, - * a build-in name is returned. + * a build-in name is returned. * @return the final lookup function name - * + * * @see {@link #getTextureLookupFragmentShaderImpl()} - * + * * @throws IllegalStateException if instance is not initialized */ public String getTextureLookupFunctionName(String desiredFuncName) throws IllegalStateException ; - + /** * Returns the complete texture2D lookup function code of type * <pre> @@ -207,14 +248,33 @@ public interface TextureSequence { * <i>funcName</i> can be negotiated and queried via {@link #getTextureLookupFunctionName(String)}. * </p> * Note: This function may return an empty string in case a build-in lookup - * function is being chosen. If the implementation desires so, + * function is being chosen. If the implementation desires so, * {@link #getTextureLookupFunctionName(String)} will ignore the desired function name * and returns the build-in lookup function name. * </p> * @see #getTextureLookupFunctionName(String) * @see #getTextureSampler2DType() - * - * @throws IllegalStateException if instance is not initialized + * + * @throws IllegalStateException if instance is not initialized + */ + public String getTextureLookupFragmentShaderImpl() throws IllegalStateException; + + /** + * Returns the hash code of the strings: + * <ul> + * <li>{@link #getTextureLookupFragmentShaderImpl()}</li> + * <li>{@link #getTextureSampler2DType()}</li> + * </ul> + * <p> + * Returns zero if {@link #isTextureAvailable() texture is not available}. + * </p> + * The returned hash code allows selection of a matching shader program for this {@link TextureSequence} instance. + * <p> + * </p> + * <p> + * Implementation shall cache the resulting hash code, + * which must be reset to zero if {@link #isTextureAvailable() texture is not available}. + * </p> */ - public String getTextureLookupFragmentShaderImpl() throws IllegalStateException ; -}
\ No newline at end of file + public int getTextureFragmentShaderHashCode(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureState.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureState.java new file mode 100644 index 000000000..467ab41c5 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureState.java @@ -0,0 +1,168 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.texture; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GL3; +import javax.media.opengl.GLException; + +/** + * Preserves a [ texture-unit, texture-target ] state. + * <p> + * The states keys are the retrieved active texture-unit and the given texture-target + * for which the following states are being queried: + * <pre> + * - texture-object + * - GL.GL_TEXTURE_MAG_FILTER + * - GL.GL_TEXTURE_MIN_FILTER + * - GL.GL_TEXTURE_WRAP_S + * - GL.GL_TEXTURE_WRAP_T + * </pre> + */ +public class TextureState { + /** + * Returns the <code>pname</code> to query the <code>textureTarget</code> currently bound to the active texture-unit. + * <p> + * Returns <code>0</code> is <code>textureTarget</code> is not supported. + * </p> + */ + public static final int getTextureTargetQueryName(final int textureTarget) { + final int texBindQName; + switch(textureTarget) { + case GL.GL_TEXTURE_2D: texBindQName = GL.GL_TEXTURE_BINDING_2D; break; + case GL.GL_TEXTURE_CUBE_MAP: texBindQName = GL.GL_TEXTURE_BINDING_CUBE_MAP; break; + case GL2ES2.GL_TEXTURE_3D: texBindQName = GL2ES2.GL_TEXTURE_BINDING_3D; break; + case GL2GL3.GL_TEXTURE_1D: texBindQName = GL2GL3.GL_TEXTURE_BINDING_1D; break; + case GL2GL3.GL_TEXTURE_1D_ARRAY: texBindQName = GL2GL3.GL_TEXTURE_BINDING_1D_ARRAY; break; + case GL.GL_TEXTURE_2D_ARRAY: texBindQName = GL.GL_TEXTURE_BINDING_2D_ARRAY; break; + case GL2GL3.GL_TEXTURE_RECTANGLE: texBindQName = GL2GL3.GL_TEXTURE_BINDING_RECTANGLE; break; + case GL2GL3.GL_TEXTURE_BUFFER: texBindQName = GL2GL3.GL_TEXTURE_BINDING_BUFFER; break; + case GL3.GL_TEXTURE_2D_MULTISAMPLE: texBindQName = GL3.GL_TEXTURE_BINDING_2D_MULTISAMPLE; break; + case GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY: texBindQName = GL3.GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY; break; + default: texBindQName = 0; + } + return texBindQName; + } + + private final int target; + /** + * <pre> + * 0 - unit + * 1 - texture object + * 2 - GL.GL_TEXTURE_MAG_FILTER + * 3 - GL.GL_TEXTURE_MIN_FILTER + * 4 - GL.GL_TEXTURE_WRAP_S + * 5 - GL.GL_TEXTURE_WRAP_T + * </pre> + */ + private final int[] state = new int[] { 0, 0, 0, 0, 0, 0 }; + + private static final String toHexString(final int i) { return "0x"+Integer.toHexString(i); } + + private static final int activeTexture(final GL gl) { + final int[] vi = { 0 }; + gl.glGetIntegerv(GL.GL_ACTIVE_TEXTURE, vi, 0); + return vi[0]; + } + + /** + * Creates a texture state for the retrieved active texture-unit and the given texture-target. + * See {@link TextureState}. + * @param gl current GL context's GL object + * @param textureTarget + * @throws GLException if textureTarget is not supported + */ + public TextureState(final GL gl, final int textureTarget) throws GLException { + this(gl, activeTexture(gl), textureTarget); + } + + /** + * Creates a texture state for the given active texture-unit and the given texture-target. + * See {@link TextureState}. + * @param gl current GL context's GL object + * @param textureUnit of range [ {@link GL#GL_TEXTURE0}.. ] + * @param textureTarget + * @throws GLException if textureTarget is not supported + */ + public TextureState(final GL gl, final int textureUnit, final int textureTarget) throws GLException { + target = textureTarget; + state[0] = textureUnit; + final int texBindQName = getTextureTargetQueryName(textureTarget); + if( 0 == texBindQName ) { + throw new GLException("Unsupported textureTarget "+toHexString(textureTarget)); + } + gl.glGetIntegerv(texBindQName, state, 1); + gl.glGetTexParameteriv(target, GL.GL_TEXTURE_MAG_FILTER, state, 2); + gl.glGetTexParameteriv(target, GL.GL_TEXTURE_MIN_FILTER, state, 3); + gl.glGetTexParameteriv(target, GL.GL_TEXTURE_WRAP_S, state, 4); + gl.glGetTexParameteriv(target, GL.GL_TEXTURE_WRAP_T, state, 5); + } + + /** + * Restores the texture-unit's texture-target state. + * <p> + * First the texture-unit is activated, then all states are restored. + * </p> + * @param gl current GL context's GL object + */ + public final void restore(final GL gl) { + gl.glActiveTexture(state[0]); + gl.glBindTexture(target, state[1]); + gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, state[2]); + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, state[3]); + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, state[4]); + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, state[5]); + } + + /** Returns the texture-unit of this state, key value. Unit is of range [ {@link GL#GL_TEXTURE0}.. ]. */ + public final int getUnit() { return state[0]; } + /** Returns the texture-target of this state, key value. */ + public final int getTarget() { return target; } + + /** Returns the state's texture-object. */ + public final int getObject() { return state[1]; } + /** Returns the state's mag-filter param. */ + public final int getMagFilter() { return state[2]; } + /** Returns the state's min-filter param. */ + public final int getMinFilter() { return state[3]; } + /** Returns the state's wrap-s param. */ + public final int getWrapS() { return state[4]; } + /** Returns the state's wrap-t param. */ + public final int getWrapT() { return state[5]; } + + + @Override + public final String toString() { + return "TextureState[unit "+(state[0] - GL.GL_TEXTURE0)+", target "+toHexString(target)+ + ": obj "+toHexString(state[1])+ + ", filter[mag "+toHexString(state[2])+", min "+toHexString(state[3])+"], "+ + ": wrap[s "+toHexString(state[4])+", t "+toHexString(state[5])+"]]"; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java index ad96a9939..ccb3ecc3c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureData.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -37,15 +38,36 @@ package com.jogamp.opengl.util.texture.awt; import java.awt.AlphaComposite; -import java.awt.Color; import java.awt.Graphics2D; import java.awt.Transparency; -import java.awt.color.*; -import java.awt.image.*; -import java.nio.*; +import java.awt.image.BufferedImage; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferDouble; +import java.awt.image.DataBufferFloat; +import java.awt.image.DataBufferInt; +import java.awt.image.DataBufferShort; +import java.awt.image.DataBufferUShort; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.SampleModel; +import java.awt.image.SinglePixelPackedSampleModel; +import java.awt.image.WritableRaster; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; -import javax.media.opengl.*; -import com.jogamp.opengl.util.texture.*; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; +import com.jogamp.opengl.util.texture.TextureData; public class AWTTextureData extends TextureData { // Mechanism for lazily converting input BufferedImages with custom @@ -56,19 +78,19 @@ public class AWTTextureData extends TextureData { private boolean expectingEXTABGR; private boolean expectingGL12; - private static final ColorModel rgbaColorModel = - new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - new int[] {8, 8, 8, 8}, true, true, + private static final java.awt.image.ColorModel rgbaColorModel = + new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB), + new int[] {8, 8, 8, 8}, true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); - private static final ColorModel rgbColorModel = - new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + private static final java.awt.image.ColorModel rgbColorModel = + new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB), new int[] {8, 8, 8, 0}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); - /** + /** * Constructs a new TextureData object with the specified parameters * and data contained in the given BufferedImage. The resulting * TextureData "wraps" the contents of the BufferedImage, so if a @@ -91,11 +113,11 @@ public class AWTTextureData extends TextureData { * texture * @param image the image containing the texture data */ - public AWTTextureData(GLProfile glp, - int internalFormat, - int pixelFormat, - boolean mipmap, - BufferedImage image) { + public AWTTextureData(final GLProfile glp, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final BufferedImage image) { super(glp); if (internalFormat == 0) { this.internalFormat = image.getColorModel().hasAlpha() ? GL.GL_RGBA : GL.GL_RGB; @@ -114,33 +136,38 @@ public class AWTTextureData extends TextureData { } } - /** Returns the intended OpenGL pixel format of the texture data. */ - public int getPixelFormat() { + private void validatePixelAttributes() { if (imageForLazyCustomConversion != null) { if (!((expectingEXTABGR && haveEXTABGR) || (expectingGL12 && haveGL12))) { - revertPixelFormatAndType(); + revertPixelAttributes(); } } - return pixelFormat; } - /** Returns the intended OpenGL pixel type of the texture data. */ + + @Override + public GLPixelAttributes getPixelAttributes() { + validatePixelAttributes(); + return super.getPixelAttributes(); + } + + @Override + public int getPixelFormat() { + validatePixelAttributes(); + return super.getPixelFormat(); + } + @Override public int getPixelType() { - if (imageForLazyCustomConversion != null) { - if (!((expectingEXTABGR && haveEXTABGR) || - (expectingGL12 && haveGL12))) { - revertPixelFormatAndType(); - } - } - return pixelType; + validatePixelAttributes(); + return super.getPixelType(); } - /** Returns the texture data, or null if it is specified as a set of mipmaps. */ + @Override public Buffer getBuffer() { if (imageForLazyCustomConversion != null) { if (!((expectingEXTABGR && haveEXTABGR) || (expectingGL12 && haveGL12))) { - revertPixelFormatAndType(); + revertPixelAttributes(); // Must present the illusion to the end user that we are simply // wrapping the input BufferedImage createFromCustom(imageForLazyCustomConversion); @@ -149,8 +176,8 @@ public class AWTTextureData extends TextureData { return buffer; } - private void createFromImage(GLProfile glp, BufferedImage image) { - pixelType = 0; // Determine from image + private void createFromImage(final GLProfile glp, final BufferedImage image) { + pixelAttributes = GLPixelAttributes.UNDEF; // Determine from image mustFlipVertically = true; width = image.getWidth(); @@ -158,7 +185,7 @@ public class AWTTextureData extends TextureData { int scanlineStride; - SampleModel sm = image.getRaster().getSampleModel(); + final SampleModel sm = image.getRaster().getSampleModel(); if (sm instanceof SinglePixelPackedSampleModel) { scanlineStride = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); @@ -180,24 +207,21 @@ public class AWTTextureData extends TextureData { if (glp.isGL2GL3()) { switch (image.getType()) { case BufferedImage.TYPE_INT_RGB: - pixelFormat = GL.GL_BGRA; - pixelType = GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV; + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_INT_ARGB_PRE: - pixelFormat = GL.GL_BGRA; - pixelType = GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV; + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_INT_BGR: - pixelFormat = GL.GL_RGBA; - pixelType = GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV; + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV); rowLength = scanlineStride; alignment = 4; expectingGL12 = true; @@ -208,8 +232,7 @@ public class AWTTextureData extends TextureData { // we can pass the image data directly to OpenGL only if // we have an integral number of pixels in each scanline if ((scanlineStride % 3) == 0) { - pixelFormat = GL2GL3.GL_BGR; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else { @@ -223,17 +246,16 @@ public class AWTTextureData extends TextureData { // we can pass the image data directly to OpenGL only if // we have an integral number of pixels in each scanline // and only if the GL_EXT_abgr extension is present - + // NOTE: disabling this code path for now as it appears it's // buggy at least on some NVidia drivers and doesn't perform // the necessary byte swapping (FIXME: needs more // investigation) if ((scanlineStride % 4) == 0 && glp.isGL2() && false) { - pixelFormat = GL2.GL_ABGR_EXT; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; alignment = 4; - + // Store a reference to the original image for later in // case it turns out that we don't have GL_EXT_abgr at the // time we're going to do the texture upload to OpenGL @@ -246,30 +268,26 @@ public class AWTTextureData extends TextureData { } } case BufferedImage.TYPE_USHORT_565_RGB: - pixelFormat = GL.GL_RGB; - pixelType = GL.GL_UNSIGNED_SHORT_5_6_5; + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_USHORT_555_RGB: - pixelFormat = GL.GL_BGRA; - pixelType = GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV; + pixelAttributes = new GLPixelAttributes(GL.GL_BGRA, GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_BYTE_GRAY: - pixelFormat = GL.GL_LUMINANCE; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 1; break; case BufferedImage.TYPE_USHORT_GRAY: - pixelFormat = GL.GL_LUMINANCE; - pixelType = GL.GL_UNSIGNED_SHORT; + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_SHORT); rowLength = scanlineStride; alignment = 2; break; @@ -282,15 +300,13 @@ public class AWTTextureData extends TextureData { case BufferedImage.TYPE_BYTE_INDEXED: case BufferedImage.TYPE_CUSTOM: default: - ColorModel cm = image.getColorModel(); + final java.awt.image.ColorModel cm = image.getColorModel(); if (cm.equals(rgbColorModel)) { - pixelFormat = GL.GL_RGB; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else if (cm.equals(rgbaColorModel)) { - pixelFormat = GL.GL_RGBA; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; // FIXME: correct? alignment = 4; } else { @@ -302,8 +318,7 @@ public class AWTTextureData extends TextureData { } else { switch (image.getType()) { case BufferedImage.TYPE_INT_RGB: - pixelFormat = GL.GL_RGB; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 3; expectingGL12 = true; @@ -318,24 +333,21 @@ public class AWTTextureData extends TextureData { case BufferedImage.TYPE_4BYTE_ABGR_PRE: throw new GLException("INT_BGR n.a."); case BufferedImage.TYPE_USHORT_565_RGB: - pixelFormat = GL.GL_RGB; - pixelType = GL.GL_UNSIGNED_SHORT_5_6_5; + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_USHORT_555_RGB: - pixelFormat = GL.GL_RGBA; - pixelType = GL.GL_UNSIGNED_SHORT_5_5_5_1; + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1); rowLength = scanlineStride; alignment = 2; expectingGL12 = true; setupLazyCustomConversion(image); break; case BufferedImage.TYPE_BYTE_GRAY: - pixelFormat = GL.GL_LUMINANCE; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride; alignment = 1; break; @@ -350,15 +362,13 @@ public class AWTTextureData extends TextureData { case BufferedImage.TYPE_BYTE_INDEXED: case BufferedImage.TYPE_CUSTOM: default: - ColorModel cm = image.getColorModel(); + final java.awt.image.ColorModel cm = image.getColorModel(); if (cm.equals(rgbColorModel)) { - pixelFormat = GL.GL_RGB; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_RGB, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 3; alignment = 1; } else if (cm.equals(rgbaColorModel)) { - pixelFormat = GL.GL_RGBA; - pixelType = GL.GL_UNSIGNED_BYTE; + pixelAttributes = new GLPixelAttributes(GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); rowLength = scanlineStride / 4; // FIXME: correct? alignment = 4; } else { @@ -372,9 +382,11 @@ public class AWTTextureData extends TextureData { createNIOBufferFromImage(image); } - private void setupLazyCustomConversion(BufferedImage image) { + private void setupLazyCustomConversion(final BufferedImage image) { imageForLazyCustomConversion = image; - boolean hasAlpha = image.getColorModel().hasAlpha(); + final boolean hasAlpha = image.getColorModel().hasAlpha(); + int pixelFormat = pixelAttributes.format; + int pixelType = pixelAttributes.type; if (pixelFormat == 0) { pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; } @@ -383,7 +395,7 @@ public class AWTTextureData extends TextureData { // Allow previously-selected pixelType (if any) to override that // we can infer from the DataBuffer - DataBuffer data = image.getRaster().getDataBuffer(); + final DataBuffer data = image.getRaster().getDataBuffer(); if (data instanceof DataBufferByte || isPackedInt(image)) { // Don't use GL_UNSIGNED_INT for BufferedImage packed int images if (pixelType == 0) pixelType = GL.GL_UNSIGNED_BYTE; @@ -393,7 +405,7 @@ public class AWTTextureData extends TextureData { if (pixelType == 0) pixelType = GL.GL_FLOAT; } else if (data instanceof DataBufferInt) { // FIXME: should we support signed ints? - if (pixelType == 0) pixelType = GL2GL3.GL_UNSIGNED_INT; + if (pixelType == 0) pixelType = GL.GL_UNSIGNED_INT; } else if (data instanceof DataBufferShort) { if (pixelType == 0) pixelType = GL.GL_SHORT; } else if (data instanceof DataBufferUShort) { @@ -401,15 +413,16 @@ public class AWTTextureData extends TextureData { } else { throw new RuntimeException("Unexpected DataBuffer type?"); } + pixelAttributes = new GLPixelAttributes(pixelFormat, pixelType); } - private void createFromCustom(BufferedImage image) { - int width = image.getWidth(); - int height = image.getHeight(); + private void createFromCustom(final BufferedImage image) { + final int width = image.getWidth(); + final int height = image.getHeight(); // create a temporary image that is compatible with OpenGL - boolean hasAlpha = image.getColorModel().hasAlpha(); - ColorModel cm = null; + final boolean hasAlpha = image.getColorModel().hasAlpha(); + java.awt.image.ColorModel cm = null; int dataBufferType = image.getRaster().getDataBuffer().getDataType(); // Don't use integer components for packed int images if (isPackedInt(image)) { @@ -419,25 +432,25 @@ public class AWTTextureData extends TextureData { cm = hasAlpha ? rgbaColorModel : rgbColorModel; } else { if (hasAlpha) { - cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB), null, true, true, Transparency.TRANSLUCENT, dataBufferType); } else { - cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + cm = new ComponentColorModel(java.awt.color.ColorSpace.getInstance(java.awt.color.ColorSpace.CS_sRGB), null, false, false, Transparency.OPAQUE, dataBufferType); } } - boolean premult = cm.isAlphaPremultiplied(); - WritableRaster raster = + final boolean premult = cm.isAlphaPremultiplied(); + final WritableRaster raster = cm.createCompatibleWritableRaster(width, height); - BufferedImage texImage = new BufferedImage(cm, raster, premult, null); + final BufferedImage texImage = new BufferedImage(cm, raster, premult, null); // copy the source image into the temporary image - Graphics2D g = texImage.createGraphics(); + final Graphics2D g = texImage.createGraphics(); g.setComposite(AlphaComposite.Src); g.drawImage(image, 0, 0, null); g.dispose(); @@ -446,29 +459,28 @@ public class AWTTextureData extends TextureData { createNIOBufferFromImage(texImage); } - private boolean isPackedInt(BufferedImage image) { - int imgType = image.getType(); + private boolean isPackedInt(final BufferedImage image) { + final int imgType = image.getType(); return (imgType == BufferedImage.TYPE_INT_RGB || imgType == BufferedImage.TYPE_INT_BGR || imgType == BufferedImage.TYPE_INT_ARGB || imgType == BufferedImage.TYPE_INT_ARGB_PRE); } - private void revertPixelFormatAndType() { + private void revertPixelAttributes() { // Knowing we don't have e.g. OpenGL 1.2 functionality available, // and knowing we're in the process of doing the fallback code // path, re-infer a vanilla pixel format and type compatible with // OpenGL 1.1 - pixelFormat = 0; - pixelType = 0; + pixelAttributes = GLPixelAttributes.UNDEF; setupLazyCustomConversion(imageForLazyCustomConversion); } - private void createNIOBufferFromImage(BufferedImage image) { + private void createNIOBufferFromImage(final BufferedImage image) { buffer = wrapImageDataBuffer(image); } - private Buffer wrapImageDataBuffer(BufferedImage image) { + private Buffer wrapImageDataBuffer(final BufferedImage image) { // // Note: Grabbing the DataBuffer will defeat Java2D's image // management mechanism (as of JDK 5/6, at least). This shouldn't @@ -478,7 +490,7 @@ public class AWTTextureData extends TextureData { // it could be. // - DataBuffer data = image.getRaster().getDataBuffer(); + final DataBuffer data = image.getRaster().getDataBuffer(); if (data instanceof DataBufferByte) { return ByteBuffer.wrap(((DataBufferByte) data).getData()); } else if (data instanceof DataBufferDouble) { diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureIO.java index fdd1365f7..c3b3adc75 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/awt/AWTTextureIO.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -59,8 +59,8 @@ public class AWTTextureIO extends TextureIO { * * @see #newTextureData(GLProfile, BufferedImage, boolean) */ - public static TextureData newTextureData(GLProfile glp, BufferedImage image, - boolean mipmap) { + public static TextureData newTextureData(final GLProfile glp, final BufferedImage image, + final boolean mipmap) { return newTextureDataImpl(glp, image, 0, 0, mipmap); } @@ -89,10 +89,10 @@ public class AWTTextureIO extends TextureIO { * @throws IllegalArgumentException if either internalFormat or * pixelFormat was 0 */ - public static TextureData newTextureData(GLProfile glp, BufferedImage image, - int internalFormat, - int pixelFormat, - boolean mipmap) throws IllegalArgumentException { + public static TextureData newTextureData(final GLProfile glp, final BufferedImage image, + final int internalFormat, + final int pixelFormat, + final boolean mipmap) throws IllegalArgumentException { if ((internalFormat == 0) || (pixelFormat == 0)) { throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); } @@ -100,7 +100,7 @@ public class AWTTextureIO extends TextureIO { return newTextureDataImpl(glp, image, internalFormat, pixelFormat, mipmap); } - /** + /** * Creates an OpenGL texture object from the specified BufferedImage * using the current OpenGL context. * @@ -112,18 +112,18 @@ public class AWTTextureIO extends TextureIO { * @throws GLException if no OpenGL context is current or if an * OpenGL error occurred */ - public static Texture newTexture(GLProfile glp, BufferedImage image, boolean mipmap) throws GLException { - TextureData data = newTextureData(glp, image, mipmap); - Texture texture = newTexture(data); + public static Texture newTexture(final GLProfile glp, final BufferedImage image, final boolean mipmap) throws GLException { + final TextureData data = newTextureData(glp, image, mipmap); + final Texture texture = newTexture(data); data.flush(); return texture; } - private static TextureData newTextureDataImpl(GLProfile glp, - BufferedImage image, - int internalFormat, - int pixelFormat, - boolean mipmap) { + private static TextureData newTextureDataImpl(final GLProfile glp, + final BufferedImage image, + final int internalFormat, + final int pixelFormat, + final boolean mipmap) { return new AWTTextureData(glp, internalFormat, pixelFormat, mipmap, image); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java index 3f91ae966..20fc92819 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/DDSImage.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -52,6 +52,7 @@ import java.nio.channels.FileChannel; import javax.media.opengl.GL; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; import com.jogamp.opengl.util.GLBuffers; @@ -67,13 +68,13 @@ public class DDSImage { that information in another way. */ public static class ImageInfo { - private ByteBuffer data; - private int width; - private int height; - private boolean isCompressed; - private int compressionFormat; + private final ByteBuffer data; + private final int width; + private final int height; + private final boolean isCompressed; + private final int compressionFormat; - public ImageInfo(ByteBuffer data, int width, int height, boolean compressed, int compressionFormat) { + public ImageInfo(final ByteBuffer data, final int width, final int height, final boolean compressed, final int compressionFormat) { this.data = data; this.width = width; this.height = height; this.isCompressed = compressed; this.compressionFormat = compressionFormat; } @@ -163,10 +164,10 @@ public class DDSImage { @return DDS image object @throws java.io.IOException if an I/O exception occurred */ - public static DDSImage read(String filename) throws IOException { + public static DDSImage read(final String filename) throws IOException { return read(new File(filename)); } - + /** Reads a DirectDraw surface from the specified file, returning the resulting DDSImage. @@ -174,8 +175,8 @@ public class DDSImage { @return DDS image object @throws java.io.IOException if an I/O exception occurred */ - public static DDSImage read(File file) throws IOException { - DDSImage image = new DDSImage(); + public static DDSImage read(final File file) throws IOException { + final DDSImage image = new DDSImage(); image.readFromFile(file); return image; } @@ -187,8 +188,8 @@ public class DDSImage { @return DDS image object @throws java.io.IOException if an I/O exception occurred */ - public static DDSImage read(ByteBuffer buf) throws IOException { - DDSImage image = new DDSImage(); + public static DDSImage read(final ByteBuffer buf) throws IOException { + final DDSImage image = new DDSImage(); image.readFromBuffer(buf); return image; } @@ -207,12 +208,12 @@ public class DDSImage { fis = null; } buf = null; - } catch (IOException e) { + } catch (final IOException e) { e.printStackTrace(); } } - /** + /** * Creates a new DDSImage from data supplied by the user. The * resulting DDSImage can be written to disk using the write() * method. @@ -228,11 +229,11 @@ public class DDSImage { * specified arguments * @return DDS image object */ - public static DDSImage createFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { - DDSImage image = new DDSImage(); + public static DDSImage createFromData(final int d3dFormat, + final int width, + final int height, + final ByteBuffer[] mipmapData) throws IllegalArgumentException { + final DDSImage image = new DDSImage(); image.initFromData(d3dFormat, width, height, mipmapData); return image; } @@ -256,7 +257,7 @@ public class DDSImage { in.mark(4); int magic = 0; for (int i = 0; i < 4; i++) { - int tmp = in.read(); + final int tmp = in.read(); if (tmp < 0) { in.reset(); return false; @@ -272,7 +273,7 @@ public class DDSImage { * @param filename File name to write to * @throws java.io.IOException if an I/O exception occurred */ - public void write(String filename) throws IOException { + public void write(final String filename) throws IOException { write(new File(filename)); } @@ -281,12 +282,12 @@ public class DDSImage { * @param file File object to write to * @throws java.io.IOException if an I/O exception occurred */ - public void write(File file) throws IOException { - FileOutputStream stream = IOUtil.getFileOutputStream(file, true); - FileChannel chan = stream.getChannel(); + public void write(final File file) throws IOException { + final FileOutputStream stream = IOUtil.getFileOutputStream(file, true); + final FileChannel chan = stream.getChannel(); // Create ByteBuffer for header in case the start of our // ByteBuffer isn't actually memory-mapped - ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize()); + final ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize()); hdr.order(ByteOrder.LITTLE_ENDIAN); header.write(hdr); hdr.rewind(); @@ -302,12 +303,12 @@ public class DDSImage { * @param flag DDSD_* flags set to test * @return true if flag present or false otherwise */ - public boolean isSurfaceDescFlagSet(int flag) { + public boolean isSurfaceDescFlagSet(final int flag) { return ((header.flags & flag) != 0); } /** Test for presence/absence of pixel format flags (DDPF_*) */ - public boolean isPixelFormatFlagSet(int flag) { + public boolean isPixelFormatFlagSet(final int flag) { return ((header.pfFlags & flag) != 0); } @@ -357,7 +358,7 @@ public class DDSImage { * @param side Side to test * @return true if side present or false otherwise */ - public boolean isCubemapSidePresent(int side) { + public boolean isCubemapSidePresent(final int side) { return isCubemap() && (header.ddsCaps2 & side) != 0; } @@ -402,7 +403,7 @@ public class DDSImage { * @param map Mipmap index * @return Image object */ - public ImageInfo getMipMap(int map) { + public ImageInfo getMipMap(final int map) { return getMipMap( 0, map ); } @@ -412,7 +413,7 @@ public class DDSImage { * @param map Mipmap index * @return Image object */ - public ImageInfo getMipMap(int side, int map) { + public ImageInfo getMipMap(final int side, final int map) { if (!isCubemap() && (side != 0)) { throw new RuntimeException( "Illegal side for 2D texture: " + side ); } @@ -434,7 +435,7 @@ public class DDSImage { } buf.limit(seek + mipMapSizeInBytes(map)); buf.position(seek); - ByteBuffer next = buf.slice(); + final ByteBuffer next = buf.slice(); buf.position(0); buf.limit(buf.capacity()); return new ImageInfo(next, mipMapWidth(map), mipMapHeight(map), isCompressed(), getCompressionFormat()); @@ -454,12 +455,12 @@ public class DDSImage { * @param side Cubemap side or 0 for 2D texture * @return Mipmap image objects set */ - public ImageInfo[] getAllMipMaps( int side ) { + public ImageInfo[] getAllMipMaps( final int side ) { int numLevels = getNumMipMaps(); if (numLevels == 0) { numLevels = 1; } - ImageInfo[] result = new ImageInfo[numLevels]; + final ImageInfo[] result = new ImageInfo[numLevels]; for (int i = 0; i < numLevels; i++) { result[i] = getMipMap(side, i); } @@ -472,9 +473,9 @@ public class DDSImage { @return String format code */ public static String getCompressionFormatName(int compressionFormat) { - StringBuilder buf = new StringBuilder(); + final StringBuilder buf = new StringBuilder(); for (int i = 0; i < 4; i++) { - char c = (char) (compressionFormat & 0xFF); + final char c = (char) (compressionFormat & 0xFF); buf.append(c); compressionFormat = compressionFormat >> 8; } @@ -491,9 +492,9 @@ public class DDSImage { GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, or GL_COMPRESSED_RGBA_S3TC_DXT5_EXT. */ - public static ByteBuffer allocateBlankBuffer(int width, - int height, - int openGLInternalFormat) { + public static ByteBuffer allocateBlankBuffer(final int width, + final int height, + final int openGLInternalFormat) { int size = width * height; switch (openGLInternalFormat) { case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: @@ -511,15 +512,15 @@ public class DDSImage { } if (size == 0) size = 1; - return GLBuffers.newDirectByteBuffer(size); + return Buffers.newDirectByteBuffer(size); } public void debugPrint() { - PrintStream tty = System.err; + final PrintStream tty = System.err; tty.println("Compressed texture: " + isCompressed()); if (isCompressed()) { - int fmt = getCompressionFormat(); - String name = getCompressionFormatName(fmt); + final int fmt = getCompressionFormat(); + final String name = getCompressionFormatName(fmt); tty.println("Compression format: 0x" + Integer.toHexString(fmt) + " (" + name + ")"); } tty.println("Width: " + header.width + " Height: " + header.height); @@ -567,7 +568,7 @@ public class DDSImage { tty.println("Raw pixel format flags: 0x" + Integer.toHexString(header.pfFlags)); tty.println("Depth: " + getDepth()); tty.println("Number of mip maps: " + getNumMipMaps()); - int fmt = getPixelFormat(); + final int fmt = getPixelFormat(); tty.print("Pixel format: "); switch (fmt) { case D3DFMT_R8G8B8: tty.println("D3DFMT_R8G8B8"); break; @@ -629,8 +630,8 @@ public class DDSImage { int ddsCapsReserved2; int textureStage; // stage in multitexture cascade - void read(ByteBuffer buf) throws IOException { - int magic = buf.getInt(); + void read(final ByteBuffer buf) throws IOException { + final int magic = buf.getInt(); if (magic != MAGIC) { throw new IOException("Incorrect magic number 0x" + Integer.toHexString(magic) + @@ -671,7 +672,7 @@ public class DDSImage { } // buf must be in little-endian byte order - void write(ByteBuffer buf) { + void write(final ByteBuffer buf) { buf.putInt(MAGIC); buf.putInt(size); buf.putInt(flags); @@ -722,15 +723,15 @@ public class DDSImage { private DDSImage() { } - private void readFromFile(File file) throws IOException { + private void readFromFile(final File file) throws IOException { fis = new FileInputStream(file); chan = fis.getChannel(); - ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY, + final ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY, 0, (int) file.length()); readFromBuffer(buf); } - private void readFromBuffer(ByteBuffer buf) throws IOException { + private void readFromBuffer(final ByteBuffer buf) throws IOException { this.buf = buf; buf.order(ByteOrder.LITTLE_ENDIAN); header = new Header(); @@ -738,10 +739,10 @@ public class DDSImage { fixupHeader(); } - private void initFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { + private void initFromData(final int d3dFormat, + final int width, + final int height, + final ByteBuffer[] mipmapData) throws IllegalArgumentException { // Check size of mipmap data compared against format, width and // height int topmostMipmapSize = width * height; @@ -763,9 +764,11 @@ public class DDSImage { default: throw new IllegalArgumentException("d3dFormat must be one of the known formats"); } - + // Now check the mipmaps against this size int curSize = topmostMipmapSize; + int mipmapWidth = width; + int mipmapHeight = height; int totalSize = 0; for (int i = 0; i < mipmapData.length; i++) { if (mipmapData[i].remaining() != curSize) { @@ -773,19 +776,22 @@ public class DDSImage { " didn't match expected data size (expected " + curSize + ", got " + mipmapData[i].remaining() + ")"); } - curSize /= 4; + // Compute next mipmap size + if (mipmapWidth > 1) mipmapWidth /= 2; + if (mipmapHeight > 1) mipmapHeight /= 2; + curSize = computeBlockSize(mipmapWidth, mipmapHeight, 1, d3dFormat); totalSize += mipmapData[i].remaining(); } // OK, create one large ByteBuffer to hold all of the mipmap data totalSize += Header.writtenSize(); - ByteBuffer buf = ByteBuffer.allocate(totalSize); + final ByteBuffer buf = ByteBuffer.allocate(totalSize); buf.position(Header.writtenSize()); for (int i = 0; i < mipmapData.length; i++) { buf.put(mipmapData[i]); } this.buf = buf; - + // Allocate and initialize a Header header = new Header(); header.size = Header.size(); @@ -840,10 +846,10 @@ public class DDSImage { } } - private static int computeCompressedBlockSize(int width, - int height, - int depth, - int compressionFormat) { + private static int computeCompressedBlockSize(final int width, + final int height, + final int depth, + final int compressionFormat) { int blockSize = ((width + 3)/4) * ((height + 3)/4) * ((depth + 3)/4); switch (compressionFormat) { case D3DFMT_DXT1: blockSize *= 8; break; @@ -852,7 +858,33 @@ public class DDSImage { return blockSize; } - private int mipMapWidth(int map) { + private static int computeBlockSize(final int width, + final int height, + final int depth, + final int pixelFormat) { + int blocksize; + switch (pixelFormat) { + case D3DFMT_R8G8B8: + blocksize = width*height*3; + break; + case D3DFMT_A8R8G8B8: + case D3DFMT_X8R8G8B8: + blocksize = width*height*4; + break; + case D3DFMT_DXT1: + case D3DFMT_DXT2: + case D3DFMT_DXT3: + case D3DFMT_DXT4: + case D3DFMT_DXT5: + blocksize = computeCompressedBlockSize(width, height, 1, pixelFormat); + break; + default: + throw new IllegalArgumentException("d3dFormat must be one of the known formats"); + } + return blocksize; + } + + private int mipMapWidth(final int map) { int width = getWidth(); for (int i = 0; i < map; i++) { width >>= 1; @@ -860,7 +892,7 @@ public class DDSImage { return Math.max(width, 1); } - private int mipMapHeight(int map) { + private int mipMapHeight(final int map) { int height = getHeight(); for (int i = 0; i < map; i++) { height >>= 1; @@ -868,11 +900,11 @@ public class DDSImage { return Math.max(height, 1); } - private int mipMapSizeInBytes(int map) { - int width = mipMapWidth(map); - int height = mipMapHeight(map); + private int mipMapSizeInBytes(final int map) { + final int width = mipMapWidth(map); + final int height = mipMapHeight(map); if (isCompressed()) { - int blockSize = (getCompressionFormat() == D3DFMT_DXT1 ? 8 : 16); + final int blockSize = (getCompressionFormat() == D3DFMT_DXT1 ? 8 : 16); return ((width+3)/4)*((height+3)/4)*blockSize; } else { return width * height * (getDepth() / 8); @@ -893,8 +925,8 @@ public class DDSImage { return size; } - private int sideShiftInBytes(int side) { - int[] sides = { + private int sideShiftInBytes(final int side) { + final int[] sides = { DDSCAPS2_CUBEMAP_POSITIVEX, DDSCAPS2_CUBEMAP_NEGATIVEX, DDSCAPS2_CUBEMAP_POSITIVEY, @@ -904,9 +936,9 @@ public class DDSImage { }; int shift = 0; - int sideSize = sideSizeInBytes(); + final int sideSize = sideSizeInBytes(); for (int i = 0; i < sides.length; i++) { - int temp = sides[i]; + final int temp = sides[i]; if ((temp & side) != 0) { return shift; } @@ -917,7 +949,7 @@ public class DDSImage { throw new RuntimeException("Illegal side: " + side); } - private boolean printIfRecognized(PrintStream tty, int flags, int flag, String what) { + private boolean printIfRecognized(final PrintStream tty, final int flags, final int flag, final String what) { if ((flags & flag) != 0) { tty.println(what); return true; diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java new file mode 100644 index 000000000..66a486f9b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/JPEGImage.java @@ -0,0 +1,177 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util.texture.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +import javax.media.opengl.GL; + +import jogamp.opengl.Debug; +import jogamp.opengl.util.jpeg.JPEGDecoder; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.util.texture.TextureData.ColorSpace; + +public class JPEGImage { + private static final boolean DEBUG = Debug.debug("JPEGImage"); + + + /** + * Reads a JPEG image from the specified InputStream, using the given color space for storage. + * + * @param in + * @param cs Storage color space, either {@link ColorSpace#RGB} or {@link ColorSpace#YCbCr}. {@link ColorSpace#YCCK} and {@link ColorSpace#CMYK} will throw an exception! + * @return + * @throws IOException + */ + public static JPEGImage read(final InputStream in, final ColorSpace cs) throws IOException { + return new JPEGImage(in, cs); + } + + /** Reads a JPEG image from the specified InputStream, using the {@link ColorSpace#RGB}. */ + public static JPEGImage read(final InputStream in) throws IOException { + return new JPEGImage(in, ColorSpace.RGB); + } + + private static class JPEGColorSink implements JPEGDecoder.ColorSink { + int width=0, height=0; + int sourceComponents=0; + ColorSpace sourceCS = ColorSpace.YCbCr; + int storageComponents; + final ColorSpace storageCS; + ByteBuffer data = null; + + JPEGColorSink(final ColorSpace storageCM) { + this.storageCS = storageCM; + switch(storageCS) { + case RGB: + case YCbCr: + storageComponents = 3; + break; + default: + throw new IllegalArgumentException("Unsupported storage color-space: "+storageCS); + } + } + + @Override + public final ColorSpace allocate(final int width, final int height, final ColorSpace sourceCM, final int sourceComponents) throws RuntimeException { + this.width = width; + this.height = height; + this.sourceComponents = sourceComponents; + this.sourceCS = sourceCM; + this.data = Buffers.newDirectByteBuffer(width * height * storageComponents); + return storageCS; + } + + @Override + public final void storeRGB(final int x, final int y, final byte r, final byte g, final byte b) { + int i = ( ( height - y - 1 ) * width + x ) * storageComponents; + data.put(i++, r); + data.put(i++, g); + data.put(i++, b); + // data.put(i++, (byte)0xff); + } + + @Override + public final void store2(final int x, final int y, final byte c1, final byte c2) { + throw new RuntimeException("not supported yet"); + } + + @Override + public final void storeYCbCr(final int x, final int y, final byte Y, final byte Cb, final byte Cr) { + int i = ( ( height - y - 1 ) * width + x ) * storageComponents; + data.put(i++, Y); + data.put(i++, Cb); + data.put(i++, Cr); + } + + @Override + public String toString() { + return "JPEGPixels["+width+"x"+height+", sourceComp "+sourceComponents+", sourceCS "+sourceCS+", storageCS "+storageCS+", storageComp "+storageComponents+"]"; + } + }; + + private JPEGImage(final InputStream in, final ColorSpace cs) throws IOException { + pixelStorage = new JPEGColorSink(cs); + final JPEGDecoder decoder = new JPEGDecoder(); + decoder.parse(in); + pixelWidth = decoder.getWidth(); + pixelHeight = decoder.getHeight(); + decoder.getPixel(pixelStorage, pixelWidth, pixelHeight); + data = pixelStorage.data; + final boolean hasAlpha = false; + + bytesPerPixel = 3; + glFormat = GL.GL_RGB; + reversedChannels = false; // RGB[A] + if(DEBUG) { + System.err.println("JPEGImage: alpha "+hasAlpha+", bytesPerPixel "+bytesPerPixel+ + ", pixels "+pixelWidth+"x"+pixelHeight+", glFormat 0x"+Integer.toHexString(glFormat)); + System.err.println("JPEGImage: "+decoder); + System.err.println("JPEGImage: "+pixelStorage); + } + decoder.clear(null); + } + private final JPEGColorSink pixelStorage; + private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel; + private final boolean reversedChannels; + private final ByteBuffer data; + + /** Returns the color space of the pixel data */ + public ColorSpace getColorSpace() { return pixelStorage.storageCS; } + + /** Returns the number of components of the pixel data */ + public int getComponentCount() { return pixelStorage.storageComponents; } + + /** Returns the width of the image. */ + public int getWidth() { return pixelWidth; } + + /** Returns the height of the image. */ + public int getHeight() { return pixelHeight; } + + /** Returns true if data has the channels reversed to BGR or BGRA, otherwise RGB or RGBA is expected. */ + public boolean getHasReversedChannels() { return reversedChannels; } + + /** Returns the OpenGL format for this texture; e.g. GL.GL_LUMINANCE, GL.GL_RGB or GL.GL_RGBA. */ + public int getGLFormat() { return glFormat; } + + /** Returns the OpenGL data type: GL.GL_UNSIGNED_BYTE. */ + public int getGLType() { return GL.GL_UNSIGNED_BYTE; } + + /** Returns the bytes per pixel */ + public int getBytesPerPixel() { return bytesPerPixel; } + + /** Returns the raw data for this texture in the correct + (bottom-to-top) order for calls to glTexImage2D. */ + public ByteBuffer getData() { return data; } + + @Override + public String toString() { return "JPEGImage["+pixelWidth+"x"+pixelHeight+", bytesPerPixel "+bytesPerPixel+", reversedChannels "+reversedChannels+", "+pixelStorage+", "+data+"]"; } +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataInputStream.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataInputStream.java index 37dbc54df..772df7279 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataInputStream.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataInputStream.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -76,118 +76,134 @@ public class LEDataInputStream extends FilterInputStream implements DataInput */ DataInputStream dataIn; - public LEDataInputStream(InputStream in) + public LEDataInputStream(final InputStream in) { super(in); dataIn = new DataInputStream(in); } + @Override public void close() throws IOException { dataIn.close(); // better close as we create it. // this will close underlying as well. } - public synchronized final int read(byte b[]) throws IOException + @Override + public synchronized final int read(final byte b[]) throws IOException { return dataIn.read(b, 0, b.length); } - public synchronized final int read(byte b[], int off, int len) throws IOException + @Override + public synchronized final int read(final byte b[], final int off, final int len) throws IOException { - int rl = dataIn.read(b, off, len); + final int rl = dataIn.read(b, off, len); return rl; } - public final void readFully(byte b[]) throws IOException + @Override + public final void readFully(final byte b[]) throws IOException { dataIn.readFully(b, 0, b.length); } - public final void readFully(byte b[], int off, int len) throws IOException + @Override + public final void readFully(final byte b[], final int off, final int len) throws IOException { dataIn.readFully(b, off, len); } - public final int skipBytes(int n) throws IOException + @Override + public final int skipBytes(final int n) throws IOException { return dataIn.skipBytes(n); } + @Override public final boolean readBoolean() throws IOException { - int ch = dataIn.read(); + final int ch = dataIn.read(); if (ch < 0) throw new EOFException(); return (ch != 0); } + @Override public final byte readByte() throws IOException { - int ch = dataIn.read(); + final int ch = dataIn.read(); if (ch < 0) throw new EOFException(); return (byte)(ch); } + @Override public final int readUnsignedByte() throws IOException { - int ch = dataIn.read(); + final int ch = dataIn.read(); if (ch < 0) throw new EOFException(); return ch; } + @Override public final short readShort() throws IOException { - int ch1 = dataIn.read(); - int ch2 = dataIn.read(); + final int ch1 = dataIn.read(); + final int ch2 = dataIn.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch1 << 0) + (ch2 << 8)); } + @Override public final int readUnsignedShort() throws IOException - { - int ch1 = dataIn.read(); - int ch2 = dataIn.read(); + { + final int ch1 = dataIn.read(); + final int ch2 = dataIn.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 0) + (ch2 << 8); } + @Override public final char readChar() throws IOException { - int ch1 = dataIn.read(); - int ch2 = dataIn.read(); + final int ch1 = dataIn.read(); + final int ch2 = dataIn.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (char)((ch1 << 0) + (ch2 << 8)); } + @Override public final int readInt() throws IOException { - int ch1 = dataIn.read(); - int ch2 = dataIn.read(); - int ch3 = dataIn.read(); - int ch4 = dataIn.read(); + final int ch1 = dataIn.read(); + final int ch2 = dataIn.read(); + final int ch3 = dataIn.read(); + final int ch4 = dataIn.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 0) + (ch2 << 8) + (ch3 << 16) + (ch4 << 24)); } + @Override public final long readLong() throws IOException { - int i1 = readInt(); - int i2 = readInt(); - return ((long)(i1) & 0xFFFFFFFFL) + (i2 << 32); + final int i1 = readInt(); + final int i2 = readInt(); + return (i1 & 0xFFFFFFFFL) + ((long)i2 << 32); } + @Override public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } + @Override public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); @@ -195,29 +211,31 @@ public class LEDataInputStream extends FilterInputStream implements DataInput /** * dont call this it is not implemented. - * @return empty new string + * @return empty new string **/ + @Override public final String readLine() throws IOException { - return new String(); + return ""; } /** * dont call this it is not implemented - * @return empty new string + * @return empty new string **/ + @Override public final String readUTF() throws IOException { - return new String(); + return ""; } /** * dont call this it is not implemented - * @return empty new string + * @return empty new string **/ - public final static String readUTF(DataInput in) throws IOException + public final static String readUTF(final DataInput in) throws IOException { - return new String(); + return ""; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataOutputStream.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataOutputStream.java index e1e1ca924..add177546 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataOutputStream.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/LEDataOutputStream.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -72,72 +72,84 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput */ DataOutputStream dataOut; - public LEDataOutputStream(OutputStream out) + public LEDataOutputStream(final OutputStream out) { super(out); dataOut = new DataOutputStream(out); } + @Override public void close() throws IOException { dataOut.close(); // better close as we create it. // this will close underlying as well. } - public synchronized final void write(byte b[]) throws IOException + @Override + public synchronized final void write(final byte b[]) throws IOException { dataOut.write(b, 0, b.length); } - public synchronized final void write(byte b[], int off, int len) throws IOException + @Override + public synchronized final void write(final byte b[], final int off, final int len) throws IOException { dataOut.write(b, off, len); } - public final void write(int b) throws IOException + @Override + public final void write(final int b) throws IOException { dataOut.write(b); } - public final void writeBoolean(boolean v) throws IOException + @Override + public final void writeBoolean(final boolean v) throws IOException { dataOut.writeBoolean(v); } - public final void writeByte(int v) throws IOException + @Override + public final void writeByte(final int v) throws IOException { dataOut.writeByte(v); } /** Don't call this -- not implemented */ - public final void writeBytes(String s) throws IOException + @Override + public final void writeBytes(final String s) throws IOException { throw new UnsupportedOperationException(); } - public final void writeChar(int v) throws IOException + @Override + public final void writeChar(final int v) throws IOException { dataOut.writeChar(((v >> 8) & 0xff) | ((v & 0xff) << 8)); } /** Don't call this -- not implemented */ - public final void writeChars(String s) throws IOException + @Override + public final void writeChars(final String s) throws IOException { throw new UnsupportedOperationException(); } - public final void writeDouble(double v) throws IOException + @Override + public final void writeDouble(final double v) throws IOException { writeLong(Double.doubleToRawLongBits(v)); } - public final void writeFloat(float v) throws IOException + @Override + public final void writeFloat(final float v) throws IOException { writeInt(Float.floatToRawIntBits(v)); } - public final void writeInt(int v) throws IOException + @Override + public final void writeInt(final int v) throws IOException { dataOut.writeInt((v >>> 24) | ((v >>> 8) & 0xff00) | @@ -145,20 +157,23 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput (v << 24)); } - public final void writeLong(long v) throws IOException + @Override + public final void writeLong(final long v) throws IOException { writeInt((int) v); writeInt((int) (v >>> 32)); } - public final void writeShort(int v) throws IOException + @Override + public final void writeShort(final int v) throws IOException { dataOut.writeShort(((v >> 8) & 0xff) | ((v & 0xff) << 8)); } /** Don't call this -- not implemented */ - public final void writeUTF(String s) throws IOException + @Override + public final void writeUTF(final String s) throws IOException { throw new UnsupportedOperationException(); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java index cd42a1157..461ddceb8 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/NetPbmTextureWriter.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -63,7 +63,7 @@ public class NetPbmTextureWriter implements TextureWriter { * magic 7 - PAM binary RGB or RGBA * </pre> */ - public NetPbmTextureWriter(int magic) { + public NetPbmTextureWriter(final int magic) { switch(magic) { case 0: case 6: @@ -84,11 +84,12 @@ public class NetPbmTextureWriter implements TextureWriter { public String getSuffix() { return (magic==6)?PPM:PAM; } - public boolean write(File file, TextureData data) throws IOException { + @Override + public boolean write(final File file, final TextureData data) throws IOException { boolean res; final int magic_old = magic; - - // file suffix selection + + // file suffix selection if (0==magic) { if (PPM.equals(IOUtil.getFileSuffix(file))) { magic = 6; @@ -97,7 +98,7 @@ public class NetPbmTextureWriter implements TextureWriter { } else { return false; } - } + } try { res = writeImpl(file, data); } finally { @@ -105,30 +106,30 @@ public class NetPbmTextureWriter implements TextureWriter { } return res; } - - private boolean writeImpl(File file, TextureData data) throws IOException { + + private boolean writeImpl(final File file, final TextureData data) throws IOException { int pixelFormat = data.getPixelFormat(); final int pixelType = data.getPixelType(); if ((pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA || - pixelFormat == GL2.GL_BGR || + pixelFormat == GL2GL3.GL_BGR || pixelFormat == GL.GL_BGRA ) && (pixelType == GL.GL_BYTE || pixelType == GL.GL_UNSIGNED_BYTE)) { - + ByteBuffer buf = (ByteBuffer) data.getBuffer(); if (null == buf ) { buf = (ByteBuffer) data.getMipmapData()[0]; } buf.rewind(); - - int comps = ( pixelFormat == GL.GL_RGBA || pixelFormat == GL.GL_BGRA ) ? 4 : 3 ; - - if( pixelFormat == GL2.GL_BGR || pixelFormat == GL.GL_BGRA ) { + + final int comps = ( pixelFormat == GL.GL_RGBA || pixelFormat == GL.GL_BGRA ) ? 4 : 3 ; + + if( pixelFormat == GL2GL3.GL_BGR || pixelFormat == GL.GL_BGRA ) { // Must reverse order of red and blue channels to get correct results for (int i = 0; i < buf.remaining(); i += comps) { - byte red = buf.get(i + 0); - byte blue = buf.get(i + 2); + final byte red = buf.get(i + 0); + final byte blue = buf.get(i + 2); buf.put(i + 0, blue); buf.put(i + 2, red); } @@ -140,9 +141,9 @@ public class NetPbmTextureWriter implements TextureWriter { throw new IOException("NetPbmTextureWriter magic 6 (PPM) doesn't RGBA pixel format, use magic 7 (PAM)"); } - FileOutputStream fos = IOUtil.getFileOutputStream(file, true); - - StringBuilder header = new StringBuilder(); + final FileOutputStream fos = IOUtil.getFileOutputStream(file, true); + + final StringBuilder header = new StringBuilder(); header.append("P"); header.append(magic); header.append("\n"); @@ -171,8 +172,8 @@ public class NetPbmTextureWriter implements TextureWriter { } fos.write(header.toString().getBytes()); - - FileChannel fosc = fos.getChannel(); + + final FileChannel fosc = fos.getChannel(); fosc.write(buf); fosc.force(true); fosc.close(); @@ -180,7 +181,7 @@ public class NetPbmTextureWriter implements TextureWriter { buf.rewind(); return true; - } + } throw new IOException("NetPbmTextureWriter writer doesn't support this pixel format / type (only GL_RGB/A + bytes)"); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java deleted file mode 100644 index d8d6f7daa..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.jogamp.opengl.util.texture.spi; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; - -import javax.media.opengl.GL; - -import jogamp.opengl.util.pngj.ImageInfo; -import jogamp.opengl.util.pngj.ImageLine; -import jogamp.opengl.util.pngj.PngReader; -import jogamp.opengl.util.pngj.PngWriter; -import jogamp.opengl.util.pngj.chunks.PngChunkTextVar; - -import com.jogamp.common.nio.Buffers; -import com.jogamp.common.util.IOUtil; - - -public class PNGImage { - /** Creates a PNGImage from data supplied by the end user. Shares - data with the passed ByteBuffer. Assumes the data is already in - the correct byte order for writing to disk, i.e., LUMINANCE, RGB or RGBA bottom-to-top (OpenGL coord). */ - public static PNGImage createFromData(int width, int height, double dpiX, double dpiY, - int bytesPerPixel, boolean reversedChannels, ByteBuffer data) { - return new PNGImage(width, height, dpiX, dpiY, bytesPerPixel, reversedChannels, data); - } - - /** Reads a PNG image from the specified InputStream. */ - public static PNGImage read(InputStream in) throws IOException { - return new PNGImage(in); - } - - /** Reverse read and store, implicitly flip image to GL coords. */ - private static final int getPixelRGBA8(ByteBuffer d, int dOff, ImageLine line, int lineOff, boolean hasAlpha) { - if(hasAlpha) { - d.put(dOff--, (byte)line.scanline[lineOff + 3]); // A - } - d.put(dOff--, (byte)line.scanline[lineOff + 2]); // B - d.put(dOff--, (byte)line.scanline[lineOff + 1]); // G - d.put(dOff--, (byte)line.scanline[lineOff ]); // R - return dOff; - } - /** Reverse read and store, implicitly flip image from GL coords. Handle reversed channels (BGR[A])*/ - private static int setPixelRGBA8(ImageLine line, int lineOff, ByteBuffer d, int dOff, boolean hasAlpha, boolean reversedChannels) { - if(reversedChannels) { - line.scanline[lineOff ] = d.get(dOff--); // R, A - line.scanline[lineOff + 1] = d.get(dOff--); // G, B - line.scanline[lineOff + 2] = d.get(dOff--); // B, G - if(hasAlpha) { - line.scanline[lineOff + 3] = d.get(dOff--);// R - } - } else { - if(hasAlpha) { - line.scanline[lineOff + 3] = d.get(dOff--); // A - } - line.scanline[lineOff + 2] = d.get(dOff--); // B - line.scanline[lineOff + 1] = d.get(dOff--); // G - line.scanline[lineOff ] = d.get(dOff--); // R - } - return dOff; - } - - private PNGImage(int width, int height, double dpiX, double dpiY, int bytesPerPixel, boolean reversedChannels, ByteBuffer data) { - pixelWidth=width; - pixelHeight=height; - dpi = new double[] { dpiX, dpiY }; - if(4 == bytesPerPixel) { - glFormat = GL.GL_RGBA; - } else if (3 == bytesPerPixel) { - glFormat = GL.GL_RGB; - } else { - throw new InternalError("XXX: bytesPerPixel "+bytesPerPixel); - } - this.bytesPerPixel = bytesPerPixel; - this.reversedChannels = reversedChannels; - this.data = data; - } - - private PNGImage(InputStream in) { - final PngReader pngr = new PngReader(new BufferedInputStream(in), null); - final int channels = pngr.imgInfo.channels; - if ( ! ( 1 == channels || 3 == channels || 4 == channels ) ) { - throw new RuntimeException("PNGImage can only handle Lum/RGB/RGBA [1/3/4 channels] images for now. Channels "+channels); - } - bytesPerPixel=pngr.imgInfo.bytesPixel; - if ( ! ( 1 == bytesPerPixel || 3 == bytesPerPixel || 4 == bytesPerPixel ) ) { - throw new RuntimeException("PNGImage can only handle Lum/RGB/RGBA [1/3/4 bpp] images for now. BytesPerPixel "+bytesPerPixel); - } - if(channels != bytesPerPixel) { - throw new RuntimeException("PNGImage currently only handles Channels [1/3/4] == BytePerPixel [1/3/4], channels: "+channels+", bytesPerPixel "+bytesPerPixel); - } - pixelWidth=pngr.imgInfo.cols; - pixelHeight=pngr.imgInfo.rows; - dpi = new double[2]; - { - final double[] dpi2 = pngr.getMetadata().getDpi(); - dpi[0]=dpi2[0]; - dpi[1]=dpi2[1]; - } - switch(channels) { - case 1: glFormat = GL.GL_LUMINANCE; break; - case 3: glFormat = GL.GL_RGB; break; - case 4: glFormat = GL.GL_RGBA; break; - default: throw new InternalError("XXX: channels: "+channels+", bytesPerPixel "+bytesPerPixel); - } - data = Buffers.newDirectByteBuffer(bytesPerPixel * pixelWidth * pixelHeight); - reversedChannels = false; // RGB[A] - final boolean hasAlpha = 4 == channels; - int dataOff = bytesPerPixel * pixelWidth * pixelHeight - 1; // start at end-of-buffer, reverse store - for (int row = 0; row < pixelHeight; row++) { - final ImageLine l1 = pngr.readRow(row); - int lineOff = ( pixelWidth - 1 ) * bytesPerPixel ; // start w/ last pixel in line, reverse read - if(1 == channels) { - for (int j = pixelWidth - 1; j >= 0; j--) { - data.put(dataOff--, (byte)l1.scanline[lineOff--]); // Luminance, 1 bytesPerPixel - } - } else { - for (int j = pixelWidth - 1; j >= 0; j--) { - dataOff = getPixelRGBA8(data, dataOff, l1, lineOff, hasAlpha); - lineOff -= bytesPerPixel; - } - } - } - pngr.end(); - } - private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel; - private boolean reversedChannels; - private final double[] dpi; - private final ByteBuffer data; - - /** Returns the width of the image. */ - public int getWidth() { return pixelWidth; } - - /** Returns the height of the image. */ - public int getHeight() { return pixelHeight; } - - /** Returns true if data has the channels reversed to BGR or BGRA, otherwise RGB or RGBA is expected. */ - public boolean getHasReversedChannels() { return reversedChannels; } - - /** Returns the dpi of the image. */ - public double[] getDpi() { return dpi; } - - /** Returns the OpenGL format for this texture; e.g. GL.GL_BGR or GL.GL_BGRA. */ - public int getGLFormat() { return glFormat; } - - /** Returns the OpenGL data type: GL.GL_UNSIGNED_BYTE. */ - public int getGLType() { return GL.GL_UNSIGNED_BYTE; } - - /** Returns the bytes per pixel */ - public int getBytesPerPixel() { return bytesPerPixel; } - - /** Returns the raw data for this texture in the correct - (bottom-to-top) order for calls to glTexImage2D. */ - public ByteBuffer getData() { return data; } - - public void write(File out, boolean allowOverwrite) throws IOException { - final ImageInfo imi = new ImageInfo(pixelWidth, pixelHeight, 8, (4 == bytesPerPixel) ? true : false); // 8 bits per channel, no alpha - // open image for writing to a output stream - final OutputStream outs = new BufferedOutputStream(IOUtil.getFileOutputStream(out, allowOverwrite)); - try { - final PngWriter png = new PngWriter(outs, imi); - // add some optional metadata (chunks) - png.getMetadata().setDpi(dpi[0], dpi[1]); - png.getMetadata().setTimeNow(0); // 0 seconds fron now = now - png.getMetadata().setText(PngChunkTextVar.KEY_Title, "JogAmp PNGImage"); - // png.getMetadata().setText("my key", "my text"); - final boolean hasAlpha = 4 == bytesPerPixel; - final ImageLine l1 = new ImageLine(imi); - int dataOff = bytesPerPixel * pixelWidth * pixelHeight - 1; // start at end-of-buffer, reverse read - for (int row = 0; row < pixelHeight; row++) { - int lineOff = ( pixelWidth - 1 ) * bytesPerPixel ; // start w/ last pixel in line, reverse store - if(1 == bytesPerPixel) { - for (int j = pixelWidth - 1; j >= 0; j--) { - l1.scanline[lineOff--] = data.get(dataOff--); // // Luminance, 1 bytesPerPixel - } - } else { - for (int j = pixelWidth - 1; j >= 0; j--) { - dataOff = setPixelRGBA8(l1, lineOff, data, dataOff, hasAlpha, reversedChannels); - lineOff -= bytesPerPixel; - } - } - png.writeRow(l1, row); - } - png.end(); - } finally { - IOUtil.close(outs, false); - } - } - - public String toString() { return "PNGImage["+pixelWidth+"x"+pixelHeight+", dpi "+dpi[0]+" x "+dpi[1]+", bytesPerPixel "+bytesPerPixel+", reversedChannels "+reversedChannels+", "+data+"]"; } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java index d35330f58..27549dfe3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/SGIImage.java @@ -1,21 +1,21 @@ /* * Portions Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -54,7 +54,7 @@ import com.jogamp.common.util.IOUtil; */ public class SGIImage { - private Header header; + private final Header header; private int format; private byte[] data; // Used for decoding RLE-compressed images @@ -72,15 +72,15 @@ public class SGIImage { byte storage; // Storage format // 0 for uncompressed // 1 for RLE compression - byte bpc; // Number of bytes per pixel channel + byte bpc; // Number of bytes per pixel channel // Legally 1 or 2 short dimension; // Number of dimensions // Legally 1, 2, or 3 // 1 means a single row, XSIZE long // 2 means a single 2D image // 3 means multiple 2D images - short xsize; // X size in pixels - short ysize; // Y size in pixels + short xsize; // X size in pixels + short ysize; // Y size in pixels short zsize; // Number of channels // 1 indicates greyscale // 3 indicates RGB @@ -105,7 +105,7 @@ public class SGIImage { magic = MAGIC; } - Header(DataInputStream in) throws IOException { + Header(final DataInputStream in) throws IOException { magic = in.readShort(); storage = in.readByte(); bpc = in.readByte(); @@ -116,16 +116,17 @@ public class SGIImage { pixmin = in.readInt(); pixmax = in.readInt(); dummy = in.readInt(); - byte[] tmpname = new byte[80]; + final byte[] tmpname = new byte[80]; in.read(tmpname); int numChars = 0; while (tmpname[numChars++] != 0); imagename = new String(tmpname, 0, numChars); colormap = in.readInt(); - byte[] tmp = new byte[404]; + final byte[] tmp = new byte[404]; in.read(tmp); } + @Override public String toString() { return ("magic: " + magic + " storage: " + (int) storage + @@ -141,21 +142,21 @@ public class SGIImage { } } - private SGIImage(Header header) { + private SGIImage(final Header header) { this.header = header; } /** Reads an SGI image from the specified file. */ - public static SGIImage read(String filename) throws IOException { + public static SGIImage read(final String filename) throws IOException { return read(new FileInputStream(filename)); } /** Reads an SGI image from the specified InputStream. */ - public static SGIImage read(InputStream in) throws IOException { - DataInputStream dIn = new DataInputStream(new BufferedInputStream(in)); + public static SGIImage read(final InputStream in) throws IOException { + final DataInputStream dIn = new DataInputStream(new BufferedInputStream(in)); - Header header = new Header(dIn); - SGIImage res = new SGIImage(header); + final Header header = new Header(dIn); + final SGIImage res = new SGIImage(header); res.decodeImage(dIn); return res; } @@ -163,28 +164,28 @@ public class SGIImage { /** Writes this SGIImage to the specified file name. If flipVertically is set, outputs the scanlines from top to bottom rather than the default bottom to top order. */ - public void write(String filename, boolean flipVertically) throws IOException { + public void write(final String filename, final boolean flipVertically) throws IOException { write(new File(filename), flipVertically); } /** Writes this SGIImage to the specified file. If flipVertically is set, outputs the scanlines from top to bottom rather than the default bottom to top order. */ - public void write(File file, boolean flipVertically) throws IOException { + public void write(final File file, final boolean flipVertically) throws IOException { writeImage(file, data, header.xsize, header.ysize, header.zsize, flipVertically); } /** Creates an SGIImage from the specified data in either RGB or RGBA format. */ - public static SGIImage createFromData(int width, - int height, - boolean hasAlpha, - byte[] data) { - Header header = new Header(); + public static SGIImage createFromData(final int width, + final int height, + final boolean hasAlpha, + final byte[] data) { + final Header header = new Header(); header.xsize = (short) width; header.ysize = (short) height; header.zsize = (short) (hasAlpha ? 4 : 3); - SGIImage image = new SGIImage(header); + final SGIImage image = new SGIImage(header); image.data = data; return image; } @@ -200,9 +201,9 @@ public class SGIImage { if (!in.markSupported()) { throw new IOException("Can not test non-destructively whether given InputStream is an SGI RGB image"); } - DataInputStream dIn = new DataInputStream(in); + final DataInputStream dIn = new DataInputStream(in); dIn.mark(4); - short magic = dIn.readShort(); + final short magic = dIn.readShort(); dIn.reset(); return (magic == MAGIC); } @@ -226,6 +227,7 @@ public class SGIImage { (bottom-to-top) order for calls to glTexImage2D. */ public byte[] getData() { return data; } + @Override public String toString() { return header.toString(); } @@ -233,11 +235,11 @@ public class SGIImage { //---------------------------------------------------------------------- // Internals only below this point // - - private void decodeImage(DataInputStream in) throws IOException { + + private void decodeImage(final DataInputStream in) throws IOException { if (header.storage == 1) { // Read RLE compression data; row starts and sizes - int x = header.ysize * header.zsize; + final int x = header.ysize * header.zsize; rowStart = new int[x]; rowSize = new int[x]; rleEnd = 4 * 2 * x + 512; @@ -251,16 +253,16 @@ public class SGIImage { } tmpData = readAll(in); - int xsize = header.xsize; - int ysize = header.ysize; - int zsize = header.zsize; + final int xsize = header.xsize; + final int ysize = header.ysize; + final int zsize = header.zsize; int lptr = 0; data = new byte[xsize * ysize * 4]; - byte[] rbuf = new byte[xsize]; - byte[] gbuf = new byte[xsize]; - byte[] bbuf = new byte[xsize]; - byte[] abuf = new byte[xsize]; + final byte[] rbuf = new byte[xsize]; + final byte[] gbuf = new byte[xsize]; + final byte[] bbuf = new byte[xsize]; + final byte[] abuf = new byte[xsize]; for (int y = 0; y < ysize; y++) { if (zsize >= 4) { getRow(rbuf, y, 0); @@ -291,15 +293,15 @@ public class SGIImage { header.zsize = 4; } - private void getRow(byte[] buf, int y, int z) { + private void getRow(final byte[] buf, final int y, final int z) { if (header.storage == 1) { - int offs = rowStart[y + z * header.ysize] - rleEnd; + final int offs = rowStart[y + z * header.ysize] - rleEnd; System.arraycopy(tmpData, offs, tmpRead, 0, rowSize[y + z * header.ysize]); int iPtr = 0; int oPtr = 0; for (;;) { byte pixel = tmpRead[iPtr++]; - int count = (int) (pixel & 0x7F); + int count = pixel & 0x7F; if (count == 0) { return; } @@ -315,12 +317,12 @@ public class SGIImage { } } } else { - int offs = (y * header.xsize) + (z * header.xsize * header.ysize); + final int offs = (y * header.xsize) + (z * header.xsize * header.ysize); System.arraycopy(tmpData, offs, buf, 0, header.xsize); } } - private void bwtorgba(byte[] b, byte[] dest, int lptr) { + private void bwtorgba(final byte[] b, final byte[] dest, final int lptr) { for (int i = 0; i < b.length; i++) { dest[4 * i + lptr + 0] = b[i]; dest[4 * i + lptr + 1] = b[i]; @@ -329,7 +331,7 @@ public class SGIImage { } } - private void latorgba(byte[] b, byte[] a, byte[] dest, int lptr) { + private void latorgba(final byte[] b, final byte[] a, final byte[] dest, final int lptr) { for (int i = 0; i < b.length; i++) { dest[4 * i + lptr + 0] = b[i]; dest[4 * i + lptr + 1] = b[i]; @@ -338,7 +340,7 @@ public class SGIImage { } } - private void rgbtorgba(byte[] r, byte[] g, byte[] b, byte[] dest, int lptr) { + private void rgbtorgba(final byte[] r, final byte[] g, final byte[] b, final byte[] dest, final int lptr) { for (int i = 0; i < b.length; i++) { dest[4 * i + lptr + 0] = r[i]; dest[4 * i + lptr + 1] = g[i]; @@ -347,7 +349,7 @@ public class SGIImage { } } - private void rgbatorgba(byte[] r, byte[] g, byte[] b, byte[] a, byte[] dest, int lptr) { + private void rgbatorgba(final byte[] r, final byte[] g, final byte[] b, final byte[] a, final byte[] dest, final int lptr) { for (int i = 0; i < b.length; i++) { dest[4 * i + lptr + 0] = r[i]; dest[4 * i + lptr + 1] = g[i]; @@ -356,19 +358,19 @@ public class SGIImage { } } - private static byte imgref(byte[] i, - int x, - int y, - int z, - int xs, - int ys, - int zs) { + private static byte imgref(final byte[] i, + final int x, + final int y, + final int z, + final int xs, + final int ys, + final int zs) { return i[(xs*ys*z)+(xs*y)+x]; } - private void writeHeader(DataOutputStream stream, - int xsize, int ysize, int zsize, boolean rle) throws IOException { + private void writeHeader(final DataOutputStream stream, + final int xsize, final int ysize, final int zsize, final boolean rle) throws IOException { // effects: outputs the 512-byte IRIS RGB header to STREAM, using xsize, // ysize, and depth as the dimensions of the image. NOTE that // the following defaults are used: @@ -413,14 +415,14 @@ public class SGIImage { stream.write(0); } - private void writeImage(File file, + private void writeImage(final File file, byte[] data, - int xsize, - int ysize, - int zsize, - boolean yflip) throws IOException { + final int xsize, + final int ysize, + final int zsize, + final boolean yflip) throws IOException { // Input data is in RGBRGBRGB or RGBARGBARGBA format; first unswizzle it - byte[] tmpData = new byte[xsize * ysize * zsize]; + final byte[] tmpData = new byte[xsize * ysize * zsize]; int dest = 0; for (int i = 0; i < zsize; i++) { for (int j = i; j < (xsize * ysize * zsize); j += zsize) { @@ -445,8 +447,8 @@ public class SGIImage { // x axis). // Build the offset tables - int[] starttab = new int[ysize * zsize]; - int[] lengthtab = new int[ysize * zsize]; + final int[] starttab = new int[ysize * zsize]; + final int[] lengthtab = new int[ysize * zsize]; // Temporary buffer for holding RLE data. // Note that this makes the assumption that RLE-compressed data will @@ -457,8 +459,8 @@ public class SGIImage { // empirical evidence here; the break-even point seems to be a look- // ahead of 3. (That is, if the three values following this one are all // the same as the current value, switch to repeat mode.) - int lookahead = 3; - byte[] rlebuf = new byte[2 * xsize * ysize * zsize]; + final int lookahead = 3; + final byte[] rlebuf = new byte[2 * xsize * ysize * zsize]; int cur_loc = 0; // current offset location. int ptr = 0; @@ -473,20 +475,20 @@ public class SGIImage { yincr = -1; } - boolean DEBUG = false; + final boolean DEBUG = false; for (int z = 0; z < zsize; z++) { for (int y = ystart; y != yend; y += yincr) { // RLE-compress each row. - + int x = 0; byte count = 0; boolean repeat_mode = false; boolean should_switch = false; - int start_ptr = ptr; + final int start_ptr = ptr; int num_ptr = ptr++; byte repeat_val = 0; - + while (x < xsize) { // see if we should switch modes should_switch = false; @@ -503,7 +505,7 @@ public class SGIImage { if (DEBUG) System.err.println("left side was " + ((int) imgref(data, x, y, z, xsize, ysize, zsize)) + ", right side was " + (int)imgref(data, x+i, y, z, xsize, ysize, zsize)); - + if (imgref(data, x, y, z, xsize, ysize, zsize) != imgref(data, x+i, y, z, xsize, ysize, zsize)) should_switch = false; @@ -531,7 +533,7 @@ public class SGIImage { repeat_mode = true; repeat_val = imgref(data, x, y, z, xsize, ysize, zsize); } - + if (x > 0) { // reset the number pointer num_ptr = ptr++; @@ -539,7 +541,7 @@ public class SGIImage { count = 0; } } - + // if not in repeat mode, copy element to ptr if (!repeat_mode) { rlebuf[ptr++] = imgref(data, x, y, z, xsize, ysize, zsize); @@ -564,7 +566,7 @@ public class SGIImage { x++; } // output this row's length into the length table - int rowlen = ptr - start_ptr; + final int rowlen = ptr - start_ptr; if (yflip) lengthtab[ysize*z+(ysize-y-1)] = rowlen; else @@ -581,15 +583,15 @@ public class SGIImage { // Now we have the offset tables computed, as well as the RLE data. // Output this information to the file. total_size = ptr; - - if (DEBUG) + + if (DEBUG) System.err.println("total_size was " + total_size); - DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(IOUtil.getFileOutputStream(file, true))); + final DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(IOUtil.getFileOutputStream(file, true))); writeHeader(stream, xsize, ysize, zsize, true); - int SIZEOF_INT = 4; + final int SIZEOF_INT = 4; for (int i = 0; i < (ysize * zsize); i++) stream.writeInt(starttab[i] + 512 + (2 * ysize * zsize * SIZEOF_INT)); for (int i = 0; i < (ysize * zsize); i++) @@ -600,18 +602,18 @@ public class SGIImage { stream.close(); } - private byte[] readAll(DataInputStream in) throws IOException { + private byte[] readAll(final DataInputStream in) throws IOException { byte[] dest = new byte[16384]; int pos = 0; int numRead = 0; - + boolean done = false; do { numRead = in.read(dest, pos, dest.length - pos); if (pos == dest.length) { // Resize destination buffer - byte[] newDest = new byte[2 * dest.length]; + final byte[] newDest = new byte[2 * dest.length]; System.arraycopy(dest, 0, newDest, 0, pos); dest = newDest; } @@ -624,7 +626,7 @@ public class SGIImage { // Trim destination buffer if (pos != dest.length) { - byte[] finalDest = new byte[pos]; + final byte[] finalDest = new byte[pos]; System.arraycopy(dest, 0, finalDest, 0, pos); dest = finalDest; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java index e202c59b7..28823abb3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TGAImage.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -71,12 +71,12 @@ import com.jogamp.common.util.IOUtil; */ public class TGAImage { - private Header header; + private final Header header; private int format; private int bpp; private ByteBuffer data; - private TGAImage(Header header) { + private TGAImage(final Header header) { this.header = header; } @@ -114,7 +114,7 @@ public class TGAImage { public final static int I_FOURWAY = 2; /** Type of this TGA file format */ - private int tgaType; + private final int tgaType; /** initial TGA image data fields */ private int idLength; // byte value @@ -142,13 +142,13 @@ public class TGAImage { tgaType = TYPE_OLD; // dont try and get footer. } - Header(LEDataInputStream in) throws IOException { + Header(final LEDataInputStream in) throws IOException { tgaType = TYPE_OLD; // dont try and get footer. // initial header fields - idLength = in.readUnsignedByte(); + idLength = in.readUnsignedByte(); colorMapType = in.readUnsignedByte(); - imageType = in.readUnsignedByte(); + imageType = in.readUnsignedByte(); // color map header fields firstEntryIndex = in.readUnsignedShort(); @@ -199,6 +199,7 @@ public class TGAImage { public byte[] imageIDbuf() { return imageIDbuf; } public String imageID() { return imageID; } + @Override public String toString() { return "TGA Header " + " id length: " + idLength + @@ -219,24 +220,24 @@ public class TGAImage { public int size() { return 18 + idLength; } // buf must be in little-endian byte order - private void write(ByteBuffer buf) { + private void write(final ByteBuffer buf) { buf.put((byte) idLength); buf.put((byte) colorMapType); buf.put((byte) imageType); buf.putShort((short) firstEntryIndex); buf.putShort((short) colorMapLength); - buf.put((byte) colorMapEntrySize); + buf.put(colorMapEntrySize); buf.putShort((short) xOrigin); buf.putShort((short) yOrigin); buf.putShort((short) width); buf.putShort((short) height); - buf.put((byte) pixelDepth); - buf.put((byte) imageDescriptor); + buf.put(pixelDepth); + buf.put(imageDescriptor); if (idLength > 0) { try { - byte[] chars = imageID.getBytes("US-ASCII"); + final byte[] chars = imageID.getBytes("US-ASCII"); buf.put(chars); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { throw new RuntimeException(e); } } @@ -249,7 +250,7 @@ public class TGAImage { * it into the JimiImage structure. This was taken from the * prototype and modified for the new Jimi structure */ - private void decodeImage(GLProfile glp, LEDataInputStream dIn) throws IOException { + private void decodeImage(final GLProfile glp, final LEDataInputStream dIn) throws IOException { switch (header.imageType()) { case Header.UCOLORMAPPED: throw new IOException("TGADecoder Uncompressed Colormapped images not supported"); @@ -273,23 +274,34 @@ public class TGAImage { throw new IOException("TGADecoder Compressed Colormapped images not supported"); case Header.TRUECOLOR: - throw new IOException("TGADecoder Compressed True Color images not supported"); + switch (header.pixelDepth) { + case 16: + throw new IOException("TGADecoder Compressed 16-bit True Color images not supported"); + + case 24: + case 32: + decodeRGBImageRLE24_32(glp, dIn); + break; + } + break; case Header.BLACKWHITE: throw new IOException("TGADecoder Compressed Grayscale images not supported"); } } - + /** * This assumes that the body is for a 24 bit or 32 bit for a * RGB or ARGB image respectively. */ - private void decodeRGBImageU24_32(GLProfile glp, LEDataInputStream dIn) throws IOException { + private void decodeRGBImageU24_32(final GLProfile glp, final LEDataInputStream dIn) throws IOException { + setupImage24_32(glp); + int i; // row index int y; // output row index - int rawWidth = header.width() * (header.pixelDepth() / 8); - byte[] rawBuf = new byte[rawWidth]; - byte[] tmpData = new byte[rawWidth * header.height()]; + final int rawWidth = header.width() * bpp; + final byte[] rawBuf = new byte[rawWidth]; + final byte[] tmpData = new byte[rawWidth * header.height()]; for (i = 0; i < header.height(); ++i) { dIn.readFully(rawBuf, 0, rawWidth); @@ -302,34 +314,60 @@ public class TGAImage { System.arraycopy(rawBuf, 0, tmpData, y * rawWidth, rawBuf.length); } - if (header.pixelDepth() == 24) { - bpp=3; - if(glp.isGL2GL3()) { - format = GL2GL3.GL_BGR; - } else { - format = GL.GL_RGB; - swapBGR(tmpData, rawWidth, header.height(), bpp); - } - } else { - assert header.pixelDepth() == 32; - bpp=4; + if(format == GL.GL_RGB || format == GL.GL_RGBA) + swapBGR(tmpData, rawWidth, header.height(), bpp); + data = ByteBuffer.wrap(tmpData); + } + + /** + * This assumes that the body is for a 24 bit or 32 bit for a + * RGB or ARGB image respectively. + */ + private void decodeRGBImageRLE24_32(final GLProfile glp, final LEDataInputStream dIn) throws IOException { + setupImage24_32(glp); + + final byte[] pixel = new byte[bpp]; + final int rawWidth = header.width() * bpp; + final byte[] tmpData = new byte[rawWidth * header.height()]; + int i = 0, j; + int packet, len; + while (i < tmpData.length) { + packet = dIn.readUnsignedByte(); + len = (packet & 0x7F) + 1; + if ((packet & 0x80) != 0) { + dIn.read(pixel); + for (j = 0; j < len; ++j) + System.arraycopy(pixel, 0, tmpData, i + j * bpp, bpp); + } else + dIn.read(tmpData, i, len * bpp); + i += bpp * len; + } + + if(format == GL.GL_RGB || format == GL.GL_RGBA) + swapBGR(tmpData, rawWidth, header.height(), bpp); + data = ByteBuffer.wrap(tmpData); + } + + private void setupImage24_32(final GLProfile glp) { + bpp = header.pixelDepth / 8; + switch (header.pixelDepth) { + case 24: + format = glp.isGL2GL3() ? GL2GL3.GL_BGR : GL.GL_RGB; + break; + case 32: boolean useBGRA = glp.isGL2GL3(); if(!useBGRA) { final GLContext ctx = GLContext.getCurrent(); useBGRA = null != ctx && ctx.isTextureFormatBGRA8888Available(); } - if( useBGRA ) { - format = GL.GL_BGRA; - } else { - format = GL.GL_RGBA; - swapBGR(tmpData, rawWidth, header.height(), bpp); - } + format = useBGRA ? GL.GL_BGRA : GL.GL_RGBA; + break; + default: + assert false; } - - data = ByteBuffer.wrap(tmpData); } - private static void swapBGR(byte[] data, int bWidth, int height, int bpp) { + private static void swapBGR(final byte[] data, final int bWidth, final int height, final int bpp) { byte r,b; int k; for(int i=0; i<height; ++i) { @@ -360,30 +398,30 @@ public class TGAImage { public ByteBuffer getData() { return data; } /** Reads a Targa image from the specified file. */ - public static TGAImage read(GLProfile glp, String filename) throws IOException { + public static TGAImage read(final GLProfile glp, final String filename) throws IOException { return read(glp, new FileInputStream(filename)); } /** Reads a Targa image from the specified InputStream. */ - public static TGAImage read(GLProfile glp, InputStream in) throws IOException { - LEDataInputStream dIn = new LEDataInputStream(new BufferedInputStream(in)); + public static TGAImage read(final GLProfile glp, final InputStream in) throws IOException { + final LEDataInputStream dIn = new LEDataInputStream(new BufferedInputStream(in)); - Header header = new Header(dIn); - TGAImage res = new TGAImage(header); + final Header header = new Header(dIn); + final TGAImage res = new TGAImage(header); res.decodeImage(glp, dIn); return res; } /** Writes the image in Targa format to the specified file name. */ - public void write(String filename) throws IOException { + public void write(final String filename) throws IOException { write(new File(filename)); } /** Writes the image in Targa format to the specified file. */ - public void write(File file) throws IOException { - FileOutputStream stream = IOUtil.getFileOutputStream(file, true); - FileChannel chan = stream.getChannel(); - ByteBuffer buf = ByteBuffer.allocate(header.size()); + public void write(final File file) throws IOException { + final FileOutputStream stream = IOUtil.getFileOutputStream(file, true); + final FileChannel chan = stream.getChannel(); + final ByteBuffer buf = ByteBuffer.allocate(header.size()); buf.order(ByteOrder.LITTLE_ENDIAN); header.write(buf); buf.rewind(); @@ -399,19 +437,19 @@ public class TGAImage { data with the passed ByteBuffer. Assumes the data is already in the correct byte order for writing to disk, i.e., BGR or BGRA. */ - public static TGAImage createFromData(int width, - int height, - boolean hasAlpha, - boolean topToBottom, - ByteBuffer data) { - Header header = new Header(); + public static TGAImage createFromData(final int width, + final int height, + final boolean hasAlpha, + final boolean topToBottom, + final ByteBuffer data) { + final Header header = new Header(); header.imageType = Header.UTRUECOLOR; header.width = width; header.height = height; header.pixelDepth = (byte) (hasAlpha ? 32 : 24); header.imageDescriptor = (byte) (topToBottom ? Header.ID_TOPTOBOTTOM : 0); // Note ID not supported - TGAImage ret = new TGAImage(header); + final TGAImage ret = new TGAImage(header); ret.data = data; return ret; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureProvider.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureProvider.java index 88018edbe..0299531b1 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureProvider.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureProvider.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureWriter.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureWriter.java index 55527cef5..35b8efa72 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureWriter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/TextureWriter.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureProvider.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureProvider.java index 6e2f1b992..4174adf52 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureProvider.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureProvider.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -54,12 +54,13 @@ import com.jogamp.opengl.util.texture.spi.*; public class IIOTextureProvider implements TextureProvider { private static final boolean DEBUG = Debug.debug("TextureIO"); - public TextureData newTextureData(GLProfile glp, File file, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - BufferedImage img = ImageIO.read(file); + @Override + public TextureData newTextureData(final GLProfile glp, final File file, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final BufferedImage img = ImageIO.read(file); if (img == null) { return null; } @@ -70,12 +71,13 @@ public class IIOTextureProvider implements TextureProvider { return new AWTTextureData(glp, internalFormat, pixelFormat, mipmap, img); } - public TextureData newTextureData(GLProfile glp, InputStream stream, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - BufferedImage img = ImageIO.read(stream); + @Override + public TextureData newTextureData(final GLProfile glp, final InputStream stream, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final BufferedImage img = ImageIO.read(stream); if (img == null) { return null; } @@ -86,12 +88,13 @@ public class IIOTextureProvider implements TextureProvider { return new AWTTextureData(glp, internalFormat, pixelFormat, mipmap, img); } - public TextureData newTextureData(GLProfile glp, URL url, - int internalFormat, - int pixelFormat, - boolean mipmap, - String fileSuffix) throws IOException { - InputStream stream = url.openStream(); + @Override + public TextureData newTextureData(final GLProfile glp, final URL url, + final int internalFormat, + final int pixelFormat, + final boolean mipmap, + final String fileSuffix) throws IOException { + final InputStream stream = url.openStream(); try { return newTextureData(glp, stream, internalFormat, pixelFormat, mipmap, fileSuffix); } finally { diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureWriter.java b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureWriter.java index 89d0d20a1..60ac5680e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureWriter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/spi/awt/IIOTextureWriter.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -53,10 +53,11 @@ import com.jogamp.opengl.util.texture.*; import com.jogamp.opengl.util.texture.spi.*; public class IIOTextureWriter implements TextureWriter { - public boolean write(File file, - TextureData data) throws IOException { - int pixelFormat = data.getPixelFormat(); - int pixelType = data.getPixelType(); + @Override + public boolean write(final File file, + final TextureData data) throws IOException { + final int pixelFormat = data.getPixelFormat(); + final int pixelType = data.getPixelType(); if ((pixelFormat == GL.GL_RGB || pixelFormat == GL.GL_RGBA) && (pixelType == GL.GL_BYTE || @@ -67,7 +68,7 @@ public class IIOTextureWriter implements TextureWriter { (pixelFormat == GL.GL_RGB) ? BufferedImage.TYPE_3BYTE_BGR : BufferedImage.TYPE_4BYTE_ABGR); - byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); + final byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); ByteBuffer buf = (ByteBuffer) data.getBuffer(); if (buf == null) { buf = (ByteBuffer) data.getMipmapData()[0]; @@ -79,17 +80,17 @@ public class IIOTextureWriter implements TextureWriter { // Swizzle image components to be correct if (pixelFormat == GL.GL_RGB) { for (int i = 0; i < imageData.length; i += 3) { - byte red = imageData[i + 0]; - byte blue = imageData[i + 2]; + final byte red = imageData[i + 0]; + final byte blue = imageData[i + 2]; imageData[i + 0] = blue; imageData[i + 2] = red; } } else { for (int i = 0; i < imageData.length; i += 4) { - byte red = imageData[i + 0]; - byte green = imageData[i + 1]; - byte blue = imageData[i + 2]; - byte alpha = imageData[i + 3]; + final byte red = imageData[i + 0]; + final byte green = imageData[i + 1]; + final byte blue = imageData[i + 2]; + final byte alpha = imageData[i + 3]; imageData[i + 0] = alpha; imageData[i + 1] = blue; imageData[i + 2] = green; @@ -103,9 +104,9 @@ public class IIOTextureWriter implements TextureWriter { // Happened to notice that writing RGBA images to JPEGS is broken if (TextureIO.JPG.equals(IOUtil.getFileSuffix(file)) && image.getType() == BufferedImage.TYPE_4BYTE_ABGR) { - BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), + final BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR); - Graphics g = tmpImage.getGraphics(); + final Graphics g = tmpImage.getGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); image = tmpImage; @@ -113,7 +114,7 @@ public class IIOTextureWriter implements TextureWriter { return ImageIO.write(image, IOUtil.getFileSuffix(file), file); } - + throw new IOException("ImageIO writer doesn't support this pixel format / type (only GL_RGB/A + bytes)"); } } |