diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp')
140 files changed, 18447 insertions, 6144 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..83f5e4ebd 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 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..b36fd2637 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 @@ -50,7 +50,7 @@ public class Mixer { // Windows Event object private 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); @@ -74,13 +74,13 @@ public class Mixer { } synchronized void add(Track track) { - ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone(); + ArrayList<Track> newTracks = new ArrayList<Track>(tracks); newTracks.add(track); tracks = newTracks; } synchronized void remove(Track track) { - ArrayList/*<Track>*/ newTracks = (ArrayList) tracks.clone(); + ArrayList<Track> newTracks = new ArrayList<Track>(tracks); newTracks.remove(track); tracks = newTracks; } @@ -129,12 +129,13 @@ public class Mixer { super("Mixer Thread"); } + @Override public void run() { while (!shutdown) { - List/*<Track>*/ curTracks = tracks; + List<Track> curTracks = tracks; - for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) { - Track track = (Track) iter.next(); + for (Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + Track track = iter.next(); try { track.fill(); } catch (IOException e) { @@ -166,6 +167,7 @@ public class Mixer { } } + @Override public void run() { while (!shutdown) { // Get the next buffer @@ -207,10 +209,10 @@ public class Mixer { } // Run down all of the registered tracks mixing them in - List/*<Track>*/ curTracks = tracks; + List<Track> curTracks = tracks; - for (Iterator iter = curTracks.iterator(); iter.hasNext(); ) { - Track track = (Track) iter.next(); + for (Iterator<Track> iter = curTracks.iterator(); iter.hasNext(); ) { + Track track = iter.next(); // Consider only playing tracks if (track.isPlaying()) { // First recompute its gain @@ -344,7 +346,7 @@ public class Mixer { e.printStackTrace(); } } - + if (directByteBufferConstructor != null) { try { return (ByteBuffer) 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..01346553c 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 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..98a787478 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 @@ -151,7 +151,7 @@ public class Track { // These are only for use by the Mixer private float leftGain; private float rightGain; - + void setLeftGain(float leftGain) { this.leftGain = leftGain; } 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..79fb80169 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 @@ -206,6 +206,7 @@ class Vec3f { 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 1d9cf3668..023913d7b 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,7 @@ import java.lang.reflect.Method; import java.nio.Buffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -57,21 +59,52 @@ 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) { Class<?> clazz = null; @@ -110,8 +143,12 @@ public class BuildComposablePipeline { 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,7 +156,7 @@ public class BuildComposablePipeline { outputName = null; // TBD .. classPrologOpt = null; classDownstream = classToComposeAround; - mode = GEN_DEBUG | GEN_TRACE; + mode = GEN_DEBUG | GEN_TRACE ; } BuildComposablePipeline composer = @@ -155,7 +192,7 @@ public class BuildComposablePipeline { } try { - hasStackOverflow = + hasGL2ES1StackOverflow = hasImmediateMode && (classToComposeAround.getField("GL_STACK_OVERFLOW") != null); } catch (Exception e) { } @@ -167,16 +204,19 @@ 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>(); + final Set<PlainMethod> publicMethodsPlain = new HashSet<PlainMethod>(); for (Iterator<Method> iter = publicMethodsRaw.iterator(); iter.hasNext();) { - Method method = iter.next(); + 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")) { + final String name = method.getName(); + if ( !name.startsWith("getGL") && + !name.startsWith("isGL") && + !name.equals("getDownstreamGL") && + !name.equals("toString") ) { + final boolean runHooks = name.startsWith("gl") || addedGLHooks.containsKey(name); publicMethodsPlain.add(new PlainMethod(method, runHooks)); } } @@ -377,6 +417,7 @@ public class BuildComposablePipeline { ifNames, null, new CodeGenUtils.EmissionCallback() { + @Override public void emit(PrintWriter w) { emitClassDocComment(w); } @@ -410,7 +451,7 @@ public class BuildComposablePipeline { output.flush(); output.close(); - System.out.println("wrote to file: " + file); // JAU + System.out.println("wrote to file: " + file); } /** Get the name of the object through which API calls should be routed. */ @@ -430,19 +471,14 @@ public class BuildComposablePipeline { } 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(")"); + 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) { output.println(" {"); - output.print(" "); Class<?> retType = m.getReturnType(); boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(m); @@ -488,6 +524,9 @@ public class BuildComposablePipeline { output.print(" return "); } } + else { + output.print(" "); + } output.print(getDownstreamObjectName()); output.print('.'); output.print(m.getName()); @@ -563,9 +602,10 @@ public class BuildComposablePipeline { * closing parenthesis of the class is emitted. */ protected void postMethodEmissionHook(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\");"); } @@ -602,12 +642,17 @@ 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)) { - output.println(" return true;"); + output.println(" @Override"); + output.println(" public final boolean is" + type + "() {"); + if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { + 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(" }"); } @@ -616,7 +661,10 @@ public class BuildComposablePipeline { * Emits all of the isGL* methods. */ protected void emitGLIsMethods(PrintWriter output) { - emitGLIsMethod(output, "GL"); + output.println(" @Override"); + output.println(" public final boolean isGL() {"); + output.println(" return true;"); + output.println(" }"); emitGLIsMethod(output, "GL4bc"); emitGLIsMethod(output, "GL4"); emitGLIsMethod(output, "GL3bc"); @@ -624,25 +672,57 @@ public class BuildComposablePipeline { emitGLIsMethod(output, "GL2"); emitGLIsMethod(output, "GLES1"); emitGLIsMethod(output, "GLES2"); + emitGLIsMethod(output, "GLES3"); emitGLIsMethod(output, "GL2ES1"); emitGLIsMethod(output, "GL2ES2"); + emitGLIsMethod(output, "GL2ES3"); + emitGLIsMethod(output, "GL3ES3"); + emitGLIsMethod(output, "GL4ES3"); emitGLIsMethod(output, "GL2GL3"); - output.println(" public boolean isGLES() {"); - output.println(" return isGLES2() || isGLES1();"); + if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { + output.println(" @Override"); + output.println(" public final boolean isGLES() {"); + output.println(" return isGLES3() || isGLES2() || isGLES1();"); + output.println(" }"); + } else { + emitGLIsMethod(output, "GLES"); + } + output.println(" @Override"); + output.println(" public final boolean isGL4core() {"); + output.println(" return " + getDownstreamObjectName() + ".isGL4core();"); + output.println(" }"); + output.println(" @Override"); + output.println(" public final boolean isGL3core() {"); + output.println(" return " + getDownstreamObjectName() + ".isGL3core();"); + output.println(" }"); + output.println(" @Override"); + output.println(" public final boolean isGLcore() {"); + output.println(" return " + getDownstreamObjectName() + ".isGLcore();"); output.println(" }"); - output.println(" public boolean isGLES2Compatible() {"); + output.println(" @Override"); + output.println(" public final boolean isGLES2Compatible() {"); output.println(" return " + getDownstreamObjectName() + ".isGLES2Compatible();"); output.println(" }"); + output.println(" @Override"); + output.println(" public final boolean isGLES3Compatible() {"); + output.println(" return " + getDownstreamObjectName() + ".isGLES3Compatible();"); + 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); + output.println(" @Override"); + output.println(" public final javax.media.opengl." + type + " get" + type + "() {"); + final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); if (clazz.isAssignableFrom(baseInterfaceClass)) { - output.println(" return this;"); + if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { + output.println(" return this;"); + } else { + 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\");"); } @@ -653,7 +733,10 @@ public class BuildComposablePipeline { * Emits all of the getGL* methods. */ protected void emitGLGetMethods(PrintWriter output) { - emitGLGetMethod(output, "GL"); + output.println(" @Override"); + output.println(" public final javax.media.opengl.GL getGL() {"); + output.println(" return this;"); + output.println(" }"); emitGLGetMethod(output, "GL4bc"); emitGLGetMethod(output, "GL4"); emitGLGetMethod(output, "GL3bc"); @@ -661,10 +744,19 @@ public class BuildComposablePipeline { emitGLGetMethod(output, "GL2"); emitGLGetMethod(output, "GLES1"); emitGLGetMethod(output, "GLES2"); + emitGLGetMethod(output, "GLES3"); emitGLGetMethod(output, "GL2ES1"); emitGLGetMethod(output, "GL2ES2"); + emitGLGetMethod(output, "GL2ES3"); + emitGLGetMethod(output, "GL3ES3"); + emitGLGetMethod(output, "GL4ES3"); emitGLGetMethod(output, "GL2GL3"); - output.println(" public GLProfile getGLProfile() {"); + output.println(" @Override"); + output.println(" public final GL getDownstreamGL() throws GLException {"); + output.println(" return " + getDownstreamObjectName() + ";"); + output.println(" }"); + output.println(" @Override"); + output.println(" public final GLProfile getGLProfile() {"); output.println(" return " + getDownstreamObjectName() + ".getGLProfile();"); output.println(" }"); } @@ -682,18 +774,22 @@ public class BuildComposablePipeline { 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; } @@ -703,6 +799,7 @@ public class BuildComposablePipeline { super.preMethodEmissionHook(output); } + @Override protected void constructorHook(PrintWriter output) { output.print(" public " + getOutputName() + "("); output.print(downstreamName + " " + getDownstreamObjectName()); @@ -733,6 +830,7 @@ public class BuildComposablePipeline { } } + @Override protected void emitClassDocComment(PrintWriter output) { output.println("/**"); output.println(" * Composable pipeline {@link " + outputPackage + "." + outputName + "}, implementing the interface"); @@ -767,10 +865,12 @@ public class BuildComposablePipeline { output.println("*/"); } + @Override protected boolean hasPreDownstreamCallHook(Method m) { return null != getMethod(prologClassOpt, m); } + @Override protected void preDownstreamCallHook(PrintWriter output, Method m) { if (null != prologNameOpt) { output.print(getPrologObjectNameOpt()); @@ -782,10 +882,12 @@ public class BuildComposablePipeline { } } + @Override protected boolean hasPostDownstreamCallHook(Method m) { return false; } + @Override protected void postDownstreamCallHook(PrintWriter output, Method m) { } } // end class CustomPipeline @@ -799,18 +901,22 @@ public class BuildComposablePipeline { 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; } @@ -820,6 +926,7 @@ public class BuildComposablePipeline { super.preMethodEmissionHook(output); } + @Override protected void constructorHook(PrintWriter output) { output.print(" public " + getOutputName() + "("); output.println(downstreamName + " " + getDownstreamObjectName() + ")"); @@ -842,25 +949,21 @@ public class BuildComposablePipeline { @Override protected void postMethodEmissionHook(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 +973,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,30 +1004,40 @@ public class BuildComposablePipeline { output.println(" private GLContext _context;"); } + @Override 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("*/"); + 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(" */"); } + @Override protected boolean hasPreDownstreamCallHook(Method m) { return true; } + @Override protected void preDownstreamCallHook(PrintWriter output, Method m) { output.println(" checkContext();"); } + @Override protected boolean hasPostDownstreamCallHook(Method m) { return true; } + @Override protected void postDownstreamCallHook(PrintWriter output, Method m) { if (m.getName().equals("glBegin")) { output.println(" insideBeginEndPair = true;"); @@ -934,24 +1047,39 @@ public class BuildComposablePipeline { output.println(" insideBeginEndPair = false;"); } - output.println(" String txt = new String(\"" + m.getName() + "(\" +"); + output.println(" final int err = checkGLError();"); + output.println(" if (err != GL_NO_ERROR) {"); + + StringBuilder fmtsb = new StringBuilder(); + StringBuilder argsb = new StringBuilder(); + + fmtsb.append("\"%s("); + argsb.append("\"").append(m.getName()).append("\""); Class<?>[] params = m.getParameterTypes(); - for (int i = 0; params != null && i < params.length; i++) { - output.print(" \"<" + params[i].getName() + ">"); + 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 @@ -966,18 +1094,22 @@ public class BuildComposablePipeline { 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; } @@ -987,6 +1119,7 @@ public class BuildComposablePipeline { super.preMethodEmissionHook(output); } + @Override protected void constructorHook(PrintWriter output) { output.print(" public " + getOutputName() + "("); output.println(downstreamName + " " + getDownstreamObjectName() + ", PrintStream " + getOutputStreamName() + ")"); @@ -1037,27 +1170,36 @@ public class BuildComposablePipeline { output.println("}"); } + @Override 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("*/"); + 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(" */"); } + @Override protected boolean hasPreDownstreamCallHook(Method m) { return true; } + @Override protected void preDownstreamCallHook(PrintWriter output, Method m) { 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,10 +1207,12 @@ public class BuildComposablePipeline { output.println(");"); } + @Override protected boolean hasPostDownstreamCallHook(Method m) { return true; } + @Override protected void postDownstreamCallHook(PrintWriter output, Method m) { Class<?> ret = m.getReturnType(); if (ret != Void.TYPE) { @@ -1076,6 +1220,9 @@ public class BuildComposablePipeline { } else { output.println(" println(\"\");"); } + + if (m.getName().equals("glBegin")) + output.println(" indent+=2;"); } private String getOutputStreamName() { diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildStaticGLInfo.java index 87a734e1f..a5a26d18f 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,15 +105,22 @@ import java.util.regex.Pattern; * */ public class BuildStaticGLInfo { - // Handles function pointer - protected static int funcIdentifierGroup = 10; + // Handles function pointer + protected static final int funcIdentifierGroup = 9; 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?(\\(.*)"); + 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 = Pattern.compile("\\#ifndef ([CEW]?GL[XU]?_[A-Za-z0-9_]+)(.*)"); - protected static int defineIdentifierGroup = 1; + protected static Pattern ifPattern = + Pattern.compile("\\#if(.*)"); + protected static Pattern elsePattern = + Pattern.compile("\\#(elif|else)(.*)"); + protected static Pattern endifPattern = + Pattern.compile("\\#endif(.*)"); + + protected static final int defineIdentifierGroup = 1; protected static Pattern definePattern = Pattern.compile("\\#define ([CEW]?GL[XU]?_[A-Za-z0-9_]+)\\s*([A-Za-z0-9_]+)(.*)"); @@ -194,38 +201,62 @@ public class BuildStaticGLInfo { 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 + ">"); } } } @@ -293,9 +324,9 @@ public class BuildStaticGLInfo { output.println(" public static String getFunctionAssociation(String glFunctionName)"); output.println(" {"); output.println(" String mappedName = null;"); - output.println(" int funcNamePermNum = com.jogamp.gluegen.runtime.opengl.GLExtensionNames.getFuncNamePermutationNumber(glFunctionName);"); + output.println(" int funcNamePermNum = com.jogamp.gluegen.runtime.opengl.GLNameResolver.getFuncNamePermutationNumber(glFunctionName);"); output.println(" for(int i = 0; null==mappedName && i < funcNamePermNum; i++) {"); - output.println(" String tmp = com.jogamp.gluegen.runtime.opengl.GLExtensionNames.getFuncNamePermutation(glFunctionName, i);"); + output.println(" String tmp = com.jogamp.gluegen.runtime.opengl.GLNameResolver.getFuncNamePermutation(glFunctionName, i);"); output.println(" try {"); output.println(" mappedName = (String)funcToAssocMap.get(tmp);"); output.println(" } catch (Exception e) { }"); @@ -356,7 +387,7 @@ public class BuildStaticGLInfo { 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 c1a4facd2..f1a32fa9c 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. */ @@ -43,7 +43,7 @@ import com.jogamp.gluegen.GlueEmitterControls; import com.jogamp.gluegen.GlueGen; import com.jogamp.gluegen.MethodBinding; import com.jogamp.gluegen.procaddress.ProcAddressConfiguration; -import com.jogamp.gluegen.runtime.opengl.GLExtensionNames; +import com.jogamp.gluegen.runtime.opengl.GLNameResolver; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -61,11 +61,13 @@ 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> forcedExtensions = new HashSet<String>(); private 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 Set<String> bufferObjectOnly = new HashSet<String>(); private GLEmitter emitter; private Set<String> dropUniqVendorExtensions = new HashSet<String>(); @@ -90,6 +92,9 @@ public class GLConfiguration extends ProcAddressConfiguration { if (cmd.equalsIgnoreCase("IgnoreExtension")) { String sym = readString("IgnoreExtension", tok, filename, lineNo); ignoredExtensions.add(sym); + } else if (cmd.equalsIgnoreCase("ForceExtension")) { + String sym = readString("ForceExtension", tok, filename, lineNo); + forcedExtensions.add(sym); } else if (cmd.equalsIgnoreCase("RenameExtensionIntoCore")) { String sym = readString("RenameExtensionIntoCore", tok, filename, lineNo); extensionsRenamedIntoCore.add(sym); @@ -102,6 +107,9 @@ public class GLConfiguration extends ProcAddressConfiguration { glHeaders.add(sym); } else if (cmd.equalsIgnoreCase("BufferObjectKind")) { readBufferObjectKind(tok, filename, lineNo); + } else if (cmd.equalsIgnoreCase("BufferObjectOnly")) { + String sym = readString("BufferObjectOnly", tok, filename, lineNo); + bufferObjectOnly.add(sym); } else if (cmd.equalsIgnoreCase("DropUniqVendorExtensions")) { String sym = readString("DropUniqVendorExtensions", tok, filename, lineNo); dropUniqVendorExtensions.add(sym); @@ -123,10 +131,12 @@ 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); @@ -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);"; @@ -202,16 +214,21 @@ public class GLConfiguration extends ProcAddressConfiguration { for (String str : ignoredExtensions) { System.err.println("\t" + str); } + System.err.println("GL Forced extensions: "); + for (String str : forcedExtensions) { + System.err.println("\t" + str); + } super.dumpIgnores(); } protected boolean shouldIgnoreExtension(String symbol, 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(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(String origSymbol : origSymbols) { + if( shouldForceExtension( origSymbol, true, symbol ) ) { + ignoredExtension = false; + break; + } + } + } } } + if( ignoredExtension ) { + return true; + } } - boolean isGLEnum = GLExtensionNames.isGLEnumeration(symbol); - boolean isGLFunc = GLExtensionNames.isGLFunction(symbol); + boolean isGLEnum = GLNameResolver.isGLEnumeration(symbol); + boolean isGLFunc = GLNameResolver.isGLFunction(symbol); if (isGLFunc || isGLEnum) { - if (GLExtensionNames.isExtensionVEN(symbol, isGLFunc)) { - String extSuffix = GLExtensionNames.getExtensionSuffix(symbol, isGLFunc); + if (GLNameResolver.isExtensionVEN(symbol, isGLFunc)) { + String extSuffix = GLNameResolver.getExtensionSuffix(symbol, isGLFunc); if (getDropUniqVendorExtensions(extSuffix)) { if (DEBUG_IGNORES) { System.err.println("Ignore UniqVendorEXT: " + symbol + ", vendor " + extSuffix); @@ -241,6 +275,29 @@ 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(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) { return shouldIgnoreInInterface(symbol, true); @@ -287,6 +344,10 @@ public class GLConfiguration extends ProcAddressConfiguration { return (getBufferObjectKind(name) != null); } + public boolean isBufferObjectOnly(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 { diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java b/src/jogl/classes/com/jogamp/gluegen/opengl/GLEmitter.java index f4658ad7b..547382ed1 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. */ @@ -51,7 +51,7 @@ import com.jogamp.gluegen.SymbolFilter; import com.jogamp.gluegen.cgram.types.FunctionSymbol; import com.jogamp.gluegen.procaddress.ProcAddressEmitter; import com.jogamp.gluegen.procaddress.ProcAddressJavaMethodBindingEmitter; -import com.jogamp.gluegen.runtime.opengl.GLExtensionNames; +import com.jogamp.gluegen.runtime.opengl.GLNameResolver; import java.io.IOException; import java.io.PrintWriter; @@ -74,7 +74,7 @@ public class GLEmitter extends ProcAddressEmitter { // Buffer Object variants. Used as a Set rather than a Map. private 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 { @@ -110,13 +110,13 @@ public class GLEmitter extends ProcAddressEmitter { if (declarations != null) { for (Iterator<String> iterator = declarations.iterator(); iterator.hasNext();) { String decl = iterator.next(); - boolean isGLFunction = GLExtensionNames.isGLFunction(decl); + boolean isGLFunction = GLNameResolver.isGLFunction(decl); boolean isGLEnumeration = false; if (!isGLFunction) { - isGLEnumeration = GLExtensionNames.isGLEnumeration(decl); + isGLEnumeration = GLNameResolver.isGLEnumeration(decl); } if (isGLFunction || isGLEnumeration) { - String renamed = GLExtensionNames.normalize(decl, isGLFunction); + 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,6 +134,7 @@ public class GLEmitter extends ProcAddressEmitter { private List<ConstantDefinition> constants; private List<FunctionSymbol> functions; + @Override public void filterSymbols(List<ConstantDefinition> constants, List<FunctionSymbol> functions) { this.constants = constants; @@ -141,10 +142,12 @@ public class GLEmitter extends ProcAddressEmitter { doWork(); } + @Override public List<ConstantDefinition> getConstants() { return constants; } + @Override public List<FunctionSymbol> getFunctions() { return functions; } @@ -181,7 +184,7 @@ public class GLEmitter extends ProcAddressEmitter { String cause = null; for (String decl : declarations) { boolean isFunc = !decl.startsWith("GL_"); - if (!GLExtensionNames.isExtension(decl, isFunc)) { + if (!GLNameResolver.isExtension(decl, isFunc)) { isExtension = false; break; } @@ -199,7 +202,7 @@ public class GLEmitter extends ProcAddressEmitter { } } cause = decl; - String unifiedName = GLExtensionNames.normalize(decl, isFunc); + 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 @@ -262,18 +265,23 @@ public class GLEmitter extends ProcAddressEmitter { case (though we default to true currently). */ @Override protected List<MethodBinding> expandMethodBinding(MethodBinding binding) { - List<MethodBinding> bindings = super.expandMethodBinding(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,9 +309,16 @@ 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 @@ -384,7 +400,7 @@ public class GLEmitter extends ProcAddressEmitter { } private int addExtensionListOfAliasedSymbols2Buffer(BuildStaticGLInfo glInfo, StringBuilder buf, String sep1, String sep2, String name, 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); @@ -393,7 +409,7 @@ public class GLEmitter extends ProcAddressEmitter { if(!exclude.contains(origName)) { if (needsSep2) { buf.append(sep2); // diff-name seperator - } + } int num2 = addExtensionListOfSymbol2Buffer(glInfo, buf, sep1, origName); // extensions of orig-name needsSep2 = num<num2; num += num2; @@ -403,7 +419,7 @@ 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(); if (null == glInfo) { @@ -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 = "+GLExtensionNames.class.getName()+".normalizeVEN(com.jogamp.gluegen.runtime.opengl.GLExtensionNames.normalizeARB(functionNameUsr, true), true);"); - w.println(" String addressFieldNameBase = PROCADDRESS_VAR_PREFIX + functionNameBase;"); - w.println(" java.lang.reflect.Field addressField = null;"); - w.println(" int funcNamePermNum = "+GLExtensionNames.class.getName()+".getFuncNamePermutationNumber(functionNameBase);"); - w.println(" for(int i = 0; null==addressField && i < funcNamePermNum; i++) {"); - w.println(" String addressFieldName = "+GLExtensionNames.class.getName()+".getFuncNamePermutation(addressFieldNameBase, i);"); - w.println(" try {"); - w.println(" addressField = getClass().getField(addressFieldName);"); - w.println(" } catch (Exception e) { }"); + 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..389d35f99 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; @@ -103,11 +104,14 @@ public class GLJavaMethodBindingEmitter extends ProcAddressJavaMethodBindingEmit @Override protected void emitBindingCSignature(MethodBinding binding, PrintWriter writer) { - super.emitBindingCSignature(binding, writer); - 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()) { 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..21946ea89 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,7 +72,7 @@ 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 @@ -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(); - + // ========================================================================= /** * <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,7 +114,7 @@ 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) @@ -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) { - 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() @@ -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) { // 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,10 +259,10 @@ 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()); @@ -272,25 +273,25 @@ public class StaticGLGenTask extends Task } } - /** - * <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(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), + Project.MSG_WARN), null); - + // set the project and command line execute.setAntRun(project); execute.setCommandline(command); execute.setWorkingDirectory( project.getBaseDir() ); - + // execute the command try { 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..4ac9ae3f3 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,8 +55,8 @@ 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 { 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..6d9d6f2bb 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. */ @@ -131,6 +131,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding } } + @Override protected String getReturnTypeString(boolean skipArray) { if (isForImplementingMethodCall()) { JavaType returnType = getBinding().getJavaReturnType(); @@ -142,6 +143,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return super.getReturnTypeString(skipArray); } + @Override protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { super.emitPreCallSetup(binding, writer); for (int i = 0; i < binding.getNumArguments(); i++) { @@ -162,6 +164,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return "__buffer_array_" + argNumber; } + @Override protected int emitArguments(PrintWriter writer) { boolean needComma = false; @@ -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()) { + 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; } + @Override protected void emitReturnVariableSetupAndCall(MethodBinding binding, PrintWriter writer) { writer.print(" "); JavaType returnType = binding.getJavaReturnType(); boolean needsResultAssignment = false; - + if (!returnType.isVoid()) { if (returnType.isCompoundTypeWrapper() || returnType.isNIOByteBuffer()) { @@ -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,6 +459,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding return numArgsEmitted; } + @Override protected void emitCallResultReturn(MethodBinding binding, PrintWriter writer) { for (int i = 0; i < binding.getNumArguments(); i++) { JavaType type = binding.getJavaArgumentType(i); @@ -468,6 +473,7 @@ public class NativeSignatureJavaMethodBindingEmitter extends GLJavaMethodBinding super.emitCallResultReturn(binding, writer); } + @Override public String getName() { String res = super.getName(); if (forImplementingMethodCall && bufferObjectVariant) { diff --git a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLExtensionNames.java b/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLNameResolver.java index 426333034..9b57a2f2d 100644 --- a/src/jogl/classes/com/jogamp/gluegen/runtime/opengl/GLExtensionNames.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,25 +29,26 @@ * 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; -public class GLExtensionNames { +/** Runtime utility identify and resolve extension names, which may be subsumed to core. */ +public class GLNameResolver { //GL_XYZ : GL_XYZ, GL_XYZ_GL2, GL_XYZ_ARB, GL_XYZ_OES, GL_XYZ_OML //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", + public static final String[] extensionsVEN = { "3DFX", "AMD", "ANGLE", "ARM", @@ -157,7 +158,7 @@ public class GLExtensionNames { return str; } public static final boolean isExtension(String str, boolean isGLFunc) { - return isExtension(extensionsARB, str, isGLFunc) || + return isExtension(extensionsARB, str, isGLFunc) || isExtension(extensionsVEN, str, isGLFunc); } 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 fe9efebc7..3fb315c99 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,13 +42,14 @@ public class GLProcAddressResolver implements FunctionAddressResolver { public static final boolean DEBUG = false; + @Override public long resolve(String name, DynamicLookupHelper lookup) { long newProcAddress = 0; - int permutations = GLExtensionNames.getFuncNamePermutationNumber(name); + int permutations = GLNameResolver.getFuncNamePermutationNumber(name); for (int i = 0; 0 == newProcAddress && i < permutations; i++) { - String funcName = GLExtensionNames.getFuncNamePermutation(name, i); + String funcName = GLNameResolver.getFuncNamePermutation(name, i); try { newProcAddress = lookup.dynamicLookupFunction(funcName); } catch (Exception e) { diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index 5b0d985ad..c8d5a9db6 100755..100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -32,23 +32,23 @@ import java.util.Collections; 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.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. * 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 +60,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 +79,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 */ @@ -104,21 +104,21 @@ public class OutlineShape implements Comparable<OutlineShape> { VerticesState(int state){ this.state = state; } - } + } public static final int DIRTY_BOUNDS = 1 << 0; 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. */ /* pp */ ArrayList<Outline> outlines; - private AABBox bbox; + private final AABBox bbox; /** dirty bits DIRTY_BOUNDS */ - private int dirtyBits; + private int dirtyBits; /** Create a new Outline based Shape */ @@ -128,7 +128,7 @@ public class OutlineShape implements Comparable<OutlineShape> { this.outlines.add(new Outline()); this.outlineState = VerticesState.UNDEFINED; this.bbox = new AABBox(); - this.dirtyBits = 0; + this.dirtyBits = 0; } /** Clears all data and reset all states as if this instance was newly created */ @@ -137,7 +137,7 @@ public class OutlineShape implements Comparable<OutlineShape> { outlines.add(new Outline()); outlineState = VerticesState.UNDEFINED; bbox.reset(); - dirtyBits = 0; + dirtyBits = 0; } /** Returns the associated vertex factory of this outline shape @@ -149,10 +149,10 @@ public class OutlineShape implements Comparable<OutlineShape> { 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 */ @@ -164,26 +164,26 @@ public class OutlineShape implements Comparable<OutlineShape> { /** 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 { addOutline(outlines.size(), outline); } /** 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 { @@ -213,7 +213,7 @@ public class OutlineShape implements Comparable<OutlineShape> { * using {@link #addOutline(Outline)} for each element. * <p>Closes the current last outline via {@link #closeLastOutline()} 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 { @@ -228,10 +228,10 @@ public class OutlineShape implements Comparable<OutlineShape> { /** 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 { @@ -244,7 +244,7 @@ public class OutlineShape implements Comparable<OutlineShape> { /** 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()) */ @@ -261,15 +261,15 @@ public class OutlineShape implements Comparable<OutlineShape> { return outlines.get(outlines.size()-1); } - /** @return the {@code Outline} at {@code position} + /** @return 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 { return outlines.get(position); - } + } /** Adds a vertex to the last open outline in the - * shape. + * shape. * @param v the vertex to be added to the OutlineShape */ public final void addVertex(Vertex v) { @@ -280,9 +280,9 @@ public class OutlineShape implements Comparable<OutlineShape> { } } - /** 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 in 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) { @@ -295,7 +295,7 @@ public class OutlineShape implements Comparable<OutlineShape> { /** Add a 2D {@link Vertex} to the last outline by defining the coordniate 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 @@ -317,10 +317,10 @@ public class OutlineShape implements Comparable<OutlineShape> { 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 @@ -330,11 +330,11 @@ public class OutlineShape implements Comparable<OutlineShape> { */ public final void addVertex(float[] coordsBuffer, int offset, int length, 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 + * A new temp vertex is added at the end which * is equal to the first.</p> */ public void closeLastOutline() { @@ -351,7 +351,7 @@ public class OutlineShape implements Comparable<OutlineShape> { /** Ensure the outlines represent * the specified destinationType. * and removes all overlaps in boundary triangles - * @param destinationType the target outline's vertices state. Currently only + * @param destinationType the target outline's vertices state. Currently only * {@link OutlineShape.VerticesState#QUADRATIC_NURBS} are supported. */ public void transformOutlines(VerticesState destinationType) { @@ -371,7 +371,7 @@ public class OutlineShape implements Comparable<OutlineShape> { float[] v2 = VectorUtil.mid(v1, v3); //drop off-curve vertex to image on the curve - b.setCoord(v2, 0, 3); + b.setCoord(v2, 0, 3); b.setOnCurve(true); outline.addVertex(index, vertexFactory.create(v1, 0, 3, false)); @@ -379,19 +379,19 @@ public class OutlineShape implements Comparable<OutlineShape> { } /** Check overlaps between curved triangles - * first check if any vertex in triangle a is in triangle b + * 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() { + private void checkOverlaps() { ArrayList<Vertex> overlaps = new ArrayList<Vertex>(3); 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++) { @@ -430,10 +430,10 @@ public class OutlineShape implements Comparable<OutlineShape> { private final float[] tempVecAC = new float[3]; private final float[] tempVecAB = new float[3]; private final float[] tempVecAP = new float[3]; - + private Vertex checkTriOverlaps(Vertex a, Vertex b, Vertex c) { 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++) { @@ -466,7 +466,7 @@ public class OutlineShape implements Comparable<OutlineShape> { private void transformOutlines2Quadratic() { 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(); @@ -474,13 +474,13 @@ public class OutlineShape implements Comparable<OutlineShape> { 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(), + 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(vertexCount <= 0) { outlines.remove(outline); @@ -490,7 +490,7 @@ public class OutlineShape implements Comparable<OutlineShape> { } if( vertexCount > 0 ) { - if(VectorUtil.checkEquality(outline.getVertex(0).getCoord(), + if(VectorUtil.checkEquality(outline.getVertex(0).getCoord(), outline.getLastVertex().getCoord())) { outline.removeVertex(vertexCount-1); } @@ -511,7 +511,7 @@ public class OutlineShape implements Comparable<OutlineShape> { } } - /** @return the list of concatenated vertices associated with all + /** @return the list of concatenated vertices associated with all * {@code Outline}s of this object */ public ArrayList<Vertex> getVertices() { @@ -554,9 +554,10 @@ public class OutlineShape implements Comparable<OutlineShape> { } /** Compare two outline shapes with Bounding Box area - * as criteria. + * as criteria. * @see java.lang.Comparable#compareTo(java.lang.Object) */ + @Override public final int compareTo(OutlineShape outline) { float size = getBounds().getSize(); float newSize = outline.getBounds().getSize(); @@ -582,20 +583,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 */ + @Override public boolean equals(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; @@ -613,20 +615,4 @@ 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; - } } diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java index f7f51758b..e36e37c26 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/Region.java +++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java @@ -34,13 +34,13 @@ import jogamp.graph.curve.opengl.RegionFactory; import jogamp.opengl.Debug; import com.jogamp.graph.curve.opengl.GLRegion; -import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; +import com.jogamp.opengl.math.geom.AABBox; /** Abstract Outline shape GL representation define the method an OutlineShape(s) * is bound and rendered. - * + * * @see GLRegion */ public abstract class Region { @@ -73,7 +73,7 @@ public abstract class Region { } /** Check if render mode capable of non uniform weights - * + * * @param renderModes * bit-field of modes, e.g. * {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, @@ -93,7 +93,7 @@ public abstract class Region { return region; } - /** + /** * Create a {@link Region} defining this {@link OutlineShape} * @return the resulting Region. */ @@ -101,28 +101,28 @@ public abstract class Region { final Region region = RegionFactory.create(renderModes); region.addOutlineShape(outlineShape); return region; - } - + } + protected Region(int regionRenderModes) { this.renderModes = regionRenderModes; } /** Get current Models - * + * * @return bit-field of render modes */ public final int getRenderModes() { return renderModes; } /** Check if current Region is using VBAA - * + * * @return true if capable of two pass rendering - VBAA */ public boolean isVBAA() { return Region.isVBAA(renderModes); } /** Check if current instance uses non uniform weights - * + * * @return true if capable of nonuniform weights */ public boolean isNonUniformWeight() { return Region.isNonUniformWeight(renderModes); @@ -130,7 +130,7 @@ public abstract class Region { /** 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 */ public final int getNumVertices() { return numVertices; @@ -138,10 +138,10 @@ public abstract class Region { /** 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) */ public void addTriangle(Triangle tri) { triangles.add(tri); @@ -150,10 +150,10 @@ public abstract class Region { /** 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 * a list of triangle objects - * + * * @see update(GL2ES2) */ public void addTriangles(List<Triangle> tris) { triangles.addAll(tris); @@ -162,10 +162,10 @@ public abstract class Region { /** 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) */ public void addVertex(Vertex vert) { vertices.add(vert); @@ -175,10 +175,10 @@ public abstract class Region { /** 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 * a list of vertex objects - * + * * @see update(GL2ES2) */ public void addVertices(List<Vertex> verts) { vertices.addAll(verts); @@ -202,7 +202,7 @@ public abstract class Region { } setDirty(true); } - + public void addOutlineShapes(List<OutlineShape> shapes) { for (int i = 0; i < shapes.size(); i++) { addOutlineShape(shapes.get(i)); @@ -217,9 +217,9 @@ public abstract class Region { /** 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) */ public final boolean isDirty() { return dirty; 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 e046c91cb..d63e02a9c 100755..100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -37,16 +37,16 @@ import com.jogamp.graph.curve.Region; /** 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
*/
-public abstract class GLRegion extends Region {
-
+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
@@ -55,31 +55,31 @@ public abstract class GLRegion extends Region { return (GLRegion) Region.create(outlineShapes, renderModes);
}
- /**
+ /**
* Create an ogl {@link Region} defining this {@link OutlineShape}
* @return the resulting Region.
*/
public static GLRegion create(OutlineShape outlineShape, int renderModes) {
return (GLRegion) Region.create(outlineShape, renderModes);
- }
-
+ }
+
protected GLRegion(int renderModes) {
super(renderModes);
}
-
+
/** 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>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
*/
protected abstract void update(GL2ES2 gl, RenderState rs);
-
+
/** Delete and clean the associated OGL
* objects
*/
public abstract void destroy(GL2ES2 gl, RenderState rs);
-
+
/** Renders the associated OGL objects specifying
* current width/hight of window for multi pass rendering
* of the region.
@@ -87,13 +87,13 @@ public abstract class GLRegion extends Region { * @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.
+ * @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.
*/
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);
}
-
+
protected abstract void drawImpl(GL2ES2 gl, RenderState rs, int vp_width, int vp_height, int[/*1*/] texWidth);
}
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 4b7c12fea..faaf72a99 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java @@ -35,25 +35,25 @@ import com.jogamp.graph.curve.Region; public abstract class RegionRenderer extends Renderer { - /** + /** * 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} + * @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} * @return an instance of Region Renderer */ public static RegionRenderer create(RenderState rs, int renderModes) { return new jogamp.graph.curve.opengl.RegionRendererImpl01(rs, renderModes); } - + protected RegionRenderer(RenderState rs, int renderModes) { super(rs, renderModes); } - - + + /** 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 texWidth desired texture width for multipass-rendering. + * @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 */ @@ -64,10 +64,10 @@ public abstract class RegionRenderer extends Renderer { if( !areRenderModesCompatible(region) ) { throw new GLException("Incompatible render modes, : region modes "+region.getRenderModes()+ " doesn't contain renderer modes "+this.getRenderModes()); - } + } drawImpl(gl, region, texWidth); } - + /** * 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[])}. @@ -78,6 +78,6 @@ public abstract class RegionRenderer extends Renderer { protected void destroyImpl(GL2ES2 gl) { // nop } - - + + } 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..9b0f32ef6 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java @@ -40,50 +40,50 @@ import com.jogamp.opengl.util.glsl.ShaderState; public abstract 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); + return new RenderStateImpl(st, pointFactory, null); } public static RenderState createRenderState(ShaderState st, Vertex.Factory<? extends Vertex> pointFactory, PMVMatrix pmvMatrix) { return new RenderStateImpl(st, pointFactory, pmvMatrix); } - + public static final RenderState getRenderState(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 final GLUniformData gcu_PMVMatrix; + protected RenderState(ShaderState st, Vertex.Factory<? extends Vertex> vertexFactory, PMVMatrix pmvMatrix) { this.st = st; 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.gcu_PMVMatrix = new GLUniformData(UniformNames.gcu_PMVMatrix, 4, 4, this.pmvMatrix.glGetPMvMatrixf()); + st.ownUniform(gcu_PMVMatrix); } - + 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) { return (RenderState) gl.getContext().attachObject(thisKey, this); } - + public final boolean detachFrom(GL2ES2 gl) { RenderState _rs = (RenderState) gl.getContext().getAttachedObject(thisKey); if(_rs == this) { @@ -91,21 +91,22 @@ public abstract class RenderState { return true; } return false; - } - - public StringBuilder toString(StringBuilder sb) { + } + + public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) { if(null==sb) { sb = new StringBuilder(); } sb.append("RenderState["); - st.toString(sb).append(Platform.getNewline()); + st.toString(sb, alsoUnlocated).append(Platform.getNewline()); sb.append("]"); return sb; } - + + @Override public String toString() { - return toString(null).toString(); - } + return toString(null, false).toString(); + } } diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java index c9cb13ad4..31e4f0b8f 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java @@ -33,6 +33,7 @@ import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; import javax.media.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderState; import com.jogamp.opengl.util.PMVMatrix; @@ -51,8 +52,8 @@ public abstract class Renderer { protected int vp_height; protected boolean initialized; protected final RenderState rs; - private boolean vboSupported = false; - + private boolean vboSupported = false; + public final boolean isInitialized() { return initialized; } public final int getWidth() { return vp_width; } @@ -61,29 +62,29 @@ public abstract class Renderer { 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 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); } /** @@ -92,17 +93,17 @@ public abstract class Renderer { */ public final boolean areRenderModesCompatible(Region region) { final int cleanRenderModes = getRenderModes() & ( Region.VARIABLE_CURVE_WEIGHT_BIT ); - return cleanRenderModes == ( region.getRenderModes() & cleanRenderModes ); + 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, int)}</p> - * + * * @param gl referencing the current GLContext to which the ShaderState is bound to * @throws GLException if initialization failed */ @@ -116,46 +117,48 @@ public abstract class Renderer { 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( Region.isNonUniformWeight( getRenderModes() ) ) { + 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) { + public final void flushCache(GL2ES2 gl) { // FIXME: REMOVE ! } - + public void destroy(GL2ES2 gl) { if(!initialized){ if(DEBUG_INSTANCE) { @@ -166,13 +169,13 @@ public abstract class Renderer { rs.getShaderState().useProgram(gl, false); destroyImpl(gl); rs.destroy(gl); - initialized = false; + initialized = false; } - + public final RenderState getRenderState() { return rs; } public final ShaderState getShaderState() { return rs.getShaderState(); } - - public final void enable(GL2ES2 gl, boolean enable) { + + public final void enable(GL2ES2 gl, boolean enable) { rs.getShaderState().useProgram(gl, enable); } @@ -181,11 +184,11 @@ public abstract class Renderer { throw new IllegalArgumentException("Weight out of range"); } rs.getWeight().setData(v); - if(null != gl && rs.getShaderState().inUse()) { + if(null != gl && rs.getShaderState().inUse() && Region.isNonUniformWeight( getRenderModes() ) ) { 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()) { @@ -196,11 +199,11 @@ public abstract class Renderer { 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); + 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); @@ -210,7 +213,7 @@ public abstract class Renderer { 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); @@ -220,7 +223,7 @@ public abstract class Renderer { 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); @@ -238,6 +241,14 @@ public abstract class Renderer { } } + /** No PMVMatrix operation is performed here. PMVMatrix will be updated if gl is not null. */ + public boolean reshapeNotify(GL2ES2 gl, int width, int height) { + this.vp_width = width; + this.vp_height = height; + updateMatrix(gl); + return true; + } + public boolean reshapePerspective(GL2ES2 gl, float angle, int width, int height, float near, float far) { this.vp_width = width; this.vp_height = height; @@ -258,24 +269,34 @@ public abstract class Renderer { p.glLoadIdentity(); p.glOrthof(0, width, 0, height, near, far); updateMatrix(gl); - return true; + return true; } - protected String getVertexShaderName(GL2ES2 gl) { - return "curverenderer01" + getShaderGLVersionSuffix(gl); + protected String getVertexShaderName() { + return "curverenderer" + getImplVersion(); } - - 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 getFragmentShaderName() { + final String version = getImplVersion(); + final String pass = Region.isVBAA(renderModes) ? "-2pass" : "-1pass" ; + final String weight = Region.isNonUniformWeight(renderModes) ? "-weight" : "" ; + return "curverenderer" + version + pass + weight; } - - protected String getShaderGLVersionSuffix(GL2ES2 gl) { - if(gl.isGLES2()) { - return "-es2"; + + // FIXME: Really required to have sampler2D def. precision ? If not, we can drop getFragmentShaderPrecision(..) and use default ShaderCode .. + public static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n"; + + protected String getFragmentShaderPrecision(GL2ES2 gl) { + if( gl.isGLES() ) { + return es2_precision_fp; + } + if( ShaderCode.requiresGL3DefaultPrecision(gl) ) { + return ShaderCode.gl3_default_precision_fp; } - return "-gl2"; - } - + return null; + } + + protected String getImplVersion() { + return "01"; + } }
\ 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..4e8c400e0 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java +++ b/src/jogl/classes/com/jogamp/graph/curve/tess/Triangulator.java @@ -36,32 +36,32 @@ 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 * describing the shape * @param outline a bounding {@link Outline} */ public void addCurve(Outline outline); - - /** Generate the triangulation of the provided + + /** Generate the triangulation of the provided * List of {@link Outline}s * @return an arraylist of {@link Triangle}s resembling the * final shape. */ public ArrayList<Triangle> generate(); - + /** Reset the triangulation to initial state * Clearing cached data */ diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index 97510bcd0..6c0041de9 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -30,16 +30,16 @@ package com.jogamp.graph.font; import java.util.List; import com.jogamp.graph.curve.OutlineShape; -import com.jogamp.graph.geom.AABBox; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; +import com.jogamp.opengl.math.geom.AABBox; /** * Interface wrapper for font implementation. - * + * * TrueType Font Specification: * http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html - * + * * TrueType Font Table Introduction: * http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08 */ @@ -55,22 +55,22 @@ 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 { + public interface Metrics { float getAscent(float pixelSize); float getDescent(float pixelSize); float getLineGap(float pixelSize); - float getMaxExtend(float pixelSize); + float getMaxExtend(float pixelSize); float getScale(float pixelSize); AABBox getBBox(float pixelSize); } @@ -79,12 +79,12 @@ public interface Font { * Glyph for font */ 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 short getID(); @@ -97,24 +97,24 @@ public interface Font { public String getName(int nameIndex); public StringBuilder getName(StringBuilder string, 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 Metrics getMetrics(); public Glyph getGlyph(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 ); + + public boolean isPrintableChar( char c ); /** * @param glyph source of the created OutlineShape @@ -122,7 +122,7 @@ public interface Font { * @return OutlineShape of the glyph */ public OutlineShape getOutlineShape(Glyph glyph, Factory<? extends Vertex> vertexFactory); - + /** * @param shapes optional storage of OutlineShapes passed by user, new shapes are appended * @param string source of the created OutlineShapes @@ -131,7 +131,8 @@ public interface Font { * @return List of OutlineShapes, one OutlineShape per character */ public List<OutlineShape> getOutlineShapes(List<OutlineShape> shapes, CharSequence string, float pixelSize, Factory<? extends Vertex> vertexFactory); - + /** 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..884662e6e 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java @@ -33,19 +33,29 @@ import java.net.URLConnection; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; -import com.jogamp.common.util.SecurityUtil; 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 { + 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 */ public static final int UBUNTU = 0; - + /** Java fonts are optional */ public static final int JAVA = 1; - + private static final FontConstructor fontConstr; static { @@ -53,18 +63,18 @@ 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) { switch (font) { case JAVA: @@ -73,20 +83,23 @@ public class FontFactory { return UbuntuFontLoader.get(); } } - + public static final Font get(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; - } + 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..17b8b2136 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontSet.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontSet.java @@ -34,29 +34,29 @@ public interface FontSet { /** Font family REGULAR **/ public static final int FAMILY_REGULAR = 0; - + /** Font family LIGHT **/ public static final int FAMILY_LIGHT = 1; - + /** Font family MEDIUM **/ public static final int FAMILY_MEDIUM = 2; - + /** Font family CONDENSED **/ public static final int FAMILY_CONDENSED = 3; - + /** Font family MONO **/ public static final int FAMILY_MONOSPACED = 4; - + /** SERIF style/family bit flag. Fallback to Sans Serif. */ public static final int STYLE_SERIF = 1 << 1; - + /** BOLD style bit flag */ public static final int STYLE_BOLD = 1 << 2; - + /** ITALIC style bit flag */ public static final int STYLE_ITALIC = 1 << 3; Font getDefault() throws IOException ; - + Font get(int family, int stylebits) throws IOException ; } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 5030488cc..77a318078 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -30,17 +30,18 @@ package com.jogamp.graph.geom; import java.util.ArrayList; import com.jogamp.graph.geom.Vertex; -import com.jogamp.graph.math.VectorUtil; +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 */ public class Outline implements Cloneable, Comparable<Outline> { @@ -54,7 +55,7 @@ public class Outline implements Cloneable, Comparable<Outline> { * An outline can contain off Curve vertices which define curved * regions in the outline. */ - public Outline() { + public Outline() { } public final int getVertexCount() { @@ -63,7 +64,7 @@ public class Outline implements Cloneable, Comparable<Outline> { /** 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 { addVertex(vertices.size(), vertex); @@ -72,7 +73,7 @@ public class Outline implements Cloneable, Comparable<Outline> { /** 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 { @@ -87,10 +88,10 @@ public class Outline implements Cloneable, Comparable<Outline> { /** 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 { @@ -111,12 +112,12 @@ public class Outline implements Cloneable, Comparable<Outline> { /** 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; + dirtyBBox = true; return vertices.remove(position); } @@ -138,7 +139,7 @@ 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) { @@ -151,7 +152,7 @@ public class Outline implements Cloneable, Comparable<Outline> { } /** define if this outline is closed or not. - * if set to closed, checks if the last vertex is + * 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 @@ -169,9 +170,10 @@ public class Outline implements Cloneable, Comparable<Outline> { } /** Compare two outlines with Bounding Box area - * as criteria. + * as criteria. * @see java.lang.Comparable#compareTo(java.lang.Object) */ + @Override public final int compareTo(Outline outline) { float size = getBounds().getSize(); float newSize = outline.getBounds().getSize(); @@ -197,19 +199,20 @@ public class Outline implements Cloneable, Comparable<Outline> { validateBoundingBox(); } return bbox; - } + } /** * @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 */ + @Override public boolean equals(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; @@ -228,6 +231,7 @@ public class Outline implements Cloneable, Comparable<Outline> { /** * @return deep clone of this Outline */ + @Override public Outline clone() { Outline o; try { @@ -239,5 +243,5 @@ public class Outline implements Cloneable, Comparable<Outline> { o.vertices.add(vertices.get(i).clone()); } return o; - } + } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java index fb34de221..a01cd834f 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java @@ -48,11 +48,11 @@ public class 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]; } @@ -60,11 +60,11 @@ public class Triangle { public void setEdgesBoundary(boolean[] boundary) { this.boundaryEdges = boundary; } - + public boolean[] getEdgeBoundary() { return boundaryEdges; } - + public boolean[] getVerticesBoundary() { return boundaryVertices; } @@ -72,7 +72,8 @@ public class Triangle { public void setVerticesBoundary(boolean[] boundaryVertices) { this.boundaryVertices = boundaryVertices; } - + + @Override public String toString() { return "Tri ID: " + id + "\n" + vertices[0] + "\n" + vertices[1] + "\n" + vertices[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..994253f71 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java @@ -27,27 +27,27 @@ */ 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(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 +55,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); - + /** * @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/geom/opengl/SVertex.java b/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java index 9dade17e9..b27604a44 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/opengl/SVertex.java @@ -28,7 +28,7 @@ package com.jogamp.graph.geom.opengl; import com.jogamp.graph.geom.Vertex; -import com.jogamp.graph.math.VectorUtil; +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. @@ -39,25 +39,28 @@ public class SVertex implements Vertex { protected float[] coord = new float[3]; protected boolean onCurve; private float[] texCoord = new float[2]; - + 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(); } + @Override public SVertex create(float x, float y, float z, boolean onCurve) { return new SVertex(x, y, z, onCurve); } + @Override public SVertex create(float[] coordsBuffer, int offset, int length, boolean onCurve) { return new SVertex(coordsBuffer, offset, length, onCurve); - } + } } - + public SVertex() { } @@ -65,73 +68,92 @@ public class SVertex implements Vertex { setCoord(x, y, z); setOnCurve(onCurve); } - + public SVertex(float[] coordsBuffer, int offset, int length, boolean onCurve) { setCoord(coordsBuffer, offset, length); setOnCurve(onCurve); } - - public SVertex(float[] coordsBuffer, int offset, int length, + + public SVertex(float[] coordsBuffer, int offset, int length, float[] texCoordsBuffer, int offsetTC, int lengthTC, boolean onCurve) { setCoord(coordsBuffer, offset, length); setTexCoord(texCoordsBuffer, offsetTC, lengthTC); setOnCurve(onCurve); } - + + @Override 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(float[] coordsBuffer, int offset, int length) { System.arraycopy(coordsBuffer, offset, coord, 0, length); } - + + @Override + public int getCoordCount() { + return 3; + } + + @Override public final float[] getCoord() { return coord; } + @Override public final void setX(float x) { this.coord[0] = x; } + @Override public final void setY(float y) { this.coord[1] = y; } + @Override public final void setZ(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; } + @Override public final void setOnCurve(boolean onCurve) { this.onCurve = onCurve; } + @Override public final int getId(){ return id; } - + + @Override public final void setId(int id){ this.id = id; } - + + @Override public boolean equals(Object obj) { if( obj == this) { return true; @@ -140,34 +162,39 @@ public class SVertex implements Vertex { return false; } final Vertex v = (Vertex) obj; - return this == v || - isOnCurve() == v.isOnCurve() && + return this == v || + isOnCurve() == v.isOnCurve() && VectorUtil.checkEqualityVec2(getTexCoord(), v.getTexCoord()) && VectorUtil.checkEquality(getCoord(), v.getCoord()) ; } - + + @Override public final float[] getTexCoord() { return texCoord; } + @Override public final void setTexCoord(float s, float t) { this.texCoord[0] = s; this.texCoord[1] = t; } + @Override public final void setTexCoord(float[] texCoordsBuffer, int offset, int length) { System.arraycopy(texCoordsBuffer, offset, texCoord, 0, length); } - + /** * @return deep clone of this Vertex, but keeping the id blank */ + @Override public SVertex clone(){ return new SVertex(this.coord, 0, 3, this.texCoord, 0, 2, this.onCurve); } - + + @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] + "]"; } 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/opengl/FBObject.java b/src/jogl/classes/com/jogamp/opengl/FBObject.java new file mode 100644 index 000000000..90a8dc073 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/FBObject.java @@ -0,0 +1,2330 @@ +/** + * 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.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.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; + +import jogamp.opengl.Debug; + +import com.jogamp.opengl.FBObject.Attachment.Type; + +/** + * Core utility class simplifying usage of framebuffer objects (FBO) + * with all {@link GLProfile}s. + * <p> + * 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 FBOResizeQuirk = false; + + private static enum DetachAction { NONE, DISPOSE, RECREATE }; + + /** + * Marker interface, denotes a color buffer attachment. + * <p>Always an instance of {@link Attachment}.</p> + * <p>Either an instance of {@link ColorAttachment} or {@link TextureAttachment}.</b> + */ + 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. + */ + public boolean initialize(GL gl) throws GLException; + + /** + * Releases the color buffer if initialized, i.e. name is not <code>zero</code>. + * @throws GLException if buffer release fails. + */ + public void free(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); + } + + /** Common super class of all attachments */ + public static abstract class Attachment { + 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. + */ + public static Type determine(int format) throws IllegalArgumentException { + switch(format) { + case GL.GL_RGBA4: + case GL.GL_RGB5_A1: + case GL.GL_RGB565: + case GL.GL_RGB8: + case GL.GL_RGBA8: + return Type.COLOR; + case GL.GL_DEPTH_COMPONENT16: + case GL.GL_DEPTH_COMPONENT24: + case GL.GL_DEPTH_COMPONENT32: + return Type.DEPTH; + case GL.GL_STENCIL_INDEX1: + case GL.GL_STENCIL_INDEX4: + case GL.GL_STENCIL_INDEX8: + return Type.STENCIL; + case GL.GL_DEPTH24_STENCIL8: + return Type.DEPTH_STENCIL; + default: + 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) { + 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) { + final int _format; + switch(format) { + case GL.GL_RGBA: + 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: + _format = format; + } + switch(_format) { + case GL.GL_RGBA4: + caps.setRedBits(4); + caps.setGreenBits(4); + caps.setBlueBits(4); + caps.setAlphaBits(4); + break; + case GL.GL_RGB5_A1: + caps.setRedBits(5); + caps.setGreenBits(5); + caps.setBlueBits(5); + caps.setAlphaBits(1); + break; + case GL.GL_RGB565: + caps.setRedBits(5); + caps.setGreenBits(6); + caps.setBlueBits(5); + caps.setAlphaBits(0); + break; + case GL.GL_RGB8: + caps.setRedBits(8); + caps.setGreenBits(8); + caps.setBlueBits(8); + caps.setAlphaBits(0); + break; + case GL.GL_RGBA8: + caps.setRedBits(8); + caps.setGreenBits(8); + caps.setBlueBits(8); + caps.setAlphaBits(8); + break; + case GL.GL_DEPTH_COMPONENT16: + caps.setDepthBits(16); + break; + case GL.GL_DEPTH_COMPONENT24: + caps.setDepthBits(24); + break; + case GL.GL_DEPTH_COMPONENT32: + caps.setDepthBits(32); + break; + case GL.GL_STENCIL_INDEX1: + caps.setStencilBits(1); + break; + case GL.GL_STENCIL_INDEX4: + caps.setStencilBits(4); + break; + case GL.GL_STENCIL_INDEX8: + caps.setStencilBits(8); + break; + case GL.GL_DEPTH24_STENCIL8: + caps.setDepthBits(24); + caps.setStencilBits(8); + break; + default: + throw new IllegalArgumentException("format invalid: "+toHexString(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; } + + /** 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; } + + /** + * Initializes the attachment and set it's parameter, if uninitialized, i.e. name is <code>zero</code>. + * <pre> + final boolean init = 0 == name; + if( init ) { + do init .. + } + 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. + */ + public abstract boolean initialize(GL gl) throws GLException; + + /** + * Releases the attachment if initialized, i.e. name is not <code>zero</code>. + * <pre> + if(0 != name) { + do free .. + name = 0; + } + * </pre> + * @throws GLException if buffer release fails. + */ + public abstract void free(GL gl) throws GLException; + + /** + * <p> + * Comparison by {@link #type}, {@link #format}, {@link #width}, {@link #height} and {@link #name}. + * </p> + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + if( this == o ) return true; + if( ! ( o instanceof Attachment ) ) return false; + final Attachment a = (Attachment)o; + return type == a.type && + format == a.format && + width == a.width && + height== a.height && + name == a.name ; + } + + /** + * <p> + * Hashed by {@link #type}, {@link #format}, {@link #width}, {@link #height} and {@link #name}. + * </p> + * {@inheritDoc} + */ + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + int hash = 31 + type.ordinal(); + hash = ((hash << 5) - hash) + format; + hash = ((hash << 5) - hash) + width; + hash = ((hash << 5) - hash) + height; + hash = ((hash << 5) - hash) + name; + return hash; + } + + int objectHashCode() { return super.hashCode(); } + + @Override + public String toString() { + 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) { + 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: + return Type.STENCIL; + default: + throw new IllegalArgumentException("Invalid attachment point "+toHexString(attachmentPoint)); + } + } + } + + /** 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_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) { + 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) { + switch(type) { + case DEPTH_STENCIL: + case DEPTH: + case STENCIL: + case COLOR: + return type; + default: + throw new IllegalArgumentException("Invalid type: "+type); + } + } + + /** + * <p> + * Comparison by {@link #type}, {@link #format}, {@link #samples}, {@link #width}, {@link #height} and {@link #name}. + * </p> + * {@inheritDoc} + */ + @Override + public boolean equals(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}. + * </p> + * {@inheritDoc} + */ + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + int hash = super.hashCode(); + hash = ((hash << 5) - hash) + samples; + return hash; + } + + @Override + public boolean initialize(GL gl) throws GLException { + final boolean init = 0 == getName(); + if( init ) { + 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 ) { + ((GL2ES3)gl).glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, format, getWidth(), getHeight()); + } else { + gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, format, getWidth(), getHeight()); + } + 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.X: "+this); + } + } + return init; + } + + @Override + public void free(GL gl) { + final int[] name = new int[] { getName() }; + if( 0 != name[0] ) { + if(DEBUG) { + System.err.println("Attachment.free.0: "+this); + } + gl.glDeleteRenderbuffers(1, name, 0); + setName(0); + } + } + + @Override + public String toString() { + return getClass().getSimpleName()+"[type "+type+", format "+toHexString(format)+", samples "+samples+", "+getWidth()+"x"+getHeight()+ + ", name "+toHexString(getName())+", obj "+toHexString(objectHashCode())+"]"; + } + } + + /** Color render buffer attachment */ + public static class ColorAttachment extends RenderAttachment implements Colorbuffer { + public ColorAttachment(int iFormat, int samples, int width, int height, int name) { + super(Type.COLOR, iFormat, samples, width, height, name); + } + } + + /** Texture attachment */ + public static class TextureAttachment extends Attachment implements Colorbuffer { + /** details of the texture setup */ + public final int dataFormat, dataType, magFilter, minFilter, wrapS, wrapT; + + /** + * @param type allowed types are [ {@link Type#COLOR_TEXTURE}, {@link Type#DEPTH_TEXTURE}, {@link Type#STENCIL_TEXTURE} ] + * @param iFormat + * @param width + * @param height + * @param dataFormat + * @param dataType + * @param magFilter + * @param minFilter + * @param wrapS + * @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) { + super(validateType(type), iFormat, width, height, name); + this.dataFormat = dataFormat; + this.dataType = dataType; + this.magFilter = magFilter; + this.minFilter = minFilter; + this.wrapS = wrapS; + this.wrapT = wrapT; + } + + private static Type validateType(Type type) { + switch(type) { + case COLOR_TEXTURE: + case DEPTH_TEXTURE: + case STENCIL_TEXTURE: + return type; + 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. + */ + @Override + public boolean initialize(GL gl) throws GLException { + final boolean init = 0 == getName(); + if( init ) { + 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]); + if( 0 < magFilter ) { + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, magFilter); + } + if( 0 < minFilter ) { + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, minFilter); + } + if( 0 < wrapS ) { + 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); + } + 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); + } + if(DEBUG) { + System.err.println("Attachment.init.X: "+this); + } + } + return init; + } + + @Override + public void free(GL gl) { + final int[] name = new int[] { getName() }; + if( 0 != name[0] ) { + if(DEBUG) { + System.err.println("Attachment.free.0: "+this); + } + gl.glDeleteTextures(1, name, 0); + setName(0); + } + } + @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(int v) { + return "0x"+Integer.toHexString(v); + } + + /** + * 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 alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; + * @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); + } + + /** + * 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} + * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; + * @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 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; + // textureInternalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + // textureInternalFormat = alpha ? 4 : 3; + 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); + } + + /** + * 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 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 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, + magFilter, minFilter, wrapS, wrapT, 0 /* name */); + } + + private static boolean hasAlpha(int format) { + switch(format) { + case GL.GL_RGBA8: + case GL.GL_RGBA4: + case GL.GL_RGBA: + case GL.GL_BGRA: + case 4: + return true; + default: + return false; + } + } + + private boolean initialized; + private boolean fullFBOSupport; + private boolean rgba8Avail; + private boolean depth24Avail; + private boolean depth32Avail; + private boolean stencil01Avail; + private boolean stencil04Avail; + private boolean stencil08Avail; + private boolean stencil16Avail; + private boolean packedDepthStencilAvail; + private int maxColorAttachments, maxSamples, maxTextureSize, maxRenderbufferSize; + + private int width, height, samples; + private int vStatus; + private boolean ignoreStatus; + private int fbName; + private boolean bound; + + private int colorAttachmentCount; + private Colorbuffer[] colorAttachmentPoints; // colorbuffer attachment points + private RenderAttachment depth, stencil; // depth and stencil maybe equal in case of packed-depth-stencil + + private FBObject samplingSink; // MSAA sink + private TextureAttachment samplingSinkTexture; + private boolean samplingSinkDirty; + + // + // ColorAttachment helper .. + // + + private final void validateColorAttachmentPointRange(int point) { + if(!initialized) { + throw new GLException("FBO not initialized"); + } + if(maxColorAttachments != colorAttachmentPoints.length) { + throw new InternalError("maxColorAttachments "+maxColorAttachments+", array.lenght "+colorAttachmentPoints); + } + if(0 > point || point >= maxColorAttachments) { + throw new IllegalArgumentException("attachment point out of range: "+point+", should be within [0.."+(maxColorAttachments-1)+"], "+this); + } + } + + private final void validateAddColorAttachment(int point, Colorbuffer ca) { + validateColorAttachmentPointRange(point); + if( null != colorAttachmentPoints[point] ) { + throw new IllegalArgumentException("Cannot attach "+ca+", attachment point already in use by "+colorAttachmentPoints[point]+", "+this); + } + } + + 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); + } + colorAttachmentPoints[point] = ca; + colorAttachmentCount++; + } + + private final void removeColorAttachment(int point, 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); + } + colorAttachmentPoints[point] = null; + colorAttachmentCount--; + } + + /** + * 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) + */ + public final Colorbuffer getColorbuffer(int attachmentPoint) { + validateColorAttachmentPointRange(attachmentPoint); + return colorAttachmentPoints[attachmentPoint]; + } + + /** + * Finds the passed {@link Colorbuffer} within the valid range of attachment points + * using <i>reference</i> comparison only. + * <p> + * 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] + */ + public final int getColorbufferAttachmentPoint(Colorbuffer ca) { + for(int i=0; i<colorAttachmentPoints.length; i++) { + if( colorAttachmentPoints[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) + */ + public final Colorbuffer getColorbuffer(Colorbuffer ca) { + final int p = getColorbufferAttachmentPoint(ca); + return p>=0 ? getColorbuffer(p) : null; + } + + /** + * Creates an uninitialized FBObject instance. + * <p> + * Call {@link #init(GL, int, int, int)} .. etc to use it. + * </p> + */ + public FBObject() { + this.initialized = false; + + // TBD @ init + this.fullFBOSupport = false; + this.rgba8Avail = false; + this.depth24Avail = false; + this.depth32Avail = false; + this.stencil01Avail = false; + this.stencil04Avail = false; + this.stencil08Avail = false; + this.stencil16Avail = false; + this.packedDepthStencilAvail = false; + this.maxColorAttachments=-1; + this.maxSamples=-1; + this.maxTextureSize = 0; + this.maxRenderbufferSize = 0; + + this.width = 0; + this.height = 0; + this.samples = 0; + this.vStatus = -1; + this.ignoreStatus = false; + this.fbName = 0; + this.bound = false; + + this.colorAttachmentPoints = null; // at init .. + this.colorAttachmentCount = 0; + this.depth = null; + this.stencil = null; + + this.samplingSink = null; + this.samplingSinkTexture = 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"); + } + if( !gl.hasBasicFBOSupport() ) { + throw new GLException("FBO not supported w/ context: "+gl.getContext()+", "+this); + } + 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) || + 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]; + + checkPreGLError(gl); + + int realMaxColorAttachments = 1; + maxColorAttachments = 1; + if( fullFBOSupport || NV_fbo_color_attachments ) { + try { + val[0] = 0; + gl.glGetIntegerv(GL2ES2.GL_MAX_COLOR_ATTACHMENTS, val, 0); + realMaxColorAttachments = 1 <= val[0] ? val[0] : 1; // cap minimum to 1 + } catch (GLException gle) { gle.printStackTrace(); } + } + maxColorAttachments = realMaxColorAttachments <= 8 ? realMaxColorAttachments : 8; // cap to limit array size + + colorAttachmentPoints = new Colorbuffer[maxColorAttachments]; + colorAttachmentCount = 0; + + maxSamples = gl.getMaxRenderbufferSamples(); + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, val, 0); + maxTextureSize = val[0]; + gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, val, 0); + maxRenderbufferSize = val[0]; + + checkPreGLError(gl); + + if( 0 >= width ) { width = 1; } + if( 0 >= height ) { height = 1; } + this.width = width; + this.height = height; + this.samples = samples <= maxSamples ? samples : maxSamples; + + if(DEBUG) { + System.err.println("FBObject "+width+"x"+height+", "+samples+" -> "+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("maxRenderbufferSize: "+maxRenderbufferSize); + System.err.println("rgba8: "+rgba8Avail); + System.err.println("depth24: "+depth24Avail); + System.err.println("depth32: "+depth32Avail); + System.err.println("stencil01: "+stencil01Avail); + System.err.println("stencil04: "+stencil04Avail); + System.err.println("stencil08: "+stencil08Avail); + System.err.println("stencil16: "+stencil16Avail); + 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()); + } + + 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+"]"); + } + + resetSamplingSink(gl); + + // generate fbo .. + gl.glGenFramebuffers(1, val, 0); + fbName = val[0]; + if(0 == fbName) { + throw new GLException("null framebuffer"); + } + + // bind fbo .. + 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; + samplingSinkDirty = true; + initialized = true; + + vStatus = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // always incomplete w/o attachments! + if(DEBUG) { + System.err.println("FBObject.init(): "+this); + } + } + + /** + * 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. + * </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, false); + } + + /** + * 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. + * </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()}. + * @param resetSamplingSink <code>true</code> calls {@link #resetSamplingSink(GL)} immediatly. + * <code>false</code> postpones resetting the sampling sink until {@link #use(GL, TextureAttachment)} or {@link #syncSamplingSink(GL)}, + * allowing to use the samples sink's FBO and texture until then. The latter is useful to benefit + * from implicit double buffering while resetting the sink just before it's being used, eg. at swap-buffer. + * + * @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, boolean resetSamplingSink) { + if( !initialized ) { + init(gl, newWidth, newHeight, newSamples); + return; + } + + 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(DEBUG) { + System.err.println("FBObject.reset - START - "+width+"x"+height+", "+samples+" -> "+newWidth+"x"+newHeight+", "+newSamples+"; "+this); + } + + final boolean wasBound = isBound(); + + width = newWidth; + height = newHeight; + samples = newSamples; + + if(0 < samples && null == samplingSink ) { + // needs valid samplingSink for detach*() -> bind() + samplingSink = new FBObject(); + samplingSink.init(gl, width, height, 0); + } + detachAllImpl(gl, true , true); + if(resetSamplingSink) { + resetSamplingSink(gl); + } + + samplingSinkDirty = true; + + if(!wasBound) { + unbind(gl); + } + + if(DEBUG) { + System.err.println("FBObject.reset - END - "+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) { + 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); + } + if(null != stencil && stencil != depth) { + 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() + */ + public final int getStatus() { + return vStatus; + } + + /** return the {@link #getStatus()} as a string. */ + public final String getStatusString() { + return getStatusString(vStatus); + } + + public static final String getStatusString(int fbStatus) { + switch(fbStatus) { + case -1: + return "NOT A FBO"; + + case GL.GL_FRAMEBUFFER_COMPLETE: + return "OK"; + + case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return("FBO incomplete attachment\n"); + case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return("FBO missing attachment"); + case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return("FBO attached images must have same dimensions"); + case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: + return("FBO attached images must have same format"); + case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + return("FBO missing draw buffer"); + case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + 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("Unsupported FBO format"); + case GL2ES3.GL_FRAMEBUFFER_UNDEFINED: + return("FBO undefined"); + + case 0: + return("FBO implementation fault"); + default: + return("FBO incomplete, implementation ERROR "+toHexString(fbStatus)); + } + } + + /** + * The status may even be valid if incomplete during transition of attachments. + * @see #getStatus() + */ + public final boolean isStatusValid() { + 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 GL3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + if(0 == colorAttachmentCount || null == depth) { + // we are in transition + return true; + } + + case GL.GL_FRAMEBUFFER_UNSUPPORTED: + case GL2ES3.GL_FRAMEBUFFER_UNDEFINED: + + case 0: + default: + 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(); + if(DEBUG && GL.GL_NO_ERROR != glerr) { + System.err.println("Pre-existing GL error: "+toHexString(glerr)); + Thread.dumpStack(); + } + return glerr; + } + + private final boolean checkNoError(GL gl, int err, String exceptionMessage) throws GLException { + if(GL.GL_NO_ERROR != err) { + if(null != gl) { + destroy(gl); + } + if(null != exceptionMessage) { + throw new GLException(exceptionMessage+" GL Error "+toHexString(err)+" of "+this.toString()); + } + return false; + } + return true; + } + + 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>; + * @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) + */ + public final TextureAttachment attachTexture2D(GL gl, int attachmentPoint, boolean alpha) throws GLException { + return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(gl.getGLProfile(), alpha, width, height)); + } + + /** + * 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 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)); + } + + /** + * 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 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)); + } + + /** + * Creates a {@link ColorAttachment}, selecting the format automatically. + * + * @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) { + 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); + } + + /** + * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, to this FBO's instance at the given attachment point, + * selecting the 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>; + * @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 + * @see #createColorAttachment(boolean) + */ + public final ColorAttachment attachColorbuffer(GL gl, int attachmentPoint, boolean alpha) throws GLException { + return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, createColorAttachment(alpha)); + } + + /** + * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, 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 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 { + final Attachment.Type atype = Attachment.Type.determine(internalFormat); + if( Attachment.Type.COLOR != atype ) { + throw new IllegalArgumentException("colorformat invalid: "+toHexString(internalFormat)+", "+this); + } + + return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, new ColorAttachment(internalFormat, samples, width, height, 0)); + } + + /** + * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment} or {@link TextureAttachment}, + * to this FBO's instance at the given attachment point. + * + * <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. + * </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} + */ + public final Colorbuffer attachColorbuffer(GL gl, int attachmentPoint, Colorbuffer colbuf) throws GLException { + bind(gl); + return attachColorbufferImpl(gl, attachmentPoint, colbuf); + } + + private final Colorbuffer attachColorbufferImpl(GL gl, int attachmentPoint, Colorbuffer colbuf) throws GLException { + validateAddColorAttachment(attachmentPoint, colbuf); + + final boolean initializedColorbuf = colbuf.initialize(gl); + addColorAttachment(attachmentPoint, colbuf); + + if(colbuf instanceof TextureAttachment) { + final TextureAttachment texA = (TextureAttachment) colbuf; + + 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); + 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; + + // Attach the color buffer + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, + GL.GL_RENDERBUFFER, colA.getName()); + + if(!ignoreStatus) { + updateStatus(gl); + if(!isStatusValid()) { + detachColorbuffer(gl, attachmentPoint, true); + throw new GLException("attachColorbuffer "+colA+" at "+attachmentPoint+" failed "+getStatusString()+", "+this); + } + } + } + if(DEBUG) { + System.err.println("FBObject.attachColorbuffer.X: [attachmentPoint "+attachmentPoint+", colbuf "+colbuf+"]: "+this); + } + return colbuf; + } + + /** + * Attaches one depth, stencil or packed-depth-stencil buffer to this FBO's instance, + * selecting the internalFormat automatically. + * <p> + * 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> + * <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) + * @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; + } + 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; + } + 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"); + } + 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"); + } + } + 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>. + * <p> + * Stencil and depth buffer can be attached only once. + * </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 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} + * or {@link GL#GL_DEPTH24_STENCIL8} + * @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, 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: "+toHexString(internalFormat)+", "+this); + } + attachRenderbufferImpl(gl, atype, internalFormat); + } + + protected final void attachRenderbufferImpl(GL gl, Attachment.Type atype, 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+", "+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+", "+toHexString(internalFormat)+", "+this); + } + bind(gl); + + attachRenderbufferImpl2(gl, atype, internalFormat); + } + + private final void attachRenderbufferImpl2(GL gl, Attachment.Type atype, int internalFormat) throws GLException { + if( Attachment.Type.DEPTH == atype ) { + if(null == depth) { + depth = new RenderAttachment(Type.DEPTH, internalFormat, samples, width, height, 0); + } else { + depth.setSize(width, height); + depth.setSamples(samples); + } + depth.initialize(gl); + } else if( Attachment.Type.STENCIL == atype ) { + if(null == stencil) { + stencil = new RenderAttachment(Type.STENCIL, internalFormat, samples, width, height, 0); + } else { + stencil.setSize(width, height); + stencil.setSamples(samples); + } + stencil.initialize(gl); + } else if( Attachment.Type.DEPTH_STENCIL == atype ) { + if(null == depth) { + if(null != stencil) { + throw new InternalError("XXX: DEPTH_STENCIL, depth was null, stencil not: "+this.toString()); + } + depth = new RenderAttachment(Type.DEPTH_STENCIL, internalFormat, samples, width, height, 0); + } else { + depth.setSize(width, height); + depth.setSamples(samples); + } + depth.initialize(gl); + // DEPTH_STENCIL shares buffer w/ depth and stencil + stencil = depth; + } + + // 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_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, stencil.getName()); + } + + if(!ignoreStatus) { + updateStatus(gl); + if( !isStatusValid() ) { + detachRenderbuffer(gl, atype, true); + throw new GLException("renderbuffer [attachmentType "+atype+", iformat "+toHexString(internalFormat)+"] failed: "+this.getStatusString()+": "+this.toString()); + } + } + + if(DEBUG) { + System.err.println("FBObject.attachRenderbuffer.X: [attachmentType "+atype+", iformat "+toHexString(internalFormat)+"]: "+this); + } + } + + /** + * 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 { + bind(gl); + + final Colorbuffer res = detachColorbufferImpl(gl, attachmentPoint, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + if(null == res) { + throw new IllegalArgumentException("ColorAttachment at "+attachmentPoint+", not attached, "+this); + } + if(DEBUG) { + 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) { + return null; + } + + removeColorAttachment(attachmentPoint, colbuf); + + if(colbuf instanceof TextureAttachment) { + final TextureAttachment texA = (TextureAttachment) colbuf; + 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: + } + } + if(DetachAction.RECREATE == detachAction) { + if(samples == 0) { + // stay non MSAA + texA.setSize(width, height); + } else { + // switch to MSAA + colbuf = createColorAttachment(hasAlpha(texA.format)); + } + attachColorbufferImpl(gl, attachmentPoint, colbuf); + } + } else if(colbuf instanceof ColorAttachment) { + final ColorAttachment colA = (ColorAttachment) colbuf; + if( 0 != colA.getName() ) { + 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: + } + } + if(DetachAction.RECREATE == detachAction) { + if(samples > 0) { + // stay MSAA + colA.setSize(width, height); + colA.setSamples(samples); + } else { + // switch to non MSAA + if(null != samplingSinkTexture) { + colbuf = createColorTextureAttachment(samplingSinkTexture.format, width, height, + samplingSinkTexture.dataFormat, samplingSinkTexture.dataType, + samplingSinkTexture.magFilter, samplingSinkTexture.minFilter, + samplingSinkTexture.wrapS, samplingSinkTexture.wrapT); + } else { + colbuf = createColorTextureAttachment(gl.getGLProfile(), true, width, height); + } + } + attachColorbuffer(gl, attachmentPoint, colbuf); + } + } + return colbuf; + } + + private final void freeAllColorbufferImpl(GL gl) { + for(int i=0; i<maxColorAttachments; i++) { + final Colorbuffer colbuf = colorAttachmentPoints[i]; // shortcut, don't validate here + + if(null == colbuf) { + return; + } + + if(colbuf instanceof TextureAttachment) { + final TextureAttachment texA = (TextureAttachment) colbuf; + 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 if(colbuf instanceof ColorAttachment) { + final ColorAttachment colA = (ColorAttachment) colbuf; + if( 0 != colA.getName() ) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0 + i, + GL.GL_RENDERBUFFER, 0); + } + colA.free(gl); + } + } + } + + /** + * + * @param gl + * @param dispose true if the Colorbuffer shall be disposed + * @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 { + bind(gl); + detachRenderbufferImpl(gl, atype, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + if(DEBUG) { + 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) { + 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 { + switch ( atype ) { + case DEPTH: + case STENCIL: + case DEPTH_STENCIL: + break; + default: + throw new IllegalArgumentException("only depth/stencil types allowed, was "+atype+", "+this); + } + if( null == depth && null == stencil ) { + return ; // nop + } + final boolean packed = isDepthStencilPackedFormat(); + if( packed ) { + // Note: DEPTH_STENCIL shares buffer w/ depth and stencil + atype = Attachment.Type.DEPTH_STENCIL; + } + switch ( atype ) { + case DEPTH: + if( null != depth ) { + final int format = depth.format; + if( 0 != depth.getName() ) { + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); + switch(detachAction) { + case DISPOSE: + case RECREATE: + depth.free(gl); + break; + default: + } + } + if(DetachAction.RECREATE == detachAction) { + attachRenderbufferImpl2(gl, atype, format); + } else { + depth = null; + } + } + break; + case STENCIL: + if( 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, atype, format); + } else { + stencil = null; + } + } + break; + case DEPTH_STENCIL: + if( null != depth ) { + final int format = depth.format; + 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); + } + switch(detachAction) { + case DISPOSE: + case RECREATE: + depth.free(gl); + break; + default: + } + } + 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: // handled + } + } + + private final void freeAllRenderbufferImpl(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( !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, if initialized!</p> + * <p> + * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. + * </p> + * @param gl the current GL context + */ + public final void detachAll(GL gl) { + if(null != samplingSink) { + samplingSink.detachAll(gl); + } + detachAllImpl(gl, true/* detachNonColorbuffer */, false /* recreate */); + } + + /** + * Detaches all {@link ColorAttachment}s and {@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> + * @param gl the current GL context + */ + public final void detachAllColorbuffer(GL gl) { + if(null != samplingSink) { + samplingSink.detachAllColorbuffer(gl); + } + detachAllImpl(gl, false/* detachNonColorbuffer */, false /* recreate */); + } + + /** + * 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> + * @param gl the current GL context + */ + public final void detachAllTexturebuffer(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(DEBUG) { + System.err.println("FBObject.detachAllTexturebuffer.X: "+this); + } + } + + public final void detachAllRenderbuffer(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) { + 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); + } + if( !recreate && colorAttachmentCount>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 + /* if(true) { + throw new GLException("Simulating bug 617, reset FBO failure"); + } */ + updateStatus(gl); + if(!isStatusValid()) { + throw new GLException("detachAllImpl failed "+getStatusString()+", "+this); + } + } + } finally { + ignoreStatus = false; + } + if(DEBUG) { + System.err.println("FBObject.detachAll.X: [resetNonColorbuffer "+detachNonColorbuffer+", recreate "+recreate+"]: "+this); + } + } + + /** + * @param gl the current GL context + */ + public final void destroy(GL gl) { + if(!initialized) { + return; + } + if(DEBUG) { + System.err.println("FBObject.destroy.0: "+this); + // Thread.dumpStack(); + } + if( null != samplingSink && samplingSink.isInitialized() ) { + samplingSink.destroy(gl); + } + + detachAllImpl(gl, true /* detachNonColorbuffer */, false /* recreate */); + + // 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]; + if(0!=fb_cache) { + name[0] = fb_cache; + gl.glDeleteFramebuffers(1, name, 0); + } + initialized = false; + bound = false; + if(DEBUG) { + System.err.println("FBObject.destroy.X: "+this); + } + } + + private final boolean sampleSinkSizeMismatch() { + return samplingSink.getWidth() != width || samplingSink.getHeight() != height ; + } + private final boolean sampleSinkTexMismatch() { + return null == samplingSinkTexture || 0 == samplingSinkTexture.getName() ; + } + private final boolean sampleSinkDepthStencilMismatch() { + final boolean depthMismatch = ( null != depth && null == samplingSink.depth ) || + ( null != depth && null != samplingSink.depth && + depth.format != samplingSink.depth.format ); + + final boolean stencilMismatch = ( null != stencil && null == samplingSink.stencil ) || + ( null != stencil && null != samplingSink.stencil && + stencil.format != samplingSink.stencil.format ); + + return depthMismatch || stencilMismatch; + } + + /** + * Manually reset 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> + * 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> + * @param gl the current GL context + * @throws GLException in case of an error, i.e. size too big, etc .. + */ + public final void resetSamplingSink(GL gl) throws GLException { + if(0 == samples) { + // MSAA off + if(null != samplingSink && samplingSink.initialized) { + // cleanup + samplingSink.detachAll(gl); + } + return; + } + + if(null == samplingSink ) { + samplingSink = new FBObject(); + } + + if(!samplingSink.initialized) { + samplingSink.init(gl, width, height, 0); + } + + boolean sampleSinkSizeMismatch = sampleSinkSizeMismatch(); + boolean sampleSinkTexMismatch = sampleSinkTexMismatch(); + boolean sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); + + /** if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.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(DEBUG) { + System.err.println("FBObject.resetSamplingSink: BEGIN\n\tTHIS "+this+",\n\tSINK "+samplingSink+ + "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + } + + if( sampleSinkDepthStencilMismatch ) { + samplingSink.detachAllRenderbuffer(gl); + } + + if( sampleSinkSizeMismatch ) { + samplingSink.reset(gl, width, height); + } + + if(null == samplingSinkTexture) { + samplingSinkTexture = samplingSink.attachTexture2D(gl, 0, true); + } else if( 0 == samplingSinkTexture.getName() ) { + samplingSinkTexture.setSize(width, height); + samplingSink.attachColorbuffer(gl, 0, samplingSinkTexture); + } + + if( sampleSinkDepthStencilMismatch ) { + samplingSink.attachRenderbuffer(gl, depth.format); + if( null != stencil && !isDepthStencilPackedFormat() ) { + samplingSink.attachRenderbuffer(gl, stencil.format); + } + } + + sampleSinkSizeMismatch = sampleSinkSizeMismatch(); + sampleSinkTexMismatch = sampleSinkTexMismatch(); + sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); + if(sampleSinkSizeMismatch || sampleSinkTexMismatch || sampleSinkDepthStencilMismatch) { + throw new InternalError("Samples sink mismatch after reset: \n\tTHIS "+this+",\n\t SINK "+samplingSink+ + "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + } + + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink: END\n\tTHIS "+this+",\n\tSINK "+samplingSink+ + "\n\t size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + } + } + + /** + * Setting this FBO sampling sink. + * @param newSamplingSink the new 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 + */ + public FBObject setSamplingSink(FBObject newSamplingSink) throws GLException { + final FBObject prev = samplingSink; + if( null == newSamplingSink) { + samplingSink = null; + samplingSinkTexture = null; + } else if( samples > 0 ) { + if( newSamplingSink.getNumSamples() > 0 ) { + throw new GLException("SamplingSink FBO cannot use MSAA itself: "+newSamplingSink); + } + samplingSink = newSamplingSink; + samplingSinkTexture = (TextureAttachment) newSamplingSink.getColorbuffer(0); + } else { + throw new GLException("Setting SamplingSink for non MSAA FBO not allowed: "+this); + } + 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> + * In case you have attached more than one color buffer, + * 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 { + if(!bound || fbName != gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER)) { + checkInitialized(); + if(samples > 0 && fullFBOSupport) { + // draw to multisampling - read from samplesSink + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, getWriteFramebuffer()); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, getReadFramebuffer()); + } else { + // one for all + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, getWriteFramebuffer()); + } + + bound = 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 + * 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 { + if(bound) { + if(fullFBOSupport) { + // default read/draw buffers, may utilize GLContext/GLDrawable override of + // GLContext.getDefaultDrawFramebuffer() and GLContext.getDefaultReadFramebuffer() + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, 0); + } else { + 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> + * Only use this method if a subsequent {@link #unbind(GL)}, {@link #use(GL, TextureAttachment)} or {@link #bind(GL)} + * follows on <i>any</i> FBO. + * </p> + */ + public final void markUnbound() { + 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) ; + 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()}. + * <p> + * Method also resets the sampling sink configuration via {@link #resetSamplingSink(GL)} if used and required. + * </p> + * <p> + * Method is called automatically by {@link #use(GL, TextureAttachment)}. + * </p> + * <p> + * 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 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 + */ + public final void syncSamplingSink(GL gl) { + markUnbound(); + if(samples>0 && samplingSinkDirty) { + samplingSinkDirty = false; + resetSamplingSink(gl); + checkPreGLError(gl); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, fbName); + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, samplingSink.getWriteFramebuffer()); + ((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); + checkNoError(null, gl.glGetError(), "FBObject syncSampleSink"); // throws GLException if error + } + if(fullFBOSupport) { + // default read/draw buffers, may utilize GLContext/GLDrawable override of + // GLContext.getDefaultDrawFramebuffer() and GLContext.getDefaultReadFramebuffer() + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, 0); + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, 0); + } else { + 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 #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 + */ + public final void use(GL gl, TextureAttachment ta) throws IllegalArgumentException { + if(null == ta) { throw new IllegalArgumentException("Null TextureAttachment, this: "+toString()); } + syncSamplingSink(gl); + 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) { + unbind(gl); + gl.glBindTexture(GL.GL_TEXTURE_2D, 0); // don't use it + } + + /** @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 { + checkInitialized(); + switch(bits) { + case 16: return true; + case 24: return depth24Avail; + case 32: return depth32Avail; + 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 { + checkInitialized(); + switch(bits) { + case 1: return stencil01Avail; + case 4: return stencil04Avail; + case 8: return stencil08Avail; + case 16: return stencil16Avail; + 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, boolean)}, otherwise <code>false</code> + */ + public final boolean isInitialized() { return initialized; } + /** Returns the width */ + public final int getWidth() { return width; } + /** Returns the height */ + public final int getHeight() { return height; } + /** Returns the number of samples for multisampling (MSAA). zero if no multisampling is used. */ + public final int getNumSamples() { return samples; } + /** 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 ) ? samplingSink.getReadFramebuffer() : fbName; } + public final int getDefaultReadBuffer() { return GL.GL_COLOR_ATTACHMENT0; } + /** 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 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. */ + public final RenderAttachment getDepthAttachment() { return depth; } + + /** Return the complete multisampling {@link FBObject} sink, if using multisampling. */ + public final FBObject getSamplingSinkFBO() { return samplingSink; } + + /** Return the multisampling {@link TextureAttachment} sink, if using multisampling. */ + public final TextureAttachment getSamplingSink() { return samplingSinkTexture; } + /** + * 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 samplingSinkDirty; } + + 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 "+samplingSinkTexture+", hasSamplesSink "+(null != samplingSink)+ + ", state "+getStatusString()+", obj "+toHexString(objectHashCode())+"]"; + } + + private final void updateStatus(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 new file mode 100644 index 000000000..6b1bb0e5e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -0,0 +1,189 @@ +/** + * 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 javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.WindowClosingProtocol; +import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLException; + +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + +import jogamp.opengl.GLAutoDrawableBase; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableImpl; + + +/** + * Fully functional {@link GLAutoDrawable} implementation + * 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 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> + * </ul> + * and setup a {@link com.jogamp.newt.Window#setWindowDestroyNotifyAction(Runnable) custom toolkit destruction} issuing {@link #windowDestroyNotifyOp()}. + * </p> + * <p> + * See example {@link com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateNEWT TestGLAutoDrawableDelegateNEWT}. + * </p> + */ +public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAutoDrawable { + /** + * <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 + * the drawable is created w/ it's own new instance, e.g. offscreen drawables, + * 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) { + super((GLDrawableImpl)drawable, (GLContextImpl)context, ownDevice); + if(null == drawable) { + throw new IllegalArgumentException("null drawable"); + } + this.upstreamWidget = upstreamWidget; + this.lock = ( null != lock ) ? lock : LockFactory.createRecursiveLock() ; + } + + // + // expose default methods + // + + /** Default implementation to handle repaint events from the windowing system */ + 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) { + super.defaultWindowResizedOp(newWidth, newHeight); + } + + /** + * Implementation to handle destroy notifications from the windowing system. + * + * <p> + * 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> + */ + public final void windowDestroyNotifyOp() { + super.defaultWindowDestroyNotifyOp(); + } + + // + // Complete GLAutoDrawable + // + + private Object upstreamWidget; + private final RecursiveLock lock; + + @Override + protected final RecursiveLock getLock() { return lock; } + + @Override + public final Object getUpstreamWidget() { + return upstreamWidget; + } + + /** + * Set the upstream UI toolkit object. + * @see #getUpstreamWidget() + */ + public final void setUpstreamWidget(Object newUpstreamWidget) { + upstreamWidget = newUpstreamWidget; + } + + /** + * {@inheritDoc} + * <p> + * This implementation calls {@link #defaultDestroy()}. + * </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()}. + * </p> + */ + @Override + public final void destroy() { + defaultDestroy(); + } + + @Override + protected void destroyImplInLock() { + super.destroyImplInLock(); + } + + @Override + public void display() { + defaultDisplay(); + } + + // + // GLDrawable delegation + // + + @Override + public final GLDrawableFactory getFactory() { + return drawable.getFactory(); + } + + @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..1b4187668 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java @@ -0,0 +1,435 @@ +/** + * 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.nativewindow.MutableGraphicsConfiguration; + +/** + * 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(AbstractGraphicsDevice upstreamDevice, boolean proxyOwnsUpstreamDevice, AbstractGraphicsDevice device, + GLCapabilitiesImmutable caps, + GLContext context, int count, GLAnimatorControl anim, boolean animStarted) { + this.upstreamDevice = upstreamDevice; + this.proxyOwnsUpstreamDevice = proxyOwnsUpstreamDevice; + this.device = device; + this.caps = caps; + 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 boolean owner; + + /** + * 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) + device.close(); + owner = false; + } + } + + private static AbstractGraphicsDevice cloneDevice(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> + * + * @param a {@link GLAutoDrawable} source to move components from + * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. + * + * @see #moveTo(GLAutoDrawable) + */ + public static GLEventListenerState moveFrom(GLAutoDrawable a) { + final GLAnimatorControl aAnim = a.getAnimator(); + final boolean aAnimStarted; + if( null != aAnim ) { + aAnimStarted = aAnim.isStarted(); + aAnim.remove(a); // also handles ECT + } else { + aAnimStarted = false; + } + + final GLEventListenerState glls; + final NativeSurface aSurface = a.getNativeSurface(); + final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + try { + final int aSz = a.getGLEventListenerCount(); + + // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable + // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device! + final AbstractGraphicsConfiguration aCfg = aSurface.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: "+aSurface.getClass().getName()/*+", "+aSurface*/); + } + final AbstractGraphicsDevice aUpDevice2; + final boolean proxyOwnsUpstreamDevice; + { + AbstractGraphicsDevice _aUpDevice2 = null; + if(aSurface instanceof ProxySurface) { + final ProxySurface aProxy = (ProxySurface)aSurface; + 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: "+aSurface.getClass().getName()+", "+aSurface); + 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, a.getContext(), aSz, aAnim, aAnimStarted); + + // + // remove and cache all GLEventListener and their init-state + // + for(int i=0; i<aSz; i++) { + final GLEventListener l = a.getGLEventListener(0); + glls.listenersInit[i] = a.getGLEventListenerInitState(l); + glls.listeners[i] = a.removeGLEventListener( l ); + } + + // + // trigger glFinish to sync GL ctx + // + a.invoke(true, glFinish); + + a.setContext( null, false ); + + } finally { + if( surfaceLocked ) { + aSurface.unlockSurface(); + } + } + + 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> + * Note: After this operation, the GLEventListenerState reference should be released. + * </p> + * + * @param a {@link GLAutoDrawable} destination to move GLEventListenerState components to + * + * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> + * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one. + * + * @see #moveFrom(GLAutoDrawable) + * @see #isOwner() + */ + public final void moveTo(GLAutoDrawable a) { + final GLAnimatorControl aAnim = a.getAnimator(); + final boolean hasAnimator = null != aAnim; + final boolean aPaused; + if( hasAnimator ) { + aPaused = aAnim.pause(); + aAnim.remove(a); // also handles ECT + if( aPaused ) { + aAnim.resume(); + } + } else { + aPaused = false; + } + + final List<GLRunnable> aGLCmds = new ArrayList<GLRunnable>(); + final int aSz = listenerCount(); + + final NativeSurface aSurface = a.getNativeSurface(); + final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + final boolean aRealized; + try { + + final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) aSurface.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(aSurface instanceof ProxySurface) { + aProxy = (ProxySurface)aSurface; + 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) + a.setContext( null, true ); + aRealized = a.isRealized(); + if( aRealized && null != aUpSurface ) { + // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook + a.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( aRealized && null != aUpSurface ) { + a.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)); + } + a.setContext( context, false ); + } finally { + if( surfaceLocked ) { + aSurface.unlockSurface(); + } + } + 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 ReshapeGLEventListener( listeners[i] ) ); + } + } + aGLCmds.add(glFinish); + a.invoke(aRealized, 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]; + a.addGLEventListener( l ); + a.setGLEventListenerInitState(l, listenersInit[i]); + listeners[i] = null; + } + + if( hasAnimator ) { + // prefer already bound animator + aAnim.add(a); + if( aPaused ) { + aAnim.resume(); + } + } else if ( null != anim ) { + // use previously bound animator + anim.add(a); // also handles ECT + if(animStarted) { + anim.start(); + } + } + } + + public static GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getWidth(), drawable.getHeight()); + return true; + } + }; + + public static GLRunnable glFinish = new GLRunnable() { + @Override + public boolean run(GLAutoDrawable drawable) { + drawable.getGL().glFinish(); + return true; + } + }; + + public static class ReshapeGLEventListener implements GLRunnable { + private GLEventListener listener; + public ReshapeGLEventListener(GLEventListener listener) { + this.listener = listener; + } + @Override + public boolean run(GLAutoDrawable drawable) { + listener.reshape(drawable, 0, 0, drawable.getWidth(), drawable.getHeight()); + return true; + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLExtensions.java b/src/jogl/classes/com/jogamp/opengl/GLExtensions.java new file mode 100644 index 000000000..da9e08a32 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLExtensions.java @@ -0,0 +1,87 @@ +/** + * 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; + +/** + * 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"; + public static final String EXT_framebuffer_blit = "GL_EXT_framebuffer_blit"; + public static final String EXT_framebuffer_multisample = "GL_EXT_framebuffer_multisample"; + public static final String EXT_packed_depth_stencil = "GL_EXT_packed_depth_stencil"; + public static final String OES_depth24 = "GL_OES_depth24"; + 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"; + public static final String NV_texture_rectangle = "GL_NV_texture_rectangle"; + public static final String EXT_texture_format_BGRA8888 = "GL_EXT_texture_format_BGRA8888"; + public static final String IMG_texture_format_BGRA8888 = "GL_IMG_texture_format_BGRA8888"; + public static final String EXT_texture_compression_s3tc = "GL_EXT_texture_compression_s3tc"; + 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"; + 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_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..a643d81a9 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java @@ -0,0 +1,475 @@ +/** + * 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 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> + * Quirk can also be enabled via property: <code>jogl.fbo.force.min</code>. + */ + 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; + + /** Number of quirks known. */ + public static final int COUNT = 17; + + private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", + "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", + "GLNonCompliant", "GLFlushBeforeRelease", "DontCloseX11Display", + "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext", + "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request", + "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly" + }; + + 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(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(AbstractGraphicsDevice device1, AbstractGraphicsDevice device2) { + return device1.getUniqueID() == device2.getUniqueID(); + } + + /** + * {@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(AbstractGraphicsDevice device, int[] quirks, int offset, 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(AbstractGraphicsDevice device, 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(AbstractGraphicsDevice device, 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(AbstractGraphicsDevice device, 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(int[] quirks, int offset, int len) throws IllegalArgumentException { + this(); + addQuirks(quirks, offset, len); + } + + /** + * @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(int[] quirks, int offset, 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(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(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<COUNT; 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(int quirk) throws IllegalArgumentException { + if( !( 0 <= quirk && quirk < COUNT ) ) { + throw new IllegalArgumentException("Quirks must be in range [0.."+COUNT+"[, 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(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 c8e5d212b..40f0d180f 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; @@ -90,19 +90,15 @@ public class JoglVersion extends JogampVersion { sb.append("\tnone").append(Platform.getNewline()); } sb.append(Platform.getNewline()); - return sb; + return sb; } - - public static StringBuilder getDefaultOpenGLInfo(StringBuilder sb) { + + public static StringBuilder getAllAvailableCapabilitiesInfo(AbstractGraphicsDevice device, StringBuilder sb) { if(null==sb) { sb = new StringBuilder(); } - final AbstractGraphicsDevice device = GLProfile.getDefaultDevice(); - sb.append("Default Profiles ").append(Platform.getNewline()); - if(null!=device) { - GLProfile.glAvailabilityToString(device, sb, "\t", 1); - } else { - sb.append("none"); + if(null == device) { + device = GLProfile.getDefaultDevice(); } sb.append(Platform.getNewline()).append(Platform.getNewline()); sb.append("Desktop Capabilities: ").append(Platform.getNewline()); @@ -111,52 +107,116 @@ public class JoglVersion extends JogampVersion { getAvailableCapabilitiesInfo(GLDrawableFactory.getEGLFactory(), device, sb); return sb; } - + + public static StringBuilder getDefaultOpenGLInfo(AbstractGraphicsDevice device, StringBuilder sb, boolean withCapabilitiesInfo) { + if(null==sb) { + sb = new StringBuilder(); + } + if(null == device) { + device = GLProfile.getDefaultDevice(); + } + 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 = getAllAvailableCapabilitiesInfo(device, sb); + } + return sb; + } + public static StringBuilder getGLInfo(GL gl, StringBuilder sb) { + return getGLInfo(gl, sb, false); + } + public static StringBuilder getGLInfo(GL gl, StringBuilder sb, boolean withCapabilitiesAndExtensionInfo) { AbstractGraphicsDevice device = gl.getContext().getGLDrawable().getNativeSurface() .getGraphicsConfiguration().getScreen().getDevice(); if(null==sb) { sb = new StringBuilder(); } - GLContext ctx = gl.getContext(); 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()); + + sb = getGLStrings(gl, sb, withCapabilitiesAndExtensionInfo); + + if( withCapabilitiesAndExtensionInfo ) { + sb = getAllAvailableCapabilitiesInfo(device, sb); + } + return sb; + } + + public static StringBuilder getGLStrings(GL gl, StringBuilder sb) { + return getGLStrings(gl, sb, true); + } + + public static StringBuilder getGLStrings(GL gl, StringBuilder sb, boolean withExtensions) { + if(null==sb) { + sb = new StringBuilder(); + } + final GLContext ctx = gl.getContext(); sb.append("Swap Interval ").append(gl.getSwapInterval()); 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 StringBuilder getBriefOSGLBuildInfo(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(String args[]) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); diff --git a/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java b/src/jogl/classes/com/jogamp/opengl/cg/CgDynamicLibraryBundleInfo.java index d901096bc..4270607f2 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()) { - // Cg class and natives are available in their single atomic JAR files only - JNILibLoaderBase.addNativeJarLibs(CgDynamicLibraryBundleInfo.class, "jogl_cg", null); + // only: jogl-cg.jar -> jogl-cg-natives-<os.and.arch>.jar [atomic JAR files only] + JNILibLoaderBase.addNativeJarLibs(new Class<?>[] { CgDynamicLibraryBundleInfo.class }, null, 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 @@ -86,19 +92,19 @@ public class CgDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo { public final long toolGetProcAddress(long toolGetProcAddressHandle, String funcName) { return 0; } - + @Override - public boolean useToolGetProcAdressFirst(String funcName) { + public final boolean useToolGetProcAdressFirst(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..3e42f4d70 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. */ 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..33add46c2 --- /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..d98815d9f --- /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..5efad433a --- /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..b7dbf183f 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) { 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..191a83241 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java @@ -0,0 +1,573 @@ +/** + * 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 com.jogamp.common.os.Platform; + +/** + * 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) { + final 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) { + final 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 (also result) + * @param b 4x4 matrix in column-major order + */ + public static final void multMatrixf(final float[] a, int a_off, final float[] b, int b_off) { + 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[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 + a[a_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] ; + a[a_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] ; + a[a_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] ; + a[a_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 (also result) + * @param b 4x4 matrix in column-major order + */ + public static final void multMatrixf(final FloatBuffer a, final float[] b, int b_off) { + final int aP = a.position(); + for (int i = 0; i < 4; i++) { + // one row in column-major order + final int aP_i = aP+i; + 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 + a.put(aP_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] ); + a.put(aP_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] ); + a.put(aP_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] ); + a.put(aP_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) ); + } + } + + /** + * @param a 4x4 matrix in column-major order (also result) + * @param b 4x4 matrix in column-major order + */ + public static final void multMatrixf(final FloatBuffer a, final FloatBuffer b) { + final int aP = a.position(); + final int bP = b.position(); + for (int i = 0; i < 4; i++) { + // one row in column-major order + final int aP_i = aP+i; + 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 + a.put(aP_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) ); + a.put(aP_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) ); + a.put(aP_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) ); + a.put(aP_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) ); + } + } + + /** + * @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, float[] d, int d_off) { + final int aP = a.position(); + final int bP = b.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[d_off+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[d_off+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[d_off+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[d_off+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, int v_out_off) { + for (int i = 0; i < 4; i++) { + // (one matrix row in column-major order) X (column vector) + v_out[i + v_out_off] = + 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, float[] v_in, int v_in_off, float[] v_out, int v_out_off) { + final int matrixPos = m_in.position(); + for (int i = 0; i < 4; i++) { + // (one matrix row in column-major order) X (column vector) + v_out[i+v_out_off] = + v_in[0+v_in_off] * m_in.get(0*4+i+matrixPos) + + v_in[1+v_in_off] * m_in.get(1*4+i+matrixPos) + + v_in[2+v_in_off] * m_in.get(2*4+i+matrixPos) + + v_in[3+v_in_off] * m_in.get(3*4+i+matrixPos); + } + } + + /** + * @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, float[] v_in, float[] v_out) { + final int matrixPos = m_in.position(); + 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.get(0*4+i+matrixPos) + + v_in[1] * m_in.get(1*4+i+matrixPos) + + v_in[2] * m_in.get(2*4+i+matrixPos) + + v_in[3] * m_in.get(3*4+i+matrixPos); + } + } + + /** + * @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) { + final int inPos = v_in.position(); + final int outPos = v_out.position(); + final 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)); + } + } + + /** + * @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, String f, FloatBuffer a, int aOffset, int rows, int columns, boolean rowMajorOrder, 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, String f, float[] a, int aOffset, int rows, int columns, boolean rowMajorOrder, 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 + */ + public static StringBuilder matrixToString(StringBuilder sb, String rowPrefix, String f, FloatBuffer a, int aOffset, int rows, int columns, 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, String rowPrefix, String f, float[] a, int aOffset, int rows, int columns, 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 + */ + public static StringBuilder matrixToString(StringBuilder sb, String rowPrefix, String f, FloatBuffer a, int aOffset, FloatBuffer b, int bOffset, int rows, int columns, 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, String rowPrefix, String f, float[] a, int aOffset, float[] b, int bOffset, int rows, int columns, 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; + } + + public static final float E = 2.7182818284590452354f; + + public static final float PI = 3.14159265358979323846f; + + public static float abs(float a) { return (float) java.lang.Math.abs(a); } + + public static float pow(float a, float b) { return (float) java.lang.Math.pow(a, b); } + + public static float sin(float a) { return (float) java.lang.Math.sin(a); } + + public static float cos(float a) { return (float) java.lang.Math.cos(a); } + + public static float acos(float a) { return (float) java.lang.Math.acos(a); } + + public static float sqrt(float a) { return (float) java.lang.Math.sqrt(a); } + +}
\ No newline at end of file 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..52a59c599 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java @@ -0,0 +1,414 @@ +/** + * 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; + +public class Quaternion { + protected float x, y, z, w; + + public Quaternion() { + setIdentity(); + } + + public Quaternion(Quaternion q) { + x = q.x; + y = q.y; + z = q.z; + w = q.w; + } + + 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) { + final float theta = FloatUtil.acos(VectorUtil.dot(vector1, vector2)); + final float[] cross = VectorUtil.cross(vector1, vector2); + fromAxis(cross, theta); + } + + /*** + * Constructor to create a rotation based quaternion from axis vector and angle + * @param vector axis vector + * @param angle rotation angle (rads) + * @see #fromAxis(float[], float) + */ + public Quaternion(float[] vector, float angle) { + fromAxis(vector, angle); + } + + /*** + * Initialize this quaternion with given axis vector and rotation angle + * + * @param vector axis vector + * @param angle rotation angle (rads) + */ + public void fromAxis(float[] vector, float angle) { + final float halfangle = angle * 0.5f; + final float sin = FloatUtil.sin(halfangle); + final float[] nv = VectorUtil.normalize(vector); + x = (nv[0] * sin); + y = (nv[1] * sin); + z = (nv[2] * sin); + w = FloatUtil.cos(halfangle); + } + + /** + * Transform the rotational quaternion to axis based rotation angles + * + * @return new float[4] with ,theta,Rx,Ry,Rz + */ + public float[] toAxis() { + final float[] vec = new float[4]; + final float scale = FloatUtil.sqrt(x * x + y * y + z * z); + vec[0] = FloatUtil.acos(w) * 2.0f; + vec[1] = x / scale; + vec[2] = y / scale; + vec[3] = z / scale; + return vec; + } + + 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) { + final float w1 = w * q.w - x * q.x - y * q.y - z * q.z; + + final float x1 = w * q.x + x * q.w + y * q.z - z * q.y; + final float y1 = w * q.y - x * q.z + y * q.w + z * q.x; + final float z1 = w * q.z + x * q.y - y * q.x + z * q.w; + + 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; + } + + /*** + * Rotate given vector by this quaternion + * + * @param vector input vector + * @return rotated vector + */ + public float[] mult(float[] vector) { + // TODO : optimize + final float[] res = new float[3]; + final Quaternion a = new Quaternion(vector[0], vector[1], vector[2], 0.0f); + final Quaternion b = new Quaternion(this); + final Quaternion c = new Quaternion(this); + b.inverse(); + a.mult(b); + c.mult(a); + res[0] = c.x; + res[1] = c.y; + res[2] = c.z; + return res; + } + + /** + * Normalize a quaternion required if to be used as a rotational quaternion + */ + public void normalize() { + final float norme = (float) FloatUtil.sqrt(w * w + x * x + y * y + z * z); + if (norme == 0.0f) { + setIdentity(); + } else { + final 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() { + final float norm = w * w + x * x + y * y + z * z; + + final 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() { + final 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. + * <p> + * Note: Method does not normalize this quaternion! + * </p> + * <p> + * See http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/ + * quaternions/slerp/ + * </p> + * + * @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) { + final float cosom = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + final float t1 = 1.0f - t; + + // if the two quaternions are close, just use linear interpolation + if (cosom >= 0.95f) { + x = a.x * t1 + b.x * t; + y = a.y * t1 + b.y * t; + z = a.z * t1 + b.z * t; + w = a.w * t1 + b.w * t; + return; + } + + // the quaternions are nearly opposite, we can pick any axis normal to + // a,b + // to do the rotation + if (cosom <= -0.99f) { + x = 0.5f * (a.x + b.x); + y = 0.5f * (a.y + b.y); + z = 0.5f * (a.z + b.z); + w = 0.5f * (a.w + b.w); + return; + } + + // cosom is now withion range of acos, do a SLERP + final float sinom = FloatUtil.sqrt(1.0f - cosom * cosom); + final float omega = FloatUtil.acos(cosom); + + final float scla = FloatUtil.sin(t1 * omega) / sinom; + final float sclb = FloatUtil.sin(t * omega) / sinom; + + x = a.x * scla + b.x * sclb; + y = a.y * scla + b.y * sclb; + z = a.z * scla + b.z * sclb; + w = a.w * scla + b.w * sclb; + } + + /** + * Check if this quaternion represents an identity matrix for rotation, + * , ie (0,0,0,1). + * + * @return true if it is an identity rep., false otherwise + */ + public boolean isIdentity() { + return w == 1 && x == 0 && y == 0 && z == 0; + } + + /*** + * Set this quaternion to identity (x=0,y=0,z=0,w=1) + */ + public void setIdentity() { + x = y = z = 0; + w = 1; + } + + /** + * compute the quaternion from a 3x3 column matrix + * + * @param m 3x3 column matrix + */ + public void setFromMatrix(float[] m) { + final float T = m[0] + m[4] + m[8] + 1; + if (T > 0) { + final float S = 0.5f / (float) FloatUtil.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])) { + final float S = FloatUtil.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]) { + final float S = FloatUtil.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 { + final float S = FloatUtil.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) { + 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(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/opengl/math/VectorUtil.java index 524ca1171..053876b56 100755..100644 --- a/src/jogl/classes/com/jogamp/graph/math/VectorUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java @@ -25,14 +25,10 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.graph.math; +package com.jogamp.opengl.math; import java.util.ArrayList; -import jogamp.graph.math.MathFloat; - -import com.jogamp.graph.geom.Vertex; - public class VectorUtil { public enum Winding { @@ -43,7 +39,7 @@ public class VectorUtil { Winding(int dir) { this.dir = dir; } - } + } public static final int COLLINEAR = 0; @@ -62,9 +58,9 @@ public class VectorUtil { */ public static float[] normalize(float[] vector) { - float[] newVector = new float[3]; + final float[] newVector = new float[3]; - float d = MathFloat.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); + final float d = FloatUtil.sqrt(vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]); if(d> 0.0f) { newVector[0] = vector[0]/d; @@ -74,21 +70,49 @@ public class VectorUtil { return newVector; } - /** Scales a vector by param + /** Scales a vector by param creating a new float[] for the result! * @param vector input vector * @param scale constant to scale by - * @return scaled vector + * @return new scaled vector + * @deprecated Use {@link #scale(float[], float[], float)} */ public static float[] scale(float[] vector, float scale) { - float[] newVector = new float[3]; + final float[] newVector = new float[3]; - newVector[0] = vector[0]*scale; - newVector[1] = vector[1]*scale; - newVector[2] = vector[2]*scale; + newVector[0] = vector[0] * scale; + newVector[1] = vector[1] * scale; + newVector[2] = vector[2] * scale; return newVector; } + /** Scales a vector by param using given result float[] + * @param result vector for the result + * @param vector input vector + * @param scale single scale constant for all vector components + */ + public static float[] scale(float[] result, float[] vector, 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[] + * @param result vector for the result + * @param vector input vector + * @param scale 3 component scale constant for each vector component + * @return given result vector + */ + public static float[] scale(float[] result, float[] vector, float[] scale) + { + result[0] = vector[0] * scale[0]; + result[1] = vector[1] * scale[1]; + result[2] = vector[2] * scale[2]; + return result; + } + /** Adds to vectors * @param v1 vector 1 * @param v2 vector 2 @@ -96,7 +120,7 @@ public class VectorUtil { */ public static float[] vectorAdd(float[] v1, float[] v2) { - float[] newVector = new float[3]; + final float[] newVector = new float[3]; newVector[0] = v1[0] + v2[0]; newVector[1] = v1[1] + v2[1]; @@ -111,7 +135,7 @@ public class VectorUtil { */ public static float[] cross(float[] vec1, float[] vec2) { - float[] out = new float[3]; + final 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]; @@ -123,15 +147,15 @@ public class VectorUtil { /** Column Matrix Vector multiplication * @param colMatrix column matrix (4x4) * @param vec vector(x,y,z) - * @return result new float[3] + * @return result new float[3] */ public static float[] colMatrixVectorMult(float[] colMatrix, float[] vec) { - float[] out = new float[3]; + final 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]; + 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; } @@ -139,15 +163,15 @@ public class VectorUtil { /** Matrix Vector multiplication * @param rawMatrix column matrix (4x4) * @param vec vector(x,y,z) - * @return result new float[3] + * @return result new float[3] */ public static float[] rowMatrixVectorMult(float[] rawMatrix, float[] vec) { - float[] out = new float[3]; + final 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]; + 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; } @@ -161,6 +185,7 @@ public class VectorUtil { { return (p1+p2)/2.0f; } + /** Calculate the midpoint of two points * @param p1 first point * @param p2 second point @@ -168,21 +193,23 @@ public class VectorUtil { */ public static float[] mid(float[] p1, float[] p2) { - float[] midPoint = new float[3]; + final 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]); + return FloatUtil.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 @@ -191,11 +218,11 @@ public class VectorUtil { */ public static float computeLength(float[] p0, float[] point) { - float[] w = new float[]{point[0]-p0[0],point[1]-p0[1],point[2]-p0[2]}; + final float w0 = point[0]-p0[0]; + final float w1 = point[1]-p0[1]; + final float w2 = point[2]-p0[2]; - float distance = MathFloat.sqrt(w[0]*w[0] + w[1]*w[1] + w[2]*w[2]); - - return distance; + return FloatUtil.sqrt(w0*w0 + w1*w1 + w2*w2); } /**Check equality of 2 vec3 vectors @@ -205,11 +232,9 @@ public class VectorUtil { */ 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; + return Float.compare(v1[0], v2[0]) == 0 && + Float.compare(v1[1], v2[1]) == 0 && + Float.compare(v1[2], v2[2]) == 0 ; } /**Check equality of 2 vec2 vectors @@ -219,10 +244,8 @@ public class VectorUtil { */ 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; + return Float.compare(v1[0], v2[0]) == 0 && + Float.compare(v1[1], v2[1]) == 0 ; } /** Compute the determinant of 3 vectors @@ -233,8 +256,7 @@ public class VectorUtil { */ 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; + 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 @@ -253,22 +275,21 @@ public class VectorUtil { * @param v1 vertex 1 * @param v2 vertex2 2 */ - public static void computeVector(float[] vector, float[] v1, float[] v2) - { + public static void computeVector(float[] vector, float[] v1, float[] v2) { vector[0] = v2[0] - v1[0]; vector[1] = v2[1] - v1[1]; - vector[2] = v2[2] - v1[2]; + vector[2] = v2[2] - v1[2]; } - + /** 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 + * @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){ + public static boolean inCircle(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c, Vert2fImmutable d) { final float[] A = a.getCoord(); final float[] B = b.getCoord(); final float[] C = c.getCoord(); @@ -286,11 +307,11 @@ public class VectorUtil { * @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){ + public static float triArea(Vert2fImmutable a, Vert2fImmutable b, 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]); + 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 @@ -303,19 +324,19 @@ public class VectorUtil { public static float triArea(float[] A, float[] B, 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. + + /** 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, + public static boolean vertexInTriangle(float[] a, float[] b, float[] c, float[] p, float[] ac, float[] ab, float[] ap){ - // Compute vectors + // Compute vectors computeVector(ac, a, c); //v0 computeVector(ab, a, b); //v1 computeVector(ap, a, p); //v2 @@ -323,8 +344,8 @@ public class VectorUtil { // Compute dot products final float dot00 = dot(ac, ac); final float dot01 = dot(ac, ab); - final float dot11 = dot(ab, ab); final float dot02 = dot(ac, ap); + final float dot11 = dot(ab, ab); final float dot12 = dot(ab, ap); // Compute barycentric coordinates @@ -336,8 +357,8 @@ public class VectorUtil { return (u >= 0) && (v >= 0) && (u + v < 1); } - /** Check if one of three vertices are in triangle using - * barycentric coordinates computation. + /** 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 @@ -346,10 +367,10 @@ public class VectorUtil { * @param p3 the vertex in question * @return true if p1 or p2 or p3 is in triangle (a, b, c), false otherwise. */ - public static boolean vertexInTriangle3(float[] a, float[] b, float[] c, + public static boolean vertexInTriangle3(float[] a, float[] b, float[] c, float[] p1, float[] p2, float[] p3, float[] ac, float[] ab, float[] ap){ - // Compute vectors + // Compute vectors computeVector(ac, a, c); //v0 computeVector(ab, a, b); //v1 @@ -366,49 +387,49 @@ public class VectorUtil { final float dotAB_AP1 = dot(ab, ap); final float u1 = (dotAB_AB * dotAC_AP1 - dotAC_AB * dotAB_AP1) * invDenom; final float v1 = (dotAC_AC * dotAB_AP1 - dotAC_AB * dotAC_AP1) * invDenom; - + // Check if point is in triangle - if ( (u1 >= 0) && (v1 >= 0) && (u1 + v1 < 1) ) { + if ( (u1 >= 0) && (v1 >= 0) && (u1 + v1 < 1) ) { return true; } } - + { computeVector(ap, a, p2); //v2 final float dotAC_AP2 = dot(ac, ap); final float dotAB_AP2 = dot(ab, ap); 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) ) { + if ( (u >= 0) && (v >= 0) && (u + v < 1) ) { return true; } } - + { computeVector(ap, a, p3); //v2 final float dotAC_AP3 = dot(ac, ap); final float dotAB_AP3 = dot(ab, ap); 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) ) { + if ( (u >= 0) && (v >= 0) && (u + v < 1) ) { 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(Vertex a, Vertex b, Vertex c){ + public static boolean ccw(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c){ return triArea(a,b,c) > 0; } @@ -418,7 +439,7 @@ public class VectorUtil { * @param c third vertex * @return Winding */ - public static Winding getWinding(Vertex a, Vertex b, Vertex c) { + public static Winding getWinding(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c) { return triArea(a,b,c) > 0 ? Winding.CCW : Winding.CW ; } @@ -426,13 +447,13 @@ public class VectorUtil { * @param vertices * @return positive area if ccw else negative area value */ - public static float area(ArrayList<Vertex> vertices) { - int n = vertices.size(); + public static float area(ArrayList<? extends Vert2fImmutable> vertices) { + final 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(); + 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; @@ -442,7 +463,7 @@ public class VectorUtil { * @param vertices array of Vertices * @return CCW or CW {@link Winding} */ - public static Winding getWinding(ArrayList<Vertex> vertices) { + public static Winding getWinding(ArrayList<? extends Vert2fImmutable> vertices) { return area(vertices) >= 0 ? Winding.CCW : Winding.CW ; } @@ -452,13 +473,13 @@ public class VectorUtil { * @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 + * @return the intersection coordinates if the segments intersect, otherwise + * returns null */ - public static float[] seg2SegIntersection(Vertex a, Vertex b, Vertex c, Vertex d) { + public static float[] seg2SegIntersection(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c, Vert2fImmutable d) { final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); - if (determinant == 0) + if (determinant == 0) return null; final float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); @@ -481,15 +502,15 @@ public class VectorUtil { * @param d vertex 2 of second segment * @return true if the segments intersect, otherwise returns false */ - public static boolean testSeg2SegIntersection(Vertex a, Vertex b, Vertex c, Vertex d) { + public static boolean testSeg2SegIntersection(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c, 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) { + if (determinant == 0) { return false; } @@ -505,25 +526,25 @@ public class VectorUtil { 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 + * @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()); + public static float[] line2lineIntersection(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c, Vert2fImmutable d) { + final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); - if (determinant == 0) + 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; + 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; return new float[]{xi,yi,0}; } @@ -536,7 +557,7 @@ public class VectorUtil { * @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(Vertex a, Vertex b, Vertex c, Vertex d, Vertex e){ + public static boolean testTri2SegIntersection(Vert2fImmutable a, Vert2fImmutable b, Vert2fImmutable c, Vert2fImmutable d, Vert2fImmutable e){ return testSeg2SegIntersection(a, b, d, e) || testSeg2SegIntersection(b, c, d, e) || testSeg2SegIntersection(a, c, d, e) ; 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/graph/geom/AABBox.java b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java index 834b1a9e4..5fbc28c60 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/AABBox.java +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java @@ -25,29 +25,30 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.graph.geom; +package com.jogamp.opengl.math.geom; + +import com.jogamp.opengl.math.VectorUtil; -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 + * 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) + /** 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 + /** Create an AABBox specifying the coordinates * of the low and high * @param lx min x-coordinate * @param ly min y-coordnate @@ -57,25 +58,16 @@ public class AABBox implements Cloneable { * @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(); + float hx, float hy, 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(float[] low, float[] high) { - reset(); - resize(low[0],low[1],low[2]); - resize(high[0],high[1],high[2]); - - computeCenter(); + setSize(low[0],low[1],low[2], high[0],high[1],high[2]); } /** resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. */ @@ -86,33 +78,61 @@ public class AABBox implements Cloneable { 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; } + 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; + } + + /** + * 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 + */ + public final void setSize(float lx, float ly, float lz, + float hx, float hy, 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(); + } + /** Resize the AABBox to encapsulate another AABox * @param newBox AABBox to be encapsulated in */ @@ -139,19 +159,13 @@ public class AABBox implements Cloneable { 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. + * 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) { + public final void resize(float x, float y, float z) { /** test low */ if (x < low[0]) low[0] = x; @@ -167,12 +181,12 @@ public class AABBox implements Cloneable { high[1] = y; if (z > high[2]) high[2] = z; - + computeCenter(); } /** Resize the AABBox to encapsulate the passed - * xyz-coordinates. + * xyz-coordinates. * @param xyz xyz-axis coordinate values * @param offset of the array */ @@ -196,7 +210,7 @@ public class AABBox implements Cloneable { } return true; } - + /** Check if the xyz coordinates are bounded/contained * by this AABBox. * @param x x-axis coordinate value @@ -217,7 +231,7 @@ public class AABBox implements Cloneable { } 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 @@ -230,13 +244,13 @@ public class AABBox implements Cloneable { if (w <= 0 || h <= 0) { return false; } - + final float _w = getWidth(); - final float _h = getHeight(); + final float _h = getHeight(); if (_w <= 0 || _h <= 0) { return false; } - + final float x0 = getMinX(); final float y0 = getMinY(); return (x + w > x0 && @@ -245,8 +259,8 @@ public class AABBox implements Cloneable { y < y0 + _h); } - - /** Get the size of the Box where the size is represented by the + + /** 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 */ @@ -269,16 +283,16 @@ public class AABBox implements Cloneable { 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); } @@ -286,27 +300,45 @@ public class AABBox implements Cloneable { 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 AABBox clone() { return new AABBox(this.low, this.high); } - + + @Override public final boolean equals(Object obj) { if( obj == this ) { return true; @@ -314,11 +346,12 @@ public class AABBox implements Cloneable { if( null == obj || !(obj instanceof AABBox) ) { return false; } - final AABBox other = (AABBox) obj; - return VectorUtil.checkEquality(low, other.low) && + final AABBox other = (AABBox) obj; + return VectorUtil.checkEquality(low, other.low) && VectorUtil.checkEquality(high, other.high) ; } - + + @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]+ 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..fb311083f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java @@ -0,0 +1,388 @@ +/** + * 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 com.jogamp.common.os.Platform; + +/** + * Providing frustum {@link #getPlanes() planes} derived by different inputs + * ({@link #updateByPMV(float[], int) P*MV}, ..) + * used to {@link #classifySphere(float[], float) classify objects} and to test + * whether they are {@link #isOutside(AABBox) outside}. + * + * <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 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(float x, float y, 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(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(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(float[] pmv, int pmv_off) { + // Left: a = m41 + m11, b = m42 + m12, c = m43 + m13, d = m44 + m14 - [1..4] row-major + // Left: a = m30 + m00, b = m31 + m01, c = m32 + m02, d = m33 + m03 - [0..3] row-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] row-major + // Right: a = m30 - m00, b = m31 - m01, c = m32 - m02, d = m33 - m03 - [0..3] row-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] row-major + // Bottom: a = m30 + m10, b = m31 + m11, c = m32 + m12, d = m33 + m13 - [0..3] row-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] row-major + // Top: a = m30 - m10, b = m31 - m11, c = m32 - m12, d = m33 - m13 - [0..3] row-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] row-major + // Near: a = m30 + m20, b = m31 + m21, c = m32 + m22, d = m33 + m23 - [0..3] row-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] row-major + // Far: a = m30 - m20, b = m31 - m21, c = m32 + m22, d = m33 + m23 - [0..3] row-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(Plane p, 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(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(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(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(float[] p, 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(float[] p, float radius) { + return Location.OUTSIDE == classifySphere(p, radius); + } + + public StringBuilder toString(StringBuilder sb) { + if( null == sb ) { + sb = new StringBuilder(); + } + sb.append("Frustum[ Planes[ ").append(Platform.NEWLINE) + .append(" L: ").append(planes[0]).append(", ").append(Platform.NEWLINE) + .append(" R: ").append(planes[1]).append(", ").append(Platform.NEWLINE) + .append(" B: ").append(planes[2]).append(", ").append(Platform.NEWLINE) + .append(" T: ").append(planes[3]).append(", ").append(Platform.NEWLINE) + .append(" N: ").append(planes[4]).append(", ").append(Platform.NEWLINE) + .append(" F: ").append(planes[5]).append("], ").append(Platform.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 73ad97f5c..cd5aa338d 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -27,9 +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; @@ -43,155 +52,254 @@ 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.ThreadingImpl; +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; +import com.jogamp.common.os.Platform; 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> - * FIXME: Still needs AWT for threading impl., - * ie. will issue a 'wrong thread' error if runs in headless mode! + * Implementation allows use of custom {@link GLCapabilities}. * </p> */ -public class GLCanvas extends Canvas implements GLAutoDrawable { +public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextSetter { + private static final boolean DEBUG = Debug.debug("GLCanvas"); /* * Flag for whether the SWT thread should be used for OpenGL calls when in single-threaded mode. This is controlled * by the setting of the threading mode to worker (do not use SWT thread), awt (use SWT thread), or false (always use * calling thread). - * + * * @see Threading - * + * * Now done dynamically to avoid early loading of gluegen library. */ //private static final boolean useSWTThread = ThreadingImpl.getMode() != ThreadingImpl.WORKER; /* GL Stuff */ - private final GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private GLDrawable drawable; - private GLContext context; + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final GLDrawableHelper helper = new GLDrawableHelper(); + + private final GLCapabilitiesImmutable capsRequested; + private final GLCapabilitiesChooser capsChooser; + + private volatile Rectangle clientArea; + private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access + private volatile GLContextImpl context; // volatile: avoid locking for read-only access /* Native window surface */ - private AbstractGraphicsDevice device; - private final long nativeWindowHandle; - private final ProxySurface proxySurface; + 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 ctxCreationFlags = 0; - - private final GLCapabilitiesImmutable glCapsRequested; + private int additionalCtxCreationFlags = 0; - /* - * Lock for access to GLDrawable, as used in GLCanvas, - */ - private final RecursiveLock lock = LockFactory.createRecursiveLock(); /* Flag indicating whether an unprocessed reshape is pending. */ - private volatile boolean sendReshape; + 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(int v) { return "0x"+Integer.toHexString(v); } + private static String toHexString(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() { - drawableHelper.init(GLCanvas.this); + helper.init(GLCanvas.this, !sendReshape); } }; /* * Action to handle display in OpenGL, also processes reshape since they should be done at the same time. - * + * * Assumes GLContext is current when run. */ private final Runnable displayAction = new Runnable() { + @Override public void run() { if (sendReshape) { - drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + helper.reshape(GLCanvas.this, 0, 0, clientArea.width, clientArea.height); sendReshape = false; } - drawableHelper.display(GLCanvas.this); + helper.display(GLCanvas.this); } }; /* Action to make specified context current prior to running displayAction */ - private final Runnable makeCurrentAndDisplayAction = new Runnable() { + private final Runnable makeCurrentAndDisplayOnGLAction = new Runnable() { + @Override public void run() { - drawableHelper.invokeGL(drawable, context, displayAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !GLCanvas.this.isDisposed() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } + } finally { + _lock.unlock(); + } } }; /* Swaps buffers, assuming the GLContext is current */ - private final Runnable swapBuffersAction = new Runnable() { - public void run() { - drawable.swapBuffers(); - } - }; - - /* Swaps buffers, making the GLContext current first */ - private final Runnable makeCurrentAndSwapBuffersAction = new Runnable() { + private final Runnable swapBuffersOnGLAction = new Runnable() { + @Override public void run() { - drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction); + 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() { - public void run() { - context = null; - if (null != drawable) { - drawable.setRealized(false); - drawable = null; - } - } - }; - private final Runnable disposeOnEDTGLAction = new Runnable() { + @Override public void run() { - drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + 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; + } + + if ( null != context ) { + if( context.isCreated() ) { + // Catch dispose GLExceptions by GLEventListener, just 'print' them + // so we can continue with the destruction. + try { + if( !GLCanvas.this.isDisposed() ) { + helper.disposeGL(GLCanvas.this, context, true); + } else { + context.destroy(); + } + } catch (GLException gle) { + gle.printStackTrace(); + } + } + context = null; + } + if ( null != drawable ) { + drawable.setRealized(false); + drawable = null; + } + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; + } + screen.getDevice().close(); + + if (animatorPaused) { + animator.resume(); + } + + } finally { + _lock.unlock(); + } } }; - private final Runnable disposeGraphicsDeviceAction = new Runnable() { - public void run() { - if (null != device) { - device.close(); - device = null; - } - } + private class DisposeGLEventListenerAction implements Runnable { + private GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(GLEventListener listener, 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(); + } + } }; /** - * Storage for the client area rectangle so that it may be accessed from outside of the SWT thread. + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * on the SWT thread. + * + * @param parent + * Required (non-null) parent Composite. + * @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 + * 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 + * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the + * requested GLCapabilities, and the available capabilities of the graphics device. + * @return a new instance */ - private volatile Rectangle clientArea; + public static GLCanvas create(final Composite parent, final int style, final GLCapabilitiesImmutable caps, + 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 ); + } + }); + return res[0]; + } /** - * Creates a new SWT GLCanvas. - * + * Creates an instance using {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser, GLContext)} + * on the SWT thread. + * * @param parent * Required (non-null) parent Composite. * @param style @@ -206,354 +314,715 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { * 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 + * @deprecated Use {@link #create(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. */ - public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable caps, - final GLCapabilitiesChooser chooser, final GLContext shareWith) { + 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 }; + parent.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + res[0] = new GLCanvas( parent, style, caps, chooser, shareWith ); + } + }); + return res[0]; + } + + /** + * Creates a new SWT GLCanvas. + * + * @param parent + * Required (non-null) parent Composite. + * @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 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 capsChooser + * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the + * requested GLCapabilities, and the available capabilities of the graphics device. + */ + public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser) { + this(parent, style, capsReqUser, capsChooser, null); + } + + /** + * Creates a new SWT GLCanvas. + * + * @param parent + * Required (non-null) parent Composite. + * @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 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 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. + * @deprecated Use {@link #GLCanvas(Composite, int, GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. + */ + public GLCanvas(final Composite parent, final int style, GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser capsChooser, final GLContext shareWith) { /* NO_BACKGROUND required to avoid clearing bg in native SWT widget (we do this in the GL display) */ super(parent, style | SWT.NO_BACKGROUND); - + GLProfile.initSingleton(); // ensure JOGL is completly initialized SWTAccessor.setRealized(this, true); clientArea = GLCanvas.this.getClientArea(); - /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite) */ - device = SWTAccessor.getDevice(this); - /* Native handle for the control, used to associate with GLContext */ - nativeWindowHandle = SWTAccessor.getWindowHandle(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); + + 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 */); + } /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ - if(null == caps) { - caps = new GLCapabilities(GLProfile.getDefault(device)); + if(null == capsReqUser) { + capsReqUser = new GLCapabilities(GLProfile.getDefault(screen.getDevice())); } - glCapsRequested = caps; - - final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(caps.getGLProfile()); - - /* Create a NativeWindow proxy for the SWT canvas */ - proxySurface = glFactory.createProxySurface(device, nativeWindowHandle, caps, chooser); - - /* Associate a GL surface with the proxy */ - drawable = glFactory.createGLDrawable(proxySurface); - drawable.setRealized(true); - - context = drawable.createContext(shareWith); - - /* 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() { - public void paintControl(final PaintEvent arg0) { - if (!drawableHelper.isExternalAnimatorAnimating()) { - display(); - } - } - }); - - addControlListener(new ControlAdapter() { - @Override - public void controlResized(final ControlEvent arg0) { - clientArea = GLCanvas.this.getClientArea(); - /* Mark for OpenGL reshape next time the control is painted */ - sendReshape = true; - } - }); + + this.capsRequested = capsReqUser; + this.capsChooser = capsChooser; + if( null != shareWith ) { + helper.setSharedContext(null, shareWith); + } + + // post create .. when ready + gdkWindow = 0; + x11Window = 0; + drawable = null; + context = null; + + final Listener listener = new Listener () { + @Override + public void handleEvent (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(GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + private final UpstreamSurfaceHook swtCanvasUpStreamHook = new UpstreamSurfaceHook() { + @Override + public final void create(ProxySurface s) { /* nop */ } + + @Override + public final void destroy(ProxySurface s) { /* nop */ } + + @Override + public final int getWidth(ProxySurface s) { + return clientArea.width; + } + + @Override + public final int getHeight(ProxySurface s) { + return clientArea.height; + } + + @Override + public String toString() { + return "SWTCanvasUpstreamSurfaceHook[upstream: "+GLCanvas.this.toString()+", "+clientArea.width+"x"+clientArea.height+"]"; + } + }; + + protected final void updateSizeCheck() { + final Rectangle oClientArea = clientArea; + final Rectangle nClientArea = GLCanvas.this.getClientArea(); + if ( nClientArea != null && + ( nClientArea.width != oClientArea.width || nClientArea.height != oClientArea.height ) + ) { + clientArea = nClientArea; // write back new value + + 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 + drawable = _drawableNew; + } + } finally { + _lock.unlock(); + } + } + } + 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 + } + } + + 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 (implying !drawable.isRealized()) ! Checks of !isDispose() and isVisible() */ + protected final boolean validateDrawableAndContextWithCheck() { + if( !isValidAndVisibleOnEDT() ) { + 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 { + 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(DEBUG) { + System.err.println(getThreadName()+": SWT.GLCanvas.validate.X "+toHexString(hashCode())+": "+res+", drawable-realized "+drawable.isRealized()+", has context "+(null!=context)); + } + } finally { + _lock.unlock(); + } + 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 + 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() { + return clientArea.width; + } + + @Override + public int getHeight() { + return clientArea.height; + } + + @Override + 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(int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); } - public void addGLEventListener(final GLEventListener arg0) { - drawableHelper.addGLEventListener(arg0); + @Override + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + runInGLThread(r); + return r.listener; } - public void addGLEventListener(final int arg0, final GLEventListener arg1) throws IndexOutOfBoundsException { - drawableHelper.addGLEventListener(arg0, arg1); + @Override + public GLEventListener removeGLEventListener(final GLEventListener listener) { + return helper.removeGLEventListener(listener); } /** * {@inheritDoc} + * * <p> - * Also disposes of the SWT component. + * This impl. calls this class's {@link #dispose()} SWT override, + * where the actual implementation resides. + * </p> */ + @Override public void destroy() { - drawable.setRealized(false); dispose(); } - public void display() { - runInGLThread(makeCurrentAndDisplayAction, displayAction); + @Override + public GLAnimatorControl getAnimator() { + return helper.getAnimator(); } - public GLAnimatorControl getAnimator() { - return drawableHelper.getAnimator(); + @Override + public final Thread setExclusiveContextThread(Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); } + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override public boolean getAutoSwapBufferMode() { - return drawableHelper.getAutoSwapBufferMode(); + return helper.getAutoSwapBufferMode(); + } + + @Override + public final GLDrawable getDelegatedDrawable() { + return drawable; } + @Override public GLContext getContext() { return context; } + @Override public int getContextCreationFlags() { - return ctxCreationFlags; + return additionalCtxCreationFlags; } + @Override public GL getGL() { - final GLContext ctx = getContext(); - return (ctx == null) ? null : ctx.getGL(); + final GLContext _context = context; + return (null == _context) ? null : _context.getGL(); } - public void invoke(final boolean wait, final GLRunnable run) { - /* Queue task for running during the next display(). */ - drawableHelper.invoke(this, wait, run); + @Override + public boolean invoke(final boolean wait, final GLRunnable runnable) { + return helper.invoke(this, wait, runnable); } - public void removeGLEventListener(final GLEventListener arg0) { - drawableHelper.removeGLEventListener(arg0); + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> runnables) { + return helper.invoke(this, wait, runnables); } + @Override public void setAnimator(final GLAnimatorControl arg0) throws GLException { - drawableHelper.setAnimator(arg0); + helper.setAnimator(arg0); } + @Override public void setAutoSwapBufferMode(final boolean arg0) { - drawableHelper.setAutoSwapBufferMode(arg0); + helper.setAutoSwapBufferMode(arg0); } - public void setContext(final GLContext ctx) { - this.context = ctx; - if (ctx instanceof GLContextImpl) { - ((GLContextImpl) ctx).setContextCreationFlags(ctxCreationFlags); + @Override + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLContext oldCtx = context; + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + return oldCtx; + } finally { + _lock.unlock(); } } + @Override public void setContextCreationFlags(final int arg0) { - ctxCreationFlags = arg0; + additionalCtxCreationFlags = arg0; + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); + } } + @Override public GL setGL(final GL arg0) { - final GLContext ctx = getContext(); - if (ctx != null) { - ctx.setGL(arg0); + final GLContext _context = context; + if (null != _context) { + _context.setGL(arg0); return arg0; } return null; } - public GLContext createContext(final GLContext arg0) { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.createContext(arg0) : null; - } finally { - lock.unlock(); - } + @Override + public GLContext createContext(final GLContext shareWith) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + _lock.unlock(); + } } + @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getChosenCapabilities(); + 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. */ public GLCapabilitiesImmutable getRequestedGLCapabilities() { - return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getRequestedCapabilities(); + final GLDrawable _drawable = drawable; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities() : null; } + @Override public GLDrawableFactory getFactory() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getFactory() : null; - } finally { - lock.unlock(); - } + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getFactory() : null; } + @Override public GLProfile getGLProfile() { - return glCapsRequested.getGLProfile(); + return capsRequested.getGLProfile(); } + @Override public long getHandle() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getHandle() : 0; - } finally { - lock.unlock(); - } - } - - public int getHeight() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.height; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getHandle() : 0; } + @Override public NativeSurface getNativeSurface() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.getNativeSurface() : null; - } finally { - lock.unlock(); - } - } - - public int getWidth() { - final Rectangle clientArea = this.clientArea; - if (clientArea == null) return 0; - return clientArea.width; + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.getNativeSurface() : null; } + @Override public boolean isRealized() { - lock.lock(); - try { - final GLDrawable drawable = this.drawable; - return (drawable != null) ? drawable.isRealized() : false; - } finally { - lock.unlock(); - } + final GLDrawable _drawable = drawable; + return (_drawable != null) ? _drawable.isRealized() : false; } + @Override public void setRealized(final boolean arg0) { /* Intentionally empty */ } + @Override public void swapBuffers() throws GLException { - runInGLThread(makeCurrentAndSwapBuffersAction, swapBuffersAction); - } - - // FIXME: API of update() method ? - public void update() { - // FIXME: display(); - } - - public void dispose() { - lock.lock(); - try { - final Display display = getDisplay(); - - if (null != context) { - boolean animatorPaused = false; - final GLAnimatorControl animator = getAnimator(); - if (null != animator) { - // can't remove us from animator for recreational addNotify() - animatorPaused = animator.pause(); - } - if(context.isCreated()) { - if (Threading.isSingleThreaded() && !Threading.isOpenGLThread()) { - runInDesignatedGLThread(disposeOnEDTGLAction); - } else if (context.isCreated()) { - drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeGLAction); - } - } - - if (animatorPaused) { - animator.resume(); - } - } - if (display.getThread() == Thread.currentThread()) { - disposeGraphicsDeviceAction.run(); - } else { - display.syncExec(disposeGraphicsDeviceAction); - } - } finally { - lock.unlock(); - } - super.dispose(); + runInGLThread(swapBuffersOnGLAction); } /** - * Determines whether the current thread is the appropriate thread to use the GLContext in. If we are using one of - * the single-threaded policies in {@link Threading}, than this is either the SWT event dispatch thread, or the - * OpenGL worker thread depending on the state of {@link #useSWTThread}. Otherwise this always returns true because - * the threading model is user defined. - * <p> - * TODO: should this be moved to {@link Threading}? - * - * @return true if the calling thread is the correct thread to execute OpenGL calls in, false otherwise. + * Runs the specified action in an SWT compatible thread, which is: + * <ul> + * <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>Current thread</li> + * </ul></li> + * <li>Linux, Windows, .. + * <ul> + * <!--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() */ - protected boolean isRenderThread() { - if (Threading.isSingleThreaded()) { - if (ThreadingImpl.getMode() != ThreadingImpl.Mode.ST_WORKER) { - final Display display = getDisplay(); - return display != null && display.getThread() == Thread.currentThread(); - } - return Threading.isOpenGLThread(); - } - /* - * For multi-threaded rendering, the render thread is not defined... - */ - return true; + 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(); } - /** - * Runs the specified action in the designated OpenGL thread. If the current thread is designated, then the - * syncAction is run synchronously, otherwise the asyncAction is dispatched to the appropriate worker thread. - * - * @param asyncAction - * The non-null action to dispatch to an OpenGL worker thread. This action should not assume that a - * GLContext is current when invoked. - * @param syncAction - * The non-null action to run synchronously if the current thread is designated to handle OpenGL calls. - * This action may assume the GLContext is current. - */ - private void runInGLThread(final Runnable asyncAction, final Runnable syncAction) { - if (Threading.isSingleThreaded() && !isRenderThread()) { - /* Run in designated GL thread */ - runInDesignatedGLThread(asyncAction); - } else { - /* Run in current thread... */ - drawableHelper.invokeGL(drawable, context, syncAction, initAction); - } + private void runOnEDTIfAvail(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); + } } - /** - * Dispatches the specified runnable to the appropriate OpenGL worker thread (either the SWT event dispatch thread, - * or the OpenGL worker thread depending on the state of {@link #useSWTThread}). - * - * @param makeCurrentAndRunAction - * The non-null action to dispatch. - */ - private void runInDesignatedGLThread(final Runnable makeCurrentAndRunAction) { - if (ThreadingImpl.getMode() != ThreadingImpl.Mode.ST_WORKER) { - final Display display = getDisplay(); - assert display.getThread() != Thread.currentThread() : "Incorrect use of thread dispatching."; - display.syncExec(makeCurrentAndRunAction); - } else { - Threading.invokeOnOpenGLThread(true, makeCurrentAndRunAction); - } + @Override + public String toString() { + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getHeight() : -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 "+getWidth()+"x"+getHeight()+"]"; } - public static void main(final String[] args) { System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); // System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null).toString()); - + System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); + final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) ); final Display display = new Display(); final Shell shell = new Shell(display); 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(); System.err.println(JoglVersion.getGLInfo(gl, null)); } - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} + @Override public void display(final GLAutoDrawable drawable) {} - public void dispose(final GLAutoDrawable drawable) {} + @Override + public void dispose(final GLAutoDrawable drawable) {} }); shell.open(); canvas.display(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index 26d299663..d0de3b3a0 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 @@ -58,6 +58,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { private Map<RepaintManager,RepaintManager> repaintManagers = new IdentityHashMap<RepaintManager,RepaintManager>(); private Map<JComponent,Rectangle> dirtyRegions = new IdentityHashMap<JComponent,Rectangle>(); + @Override public void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions) { @@ -97,6 +98,7 @@ 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() { + @Override public void run() { for (Iterator<JComponent> iter = lightweights.iterator(); iter.hasNext(); ) { JComponent comp = iter.next(); @@ -164,7 +166,8 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } }; + @Override public boolean blockUntilDone(Thread thread) { - return ((Thread.currentThread() != thread) && !EventQueue.isDispatchThread()); + 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..799069292 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,12 +56,8 @@ 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 Runnable runnable; private boolean runAsFastAsPossible; @@ -69,6 +65,9 @@ public class Animator extends AnimatorBase { protected boolean pauseIssued; protected volatile boolean stopIssued; + /** + * Creates a new, empty Animator. + */ public Animator() { super(); if(DEBUG) { @@ -76,28 +75,42 @@ public class Animator extends AnimatorBase { } } + /** + * Creates a new Animator w/ an associated ThreadGroup. + */ public Animator(ThreadGroup tg) { super(); - threadGroup = tg; - + setThreadGroup(tg); if(DEBUG) { System.err.println("Animator created, ThreadGroup: "+threadGroup); } } - /** Creates a new Animator for a particular drawable. */ + /** + * Creates a new Animator for a particular drawable. + */ public Animator(GLAutoDrawable drawable) { super(); add(drawable); + if(DEBUG) { + System.err.println("Animator created, w/ "+drawable); + } } - /** Creates a new Animator for a particular drawable. */ + /** + * Creates a new Animator w/ an associated ThreadGroup for a particular drawable. + */ public Animator(ThreadGroup tg, GLAutoDrawable drawable) { - this(tg); + 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(String prefix) { return prefix + "Animator" ; } @@ -107,68 +120,67 @@ 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(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() { 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 + // 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; + } 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); + display(); // propagate exclusive change! } - setIsAnimatingSynced(false); // barrier + isAnimating = false; Animator.this.notifyAll(); try { Animator.this.wait(); } catch (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); Animator.this.notifyAll(); } } // sync Animator.this @@ -180,179 +192,124 @@ public class Animator extends AnimatorBase { Thread.yield(); } } + } catch( ThreadDeath td) { + if(DEBUG) { + System.err.println("Animator Catched: "+td.getClass().getName()+": "+td.getMessage()); + td.printStackTrace(); + } } finally { + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + display(); // propagate exclusive change! + } synchronized (Animator.this) { if(DEBUG) { - System.err.println("Animator stop " + Thread.currentThread() + ": " + toString()); + System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); } stopIssued = false; pauseIssued = false; animThread = null; - setIsAnimatingSynced(false); // barrier + isAnimating = false; Animator.this.notifyAll(); } } } } - 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(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; + 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; + return finishLifecycleAction(waitForStartedCondition, 0); } + private final Condition waitForStartedCondition = new Condition() { + @Override + public boolean eval() { + return !isStarted() || (!drawablesEmpty && !isAnimating) ; + } }; - private class WaitForStartedCondition implements Condition { - public boolean result() { - return !isStartedImpl() || (!drawablesEmpty && !isAnimating) ; - } - } - Condition waitForStartedCondition = new WaitForStartedCondition(); - - public synchronized boolean stop() { - if ( !isStartedImpl() ) { + @Override + public final synchronized boolean stop() { + if ( !isStarted() ) { return false; } stopIssued = true; - notifyAll(); - finishLifecycleAction(waitForStoppedCondition); - return true; - } - private class WaitForStoppedCondition implements Condition { - public boolean result() { - return isStartedImpl(); - } + return finishLifecycleAction(waitForStoppedCondition, 0); } - 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 d65967da1..39643744a 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,68 +51,178 @@ 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; + + 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; public interface AnimatorImpl { void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions); boolean blockUntilDone(Thread thread); } - protected ArrayList<GLAutoDrawable> drawables = new ArrayList<GLAutoDrawable>(); - protected boolean drawablesEmpty; + 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 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 (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(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(boolean force) { + if( force || null == impl ) { + if( useAWTAnimatorImpl( modeBits ) ) { + try { + impl = (AnimatorImpl) awtAnimatorImplClazz.newInstance(); + baseName = getBaseName("AWT"); + } catch (Exception e) { e.printStackTrace(); } + } + if( null == impl ) { + impl = new DefaultAnimatorImpl(); + baseName = getBaseName(""); + } + if(DEBUG) { + System.err.println("Animator.initImpl: baseName "+baseName+", implClazz "+impl.getClass().getName()+" - "+toString()+" - "+getThreadName()); + } } - synchronized (Animator.class) { - animatorCount++; - baseName = baseName.concat("-"+animatorCount); - drawablesEmpty = true; + } + 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(boolean enable, int bitValues) throws GLException { + final int _oldModeBits = modeBits; + if(enable) { + modeBits |= bitValues; + } else { + modeBits &= ~bitValues; + } + 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(); @@ -123,79 +232,245 @@ public abstract class AnimatorBase implements GLAnimatorControl { 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(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(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 != userExclusiveContextThread ? userExclusiveContextThread : animThread ) : null ; + if( propagateState ) { + setDrawablesExclCtxState(enable); + if( !enable ) { + if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) { + display(); + } else { + final boolean resumed = isAnimating() ? false : resume(); + int counter = 10; + while( 0<counter && isAnimating() && !validateDrawablesExclCtxState(dECT) ) { + try { + Thread.sleep(20); + } catch (InterruptedException e) { } + counter--; + } + if(resumed) { + pause(); + } + } + synchronized(AnimatorBase.this) { + userExclusiveContextThread=null; + } + } + } + if(DEBUG) { + System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this); + } + 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(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 (RuntimeException e) { + e.printStackTrace(); + } + } + } + protected final boolean validateDrawablesExclCtxState(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() { impl.display(drawables, ignoreExceptions, printExceptions); fpsCounter.tickFPS(); } + @Override public final void setUpdateFPSFrames(int frames, 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(boolean ignoreExceptions) { this.ignoreExceptions = ignoreExceptions; } @@ -203,11 +478,94 @@ 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(boolean printExceptions) { this.printExceptions = printExceptions; } + protected 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>, otherwise <code>false</code>. + */ + protected final synchronized boolean finishLifecycleAction(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 (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(); + } + } + 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+ + ", 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 !nok; + } + + @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()+"]"; + 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/DefaultAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java index 23b0845ee..a9c6e6456 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 @@ -41,6 +41,7 @@ import javax.media.opengl.GLAutoDrawable; up this behavior if desired. */ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { + @Override public void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions) { @@ -60,7 +61,8 @@ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { } } + @Override public boolean blockUntilDone(Thread thread) { - return (Thread.currentThread() != thread); + return Thread.currentThread() != thread; } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FBObject.java b/src/jogl/classes/com/jogamp/opengl/util/FBObject.java deleted file mode 100644 index 3e049a334..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/FBObject.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2008 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 - * 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. - * - */ - -package com.jogamp.opengl.util; - -import javax.media.opengl.*; - -public class FBObject { - static final int MAX_FBO_TEXTURES = 32; // just for our impl .. not the real 'max' FBO color attachments - private int[] fbo_tex_names; - private int[] fbo_tex_units; - private int fbo_tex_num; - private int colorattachment_num; - - private boolean initialized; - private int width, height; - private int fb, depth_rb, stencil_rb, vStatus; - private boolean bound; - - public FBObject(int width, int height) { - this.fbo_tex_names = new int[MAX_FBO_TEXTURES]; - this.fbo_tex_units = new int[MAX_FBO_TEXTURES]; - this.fbo_tex_num = 0; - this.colorattachment_num = 0; - this.initialized = false; - this.width = width; - this.height = height; - this.fb = 0; - this.depth_rb = 0; - this.stencil_rb = 0; - this.bound = false; - } - - /** - * @return true if the FB status is valid, otherwise false - * @see #getStatus() - */ - public boolean isStatusValid() { - switch(vStatus) { - case GL.GL_FRAMEBUFFER_COMPLETE: - return true; - case GL.GL_FRAMEBUFFER_UNSUPPORTED: - 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 GL2.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - //case GL2.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - //case GL2.GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: - case 0: - default: - System.out.println("Framebuffer " + fb + " is incomplete: status = 0x" + Integer.toHexString(vStatus) + - " : " + getStatusString(vStatus)); - return false; - } - } - - /** - * @return The FB status. {@link GL.GL_FRAMEBUFFER_COMPLETE} if ok, otherwise return GL FBO error state or -1 - * @see #validateStatus() - */ - public int getStatus() { - return vStatus; - } - - public String getStatusString() { - return getStatusString(vStatus); - } - - public static final String getStatusString(int fbStatus) { - switch(fbStatus) { - case -1: - return "NOT A FBO"; - case GL.GL_FRAMEBUFFER_COMPLETE: - return "OK"; - case GL.GL_FRAMEBUFFER_UNSUPPORTED: - return("GL FBO: Unsupported framebuffer format"); - case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - return("GL FBO: incomplete, incomplete attachment\n"); - case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - return("GL FBO: incomplete, missing attachment"); - case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - return("GL FBO: incomplete, attached images must have same dimensions"); - case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - return("GL FBO: incomplete, attached images must have same format"); - case GL2.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - return("GL FBO: incomplete, missing draw buffer"); - case GL2.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - return("GL FBO: incomplete, missing read buffer"); - case GL2.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - return("GL FBO: incomplete, missing multisample buffer"); - case 0: - return("GL FBO: incomplete, implementation fault"); - default: - return("GL FBO: incomplete, implementation ERROR"); - } - } - - private boolean checkNoError(GL gl, int err, String exceptionMessage) { - if(GL.GL_NO_ERROR != err) { - if(null != gl) { - destroy(gl); - } - if(null != exceptionMessage) { - throw new GLException(exceptionMessage+" GL Error 0x"+Integer.toHexString(err)); - } - return false; - } - return true; - } - - private final void checkInitialized() { - if(!initialized) { - throw new GLException("FBO not initialized, call init(GL) first."); - } - } - - private final void checkBound(GL gl, boolean shallBeBound) { - checkInitialized(); - if(bound != shallBeBound) { - final String s0 = shallBeBound ? "not" : "already" ; - throw new GLException("FBO "+s0+" bound "+toString()); - } - checkNoError(null, gl.glGetError(), "FBObject pre"); // throws GLException if error - } - - /** - * Initializes this FBO's instance with it's texture. - * - * <p>Leaves the FBO bound!</p> - * - * @param gl the current GL context - * @throws GLException in case of an error - */ - public void init(GL gl) throws GLException { - if(initialized) { - throw new GLException("FBO already initialized"); - } - checkNoError(null, gl.glGetError(), "FBObject Init.pre"); // throws GLException if error - - // generate fbo .. - int name[] = new int[1]; - - gl.glGenFramebuffers(1, name, 0); - fb = name[0]; - if(fb==0) { - throw new GLException("null generated framebuffer"); - } - - // bind fbo .. - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fb); - checkNoError(gl, gl.glGetError(), "FBObject Init.bindFB"); // throws GLException if error - if(!gl.glIsFramebuffer(fb)) { - checkNoError(gl, GL.GL_INVALID_VALUE, "FBObject Init.isFB"); // throws GLException - } - bound = true; - initialized = true; - - updateStatus(gl); - } - - /** - * Attaches a[nother] Texture2D Color Buffer to this FBO's instance, - * selecting the texture data type and format automatically. - * <p>This may be done as many times as many color attachments are supported, - * see {@link GL2GL3#GL_MAX_COLOR_ATTACHMENTS}.</p> - * - * <p>Assumes a bound FBO</p> - * <p>Leaves the FBO bound!</p> - * - * @param gl the current GL context - * @param texUnit the desired texture unit ranging from [0..{@link GL2#GL_MAX_TEXTURE_UNITS}-1], or -1 if no unit shall be activate at {@link #use(GL, int)} - * @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 wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} - * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} - * @return idx of the new attached texture, otherwise -1 - * @throws GLException in case of an error - */ - public int attachTexture2D(GL gl, int texUnit, int magFilter, int minFilter, int wrapS, int wrapT) throws GLException { - final int textureInternalFormat, textureDataFormat, textureDataType; - if(gl.isGLES()) { - textureInternalFormat=GL.GL_RGBA; - textureDataFormat=GL.GL_RGBA; - textureDataType=GL.GL_UNSIGNED_BYTE; - } else { - textureInternalFormat=GL.GL_RGBA8; - textureDataFormat=GL.GL_BGRA; - textureDataType=GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV; - } - return attachTexture2D(gl, texUnit, textureInternalFormat, textureDataFormat, textureDataType, magFilter, minFilter, wrapS, wrapT); - } - - /** - * Attaches a[nother] Texture2D Color Buffer to this FBO's instance, - * selecting the texture data type and format automatically. - * <p>This may be done as many times as many color attachments are supported, - * see {@link GL2GL3#GL_MAX_COLOR_ATTACHMENTS}.</p> - * - * <p>Assumes a bound FBO</p> - * <p>Leaves the FBO bound!</p> - * - * @param gl the current GL context - * @param texUnit the desired texture unit ranging from [0..{@link GL2#GL_MAX_TEXTURE_UNITS}-1], or -1 if no unit shall be activate at {@link #use(GL, int)} - * @param textureInternalFormat internalFormat parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} - * @param textureDataFormat format parameter to {@link GL#glTexImage2D(int, int, int, int, int, int, int, int, long)} - * @param textureDataType 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 wrapS if > 0 value for {@link GL#GL_TEXTURE_WRAP_S} - * @param wrapT if > 0 value for {@link GL#GL_TEXTURE_WRAP_T} - * @return index of the texture colorbuffer if bound and configured successfully, otherwise -1 - * @throws GLException in case the texture colorbuffer couldn't be allocated - */ - public int attachTexture2D(GL gl, int texUnit, - int textureInternalFormat, int textureDataFormat, int textureDataType, - int magFilter, int minFilter, int wrapS, int wrapT) throws GLException { - checkBound(gl, true); - final int fbo_tex_idx = fbo_tex_num; - gl.glGenTextures(1, fbo_tex_names, fbo_tex_num); - if(fbo_tex_names[fbo_tex_idx]==0) { - throw new GLException("null generated texture"); - } - fbo_tex_units[fbo_tex_idx] = texUnit; - fbo_tex_num++; - if(0<=texUnit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + texUnit); - } - gl.glBindTexture(GL.GL_TEXTURE_2D, fbo_tex_names[fbo_tex_idx]); - checkNoError(gl, gl.glGetError(), "FBObject Init.bindTex"); // throws GLException if error - gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, textureInternalFormat, width, height, 0, - textureDataFormat, textureDataType, null); - int glerr = gl.glGetError(); - if(GL.GL_NO_ERROR != glerr) { - int[] sz = new int[1]; - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, sz, 0); - // throws GLException if error - checkNoError(gl, glerr, "FBObject Init.texImage2D: "+ - " int-fmt 0x"+Integer.toHexString(textureInternalFormat)+ - ", "+width+"x"+height+ - ", data-fmt 0x"+Integer.toHexString(textureDataFormat)+ - ", data-type 0x"+Integer.toHexString(textureDataType)+ - ", max tex-sz "+sz[0]); - } - if( 0 < magFilter ) { - gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, magFilter); - } - if( 0 < minFilter ) { - gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, minFilter); - } - if( 0 < wrapS ) { - 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); - } - - // Set up the color buffer for use as a renderable texture: - gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, - GL.GL_COLOR_ATTACHMENT0 + colorattachment_num++, - GL.GL_TEXTURE_2D, fbo_tex_names[fbo_tex_idx], 0); - - updateStatus(gl); - return isStatusValid() ? fbo_tex_idx : -1; - } - - /** - * Attaches one Depth Buffer to this FBO's instance. - * <p>This may be done only one time.</p> - * - * <p>Assumes a bound FBO</p> - * <p>Leaves the FBO bound!</p> - * @param gl the current GL context - * @param depthComponentType {@link GL#GL_DEPTH_COMPONENT16}, {@link GL#GL_DEPTH_COMPONENT24} or {@link GL#GL_DEPTH_COMPONENT32} - * @return true if the depth renderbuffer could be bound and configured, otherwise false - * @throws GLException in case the depth renderbuffer couldn't be allocated or one is already attached. - */ - public boolean attachDepthBuffer(GL gl, int depthComponentType) throws GLException { - checkBound(gl, true); - if(depth_rb != 0) { - throw new GLException("FBO depth buffer already attached (rb "+depth_rb+")"); - } - int name[] = new int[1]; - gl.glGenRenderbuffers(1, name, 0); - depth_rb = name[0]; - if(depth_rb==0) { - throw new GLException("null generated renderbuffer"); - } - // Initialize the depth buffer: - gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, depth_rb); - if(!gl.glIsRenderbuffer(depth_rb)) { - System.err.println("not a depthbuffer: "+ depth_rb); - name[0] = depth_rb; - gl.glDeleteRenderbuffers(1, name, 0); - depth_rb=0; - return false; - } - - gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, depthComponentType, width, height); - // Set up the depth buffer attachment: - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, - GL.GL_DEPTH_ATTACHMENT, - GL.GL_RENDERBUFFER, depth_rb); - updateStatus(gl); - return isStatusValid(); - } - - /** - * Attaches one Stencil Buffer to this FBO's instance. - * <p>This may be done only one time.</p> - * - * <p>Assumes a bound FBO</p> - * <p>Leaves the FBO bound!</p> - * @param gl the current GL context - * @param stencilComponentType {@link GL#GL_STENCIL_INDEX1}, {@link GL#GL_STENCIL_INDEX4} or {@link GL#GL_STENCIL_INDEX8} - * @return true if the stencil renderbuffer could be bound and configured, otherwise false - * @throws GLException in case the stencil renderbuffer couldn't be allocated or one is already attached. - */ - public boolean attachStencilBuffer(GL gl, int stencilComponentType) throws GLException { - checkBound(gl, true); - if(stencil_rb != 0) { - throw new GLException("FBO stencil buffer already attached (rb "+stencil_rb+")"); - } - int name[] = new int[1]; - gl.glGenRenderbuffers(1, name, 0); - stencil_rb = name[0]; - if(stencil_rb==0) { - throw new GLException("null generated stencilbuffer"); - } - // Initialize the stencil buffer: - gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, stencil_rb); - if(!gl.glIsRenderbuffer(stencil_rb)) { - System.err.println("not a stencilbuffer: "+ stencil_rb); - name[0] = stencil_rb; - gl.glDeleteRenderbuffers(1, name, 0); - stencil_rb=0; - return false; - } - gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, stencilComponentType, width, height); - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, - GL.GL_STENCIL_ATTACHMENT, - GL.GL_RENDERBUFFER, stencil_rb); - updateStatus(gl); - return isStatusValid(); - } - - /** - * @param gl the current GL context - */ - public void destroy(GL gl) { - if(bound) { - unbind(gl); - } - - int name[] = new int[1]; - - if(0!=stencil_rb) { - name[0] = stencil_rb; - gl.glDeleteRenderbuffers(1, name, 0); - stencil_rb = 0; - } - if(0!=depth_rb) { - name[0] = depth_rb; - gl.glDeleteRenderbuffers(1, name, 0); - depth_rb=0; - } - if(null!=fbo_tex_names && fbo_tex_num>0) { - gl.glDeleteTextures(1, fbo_tex_names, fbo_tex_num); - fbo_tex_names = new int[MAX_FBO_TEXTURES]; - fbo_tex_units = new int[MAX_FBO_TEXTURES]; - fbo_tex_num = 0; - } - colorattachment_num = 0; - if(0!=fb) { - name[0] = fb; - gl.glDeleteFramebuffers(1, name, 0); - fb = 0; - } - initialized = false; - } - - /** - * Bind this FBO - * <p>In case you have attached more than one color buffer, - * you may want to setup {@link GL2GL3#glDrawBuffers(int, int[], int)}.</p> - * @param gl the current GL context - */ - public void bind(GL gl) { - checkBound(gl, false); - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fb); - bound = true; - } - - /** - * Unbind FBO, ie bind 'non' FBO 0 - * @param gl the current GL context - */ - public void unbind(GL gl) { - checkBound(gl, true); - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); - bound = false; - } - - /** - * Bind the texture with given index. - * - * <p>If a valid texture unit was named via {@link #attachTexture2D(GL, int, int, int, int, int) attachTexture2D(..)}, - * the unit is activated via {@link GL#glActiveTexture(int) glActiveTexture(GL.GL_TEXTURE0 + unit)}.</p> - * @param gl the current GL context - * @param texIdx index of the texture to use, prev. attached w/ {@link #attachTexture2D(GL, int, int, int, int, int) attachTexture2D(..)} - */ - public void use(GL gl, int texIdx) { - checkBound(gl, false); - if(texIdx >= fbo_tex_num) { - throw new GLException("Invalid texId, only "+fbo_tex_num+" textures are attached"); - } - if(0<=fbo_tex_units[texIdx]) { - gl.glActiveTexture(GL.GL_TEXTURE0 + fbo_tex_units[texIdx]); - } - gl.glBindTexture(GL.GL_TEXTURE_2D, fbo_tex_names[texIdx]); // use it .. - } - - /** Unbind texture, ie bind 'non' texture 0 */ - public void unuse(GL gl) { - checkBound(gl, false); - gl.glBindTexture(GL.GL_TEXTURE_2D, 0); // don't use it - } - - public final boolean isBound() { return bound; } - public final int getWidth() { return width; } - public final int getHeight() { return height; } - public final int getFBName() { return fb; } - public final int getTextureNumber() { return fbo_tex_num; } - public final int getTextureName(int idx) { return fbo_tex_names[idx]; } - - /** @return the named texture unit ranging from [0..{@link GL2#GL_MAX_TEXTURE_UNITS}-1], or -1 if no unit was desired. */ - public final int getTextureUnit(int idx) { return fbo_tex_units[idx]; } - public final int getColorAttachmentNumber() { return colorattachment_num; } - public final int getStencilBuffer() { return stencil_rb; } - public final int getDepthBuffer() { return depth_rb; } - public final String toString() { - return "FBO[name "+fb+", size "+width+"x"+height+", color num "+colorattachment_num+", tex num "+fbo_tex_num+", depth "+depth_rb+", stencil "+stencil_rb+"]"; - } - - private void updateStatus(GL gl) { - if(!gl.glIsFramebuffer(fb)) { - vStatus = -1; - } else { - vStatus = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); - } - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index f7fc58160..5bd803500 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,29 +29,42 @@ * 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 shouldRun; // MainTask trigger + private volatile boolean shouldStop; // MainTask trigger + @Override protected String getBaseName(String prefix) { return "FPS" + prefix + "Animator" ; } @@ -81,6 +94,7 @@ public class FPSAnimator extends AnimatorBase { value, an initial drawable to animate, and a flag indicating whether to use fixed-rate scheduling. */ public FPSAnimator(GLAutoDrawable drawable, int fps, boolean scheduleAtFixedRate) { + super(); this.fps = fps; if (drawable != null) { add(drawable); @@ -88,131 +102,266 @@ 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 synchronized void setFPS(int fps) throws GLException { + if ( isStarted() ) { + throw new GLException("Animator already started."); } + this.fps = fps; } + public final int getFPS() { return fps; } - public final boolean isAnimating() { - stateSync.lock(); - try { - return (timer != null) && (task != null); - } finally { - stateSync.unlock(); + class MainTask extends TimerTask { + private boolean justStarted; + private boolean alreadyStopped; + private boolean alreadyPaused; + + public MainTask() { } - } - public final boolean isPaused() { - stateSync.lock(); - try { - return (timer != null) && (task == null); - } finally { - stateSync.unlock(); + public void start(Timer timer) { + fpsCounter.resetFPSCounter(); + shouldRun = true; + shouldStop = 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); + } } - } - 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(); + public boolean isActive() { return !alreadyStopped && !alreadyPaused; } + + @Override + public final String toString() { + return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" shouldRun "+shouldRun+", shouldStop "+shouldStop+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; + } + + @Override + public void run() { + 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 ) { + shouldRun = false; // isAnimating:=false @ pause below + } else { + shouldRun = true; + setDrawablesExclCtxState(exclusiveContext); + FPSAnimator.this.notifyAll(); + } + if(DEBUG) { + System.err.println("FPSAnimator P1:" + Thread.currentThread() + ": " + toString()); + } } } - }; + if( shouldRun ) { + display(); + } else if( shouldStop ) { // STOP + if(DEBUG) { + System.err.println("FPSAnimator P4: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); + } + this.cancel(); - fpsCounter.resetFPSCounter(); - shouldRun = true; + if( !alreadyStopped ) { + alreadyStopped = true; + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + display(); // propagate exclusive change! + } + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + } + animThread = null; + isAnimating = false; + FPSAnimator.this.notifyAll(); + } + } + } else { + if(DEBUG) { + System.err.println("FPSAnimator P5: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); + } + this.cancel(); - if (scheduleAtFixedRate) { - timer.scheduleAtFixedRate(task, 0, delay); - } else { - timer.schedule(task, 0, delay); + if( !alreadyPaused ) { // PAUSE + alreadyPaused = true; + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + display(); // propagate exclusive change! + } + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + } + isAnimating = false; + FPSAnimator.this.notifyAll(); + } + } + } } } + 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 && ( !shouldRun && !shouldStop ) ; + } + + static int timerNo = 0; - public synchronized boolean start() { - if (timer != null) { + @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()); } - return true; + 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()); + } + 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 { + 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 { 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; + shouldStop = 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() || ( null != task && isPaused() ) ) { return false; } - stateSync.lock(); - try { + 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 { 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; + 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 isAnimating && isStarted(); + } }; - public synchronized boolean resume() { - if (timer == null) { + @Override + public final synchronized boolean resume() { + if ( null != task || !isStarted() || !isPaused() ) { 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 { + 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..a58eb82cd 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) 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); + 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,40 +98,40 @@ 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, + */ + public static GLArrayDataClient createFixed(int index, int comps, int dataType, boolean normalized, int stride, 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); + 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, + public static GLArrayDataClient createGLSL(String name, int comps, int dataType, boolean normalized, 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); + 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 @@ -116,68 +144,85 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData { 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); + 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(Object obj, 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(boolean written) { + bufferWritten = ( 0 == mappedElementCount ) ? written : true; + } + @Override public void destroy(GL gl) { reset(gl); super.destroy(gl); } + @Override public void reset(GL gl) { enableBuffer(gl, false); reset(); } + @Override public void seal(GL gl, boolean seal) { seal(seal); enableBuffer(gl, seal); } + @Override public void enableBuffer(GL gl, boolean enable) { - if( enableBufferAlways || bufferEnabled != 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; } } + @Override + public boolean bindBuffer(GL gl, 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(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; } + @Override public void seal(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,6 +280,7 @@ 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. */ + @Override public void put(Buffer v) { if ( sealed ) return; /** FIXME: isn't true for interleaved arrays ! @@ -241,48 +291,57 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData Buffers.put(buffer, v); } + @Override public void putb(byte v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.putb(buffer, v); } + @Override public void puts(short v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.puts(buffer, v); } + @Override public void puti(int v) { if ( sealed ) return; growBufferIfNecessary(1); Buffers.puti(buffer, v); } + @Override public void putx(int v) { puti(v); } + @Override public void putf(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(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 { 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(String name, int index, int comps, int dataType, boolean normalized, int stride, Buffer data, + int initialElementCount, int mappedElementCount, boolean isVertexAttribute, + GLArrayHandler handler, int vboName, long vboOffset, int vboUsage, int vboTarget, 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) { 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(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 (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..833f1ccda 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, + public static GLArrayDataServer createFixed(int index, int compsPerElement, int dataType, boolean normalized, int stride, Buffer buffer, 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); + 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, + public static GLArrayDataServer createFixed(int index, int compsPerElement, int dataType, boolean normalized, int initialElementCount, 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); + 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(String name, int compsPerElement, + int dataType, boolean normalized, int initialElementCount, 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); + 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(String name, int compsPerElement, + int dataType, boolean normalized, int mappedElementCount, int vboUsage) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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, + public static GLArrayDataServer createGLSL(String name, int compsPerElement, int dataType, boolean normalized, int stride, Buffer buffer, - int vboUsage) + 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); + 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, + public static GLArrayDataServer createData(int compsPerElement, int dataType, int stride, Buffer buffer, int vboUsage, 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); + 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(int comps, int dataType, int initialElementCount, + public static GLArrayDataServer createData(int compsPerElement, int dataType, int initialElementCount, int vboUsage, 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); + 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 createDataMapped(int compsPerElement, int dataType, int mappedElementCount, + int vboUsage, int vboTarget) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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, + public static GLArrayDataServer createFixedInterleaved(int compsPerElement, int dataType, boolean normalized, int initialElementCount, 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); + 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(int compsPerElement, int dataType, boolean normalized, int mappedElementCount, + int vboUsage) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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(int compsPerElement, int dataType, boolean normalized, int stride, Buffer buffer, + int vboUsage) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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,14 +332,14 @@ 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) { @@ -223,53 +348,107 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE 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(int compsPerElement, int dataType, boolean normalized, int initialElementCount, + int vboUsage) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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(int compsPerElement, int dataType, boolean normalized, int mappedElementCount, int vboUsage) + throws GLException + { + GLArrayDataServer ads = new GLArrayDataServer(); + 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(int compsPerElement, int dataType, boolean normalized, int stride, Buffer buffer, + 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); + 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) { @@ -278,20 +457,37 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE 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(int interleavedOffset) { + this.interleavedOffset = interleavedOffset; + } + + public final int getInterleavedOffset() { + return interleavedOffset; + } + // // Data matters GLArrayData // @@ -300,6 +496,7 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE // Data and GL state modification .. // + @Override public void destroy(GL gl) { // super.destroy(gl): // - GLArrayDataClient.destroy(gl): disables & clears client-side buffer @@ -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(boolean vboUsage) { checkSeal(false); super.setVBOEnabled(vboUsage); } + public GLBufferStorage mapStorage(GL gl, 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(GL gl, long offset, long length, 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(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(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,18 +620,20 @@ 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(String name, int index, int comps, int dataType, boolean normalized, + int stride, Buffer data, int initialElementCount, int mappedElementCount, + boolean isVertexAttribute, + GLArrayHandler glArrayHandler, int vboName, long vboOffset, int vboUsage, int vboTarget, 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; } + @Override protected void init_vbo(GL gl) { super.init_vbo(gl); if(vboEnabled && vboName==0) { @@ -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(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..f617fed73 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(int index, int comps, int dataType, boolean normalized, int stride, + public static GLArrayDataWrapper createFixed(int index, int comps, int dataType, boolean normalized, int stride, Buffer buffer, int vboName, long vboOffset, int vboUsage, int vboTarget) throws GLException { GLArrayDataWrapper adc = new GLArrayDataWrapper(); - adc.init(null, index, comps, dataType, normalized, stride, buffer, false, - vboName, vboOffset, vboUsage, vboTarget); + 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, + int mappedElementCount, int vboName, long vboOffset, int vboUsage, int vboTarget) + throws GLException + { + 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,20 +119,46 @@ 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, + public static GLArrayDataWrapper createGLSL(String name, int comps, int dataType, boolean normalized, int stride, Buffer buffer, int vboName, long vboOffset, int vboUsage, int vboTarget) throws GLException { GLArrayDataWrapper adc = new GLArrayDataWrapper(); - adc.init(name, -1, comps, dataType, normalized, stride, buffer, true, - vboName, vboOffset, vboUsage, vboTarget); + 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(String name, int comps, int dataType, boolean normalized, int stride, + int mappedElementCount, int vboName, long vboOffset, int vboUsage, int vboTarget) + throws GLException + { + 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 @@ -86,7 +168,7 @@ public class GLArrayDataWrapper implements GLArrayData { if(throwException) { throw new GLException("Instance !alive "+this); } - return false; + return false; } if(this.isVertexAttribute() && !glp.hasGLSL()) { if(throwException) { @@ -96,54 +178,103 @@ public class GLArrayDataWrapper implements GLArrayData { } return glp.isValidArrayDataType(getIndex(), getComponentCount(), getComponentType(), isVertexAttribute(), throwException); } - - // + + @Override + public void associate(Object obj, 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(int v) { location = v; return location; } + + @Override + public final int setLocation(GL2ES2 gl, int program) { + location = gl.glGetAttribLocation(program, name); + return location; + } + + @Override + public final int setLocation(GL2ES2 gl, int program, 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; } + @Override public void destroy(GL gl) { buffer = null; vboName=0; @@ -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(int dataType) { switch(dataType) { case GL.GL_BYTE: case GL.GL_UNSIGNED_BYTE: @@ -180,15 +313,18 @@ public class GLArrayDataWrapper implements GLArrayData { case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: return ShortBuffer.class; + case GL.GL_UNSIGNED_INT: case GL2ES1.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); } } + @Override public void setName(String newName) { location = -1; name = newName; @@ -198,7 +334,7 @@ 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) { this.vboEnabled=vboEnabled; } @@ -206,86 +342,83 @@ public class GLArrayDataWrapper implements GLArrayData { /** * 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) { 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(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) { 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(String name, int index, int componentsPerElement, int componentType, + boolean normalized, int stride, Buffer data, int mappedElementCount, + boolean isVertexAttribute, int vboName, long vboOffset, int vboUsage, 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(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 32391c650..b9903ac6d 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,55 +28,110 @@ * 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.GL2GL3; +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 - * GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, - * GL_UNSIGNED_INT, GL_INT, GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, - * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, - * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, - * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, - * GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_INT_8_8_8_8_REV, - * GL_UNSIGNED_INT_10_10_10_2, GL_UNSIGNED_INT_2_10_10_10_REV - * GL_UNSIGNED_INT_24_8, GL_UNSIGNED_INT_10F_11F_11F_REV, - * GL_UNSIGNED_INT_5_9_9_9_REV, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, - * GL_HILO16_NV, GL_SIGNED_HILO16_NV (27) - * @return -1 if glType is unhandled, otherwise the actual value > 0 + * @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(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(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_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_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/> + * 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 */ public static final int sizeOfGLType(int glType) { - switch (glType) { // 25 + switch (glType) { // 29 + // case GL2.GL_BITMAP: 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 SIZEOF_BYTE; - + case GL.GL_SHORT: case GL.GL_UNSIGNED_SHORT: case GL.GL_UNSIGNED_SHORT_5_6_5: @@ -85,57 +140,71 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: case GL2GL3.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_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 GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: return SIZEOF_INT; - + case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return SIZEOF_LONG; - + case GL.GL_FLOAT: return SIZEOF_FLOAT; - - case GL2.GL_DOUBLE: + + case GL2GL3.GL_DOUBLE: return SIZEOF_DOUBLE; } return -1; } - + /** - * @param glType shall be one of - * GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, - * GL_UNSIGNED_INT, GL_INT, GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, - * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, - * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, - * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, - * GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_INT_8_8_8_8_REV, - * GL_UNSIGNED_INT_10_10_10_2, GL_UNSIGNED_INT_2_10_10_10_REV - * GL_UNSIGNED_INT_24_8, GL_UNSIGNED_INT_10F_11F_11F_REV, - * GL_UNSIGNED_INT_5_9_9_9_REV, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, - * GL_HILO16_NV, GL_SIGNED_HILO16_NV (27) - * @return null if glType is unhandled, otherwise the new Buffer object + * @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_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 null if glType is unhandled, otherwise the new Buffer object */ public static final Buffer newDirectGLBuffer(int glType, int numElements) { - switch (glType) { + 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: @@ -144,8 +213,12 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: case GL2GL3.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: @@ -159,13 +232,13 @@ public class GLBuffers extends Buffers { case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: return newDirectIntBuffer(numElements); - + case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return newDirectLongBuffer(numElements); - + case GL.GL_FLOAT: return newDirectFloatBuffer(numElements); - + case GL2.GL_DOUBLE: return newDirectDoubleBuffer(numElements); } @@ -173,34 +246,48 @@ public class GLBuffers extends Buffers { } /** - * @param glType shall be one of - * GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, - * GL_UNSIGNED_INT, GL_INT, GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, - * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, - * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, - * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, - * GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_INT_8_8_8_8_REV, - * GL_UNSIGNED_INT_10_10_10_2, GL_UNSIGNED_INT_2_10_10_10_REV - * GL_UNSIGNED_INT_24_8, GL_UNSIGNED_INT_10F_11F_11F_REV, - * GL_UNSIGNED_INT_5_9_9_9_REV, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, - * GL_HILO16_NV, GL_SIGNED_HILO16_NV (27) - * @return null if glType is unhandled or parent is null or bufLen is 0, otherwise the new Buffer object + * @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_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 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) { 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) { + 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: @@ -209,8 +296,13 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: case GL2GL3.GL_UNSIGNED_SHORT_5_5_5_1: case GL2GL3.GL_UNSIGNED_SHORT_1_5_5_5_REV: - return parent.asShortBuffer(); - + 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: + 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: @@ -223,47 +315,52 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV: case GL2.GL_HILO16_NV: case GL2.GL_SIGNED_HILO16_NV: - return parent.asIntBuffer(); - + res = parent.slice().order(parent.order()).asIntBuffer(); // slice and duplicate may change byte order + break; + case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - return parent.asLongBuffer(); - + res = parent.slice().order(parent.order()).asLongBuffer(); // slice and duplicate may change byte order + break; + case GL.GL_FLOAT: - return parent.asFloatBuffer(); - + res = parent.slice().order(parent.order()).asFloatBuffer(); // slice and duplicate may change byte order + break; + case GL2.GL_DOUBLE: - return parent.asDoubleBuffer(); + 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) { 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, + public static final int sizeof(GL gl, int tmp[], + int bytesPerPixel, int width, int height, int depth, boolean pack) { int rowLength = 0; int skipRows = 0; @@ -271,31 +368,31 @@ public class GLBuffers extends Buffers { 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); + 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 (depth > 1) { + 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); + 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); + if (depth > 1) { + imageHeight = glGetInteger(gl, GL2GL3.GL_UNPACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2GL3.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 @@ -304,13 +401,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; @@ -326,151 +423,195 @@ 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 - * GL_COLOR_INDEX, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL, - * GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_LUMINANCE, - * GL_RG, GL_LUMINANCE_ALPHA, - * GL_RGB, GL_BGR, GL_RGBA, GL_BGRA, GL_ABGR_EXT, - * GL_RED_INTEGER, GL_GREEN_INTEGER, GL_BLUE_INTEGER, - * GL_RG_INTEGER, GL_RGB_INTEGER, GL_BGR_INTEGER, - * GL_RGBA_INTEGER, GL_BGRA_INTEGER, GL_HILO_NV, GL_SIGNED_HILO_NV (26) - * - * @param type must be one of - * GL_BITMAP, - * GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, - * GL_UNSIGNED_INT, GL_INT, GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, - * GL_UNSIGNED_BYTE_3_3_2, GL_UNSIGNED_BYTE_2_3_3_REV, - * GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_5_6_5_REV, - * GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_SHORT_4_4_4_4_REV, - * GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_1_5_5_5_REV, - * GL_UNSIGNED_INT_8_8_8_8, GL_UNSIGNED_INT_8_8_8_8_REV, - * GL_UNSIGNED_INT_10_10_10_2, GL_UNSIGNED_INT_2_10_10_10_REV - * GL_UNSIGNED_INT_24_8, GL_UNSIGNED_INT_10F_11F_11F_REV, - * GL_UNSIGNED_INT_5_9_9_9_REV, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, - * GL_HILO16_NV, GL_SIGNED_HILO16_NV (28) - * + * + * @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/> + * * @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[], + 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; - if (width < 0) return 0; if (height < 0) return 0; if (depth < 0) return 0; - - switch (format) /* 24 */ { - 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."); - } - - switch (type) /* 26 */ { + + 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(int format, 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))); + compSize = 1; } 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: - esize = 2; + case GLES2.GL_HALF_FLOAT_OES: + 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: @@ -478,14 +619,16 @@ public class GLBuffers extends Buffers { case GL2GL3.GL_UNSIGNED_SHORT_4_4_4_4_REV: case GL2GL3.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: @@ -493,21 +636,102 @@ public class GLBuffers extends Buffers { 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; + compSize = 4; + compCount = 1; + break; case GL2GL3.GL_FLOAT_32_UNSIGNED_INT_24_8_REV: - esize = 8; - elements = 1; - break; - + 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(int format) throws GLException { + final int compCount; + + 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: + compCount = 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: + compCount = 2; + break; + case GL.GL_RGB: + case GL2GL3.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 GL2GL3.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 @@ -519,8 +743,8 @@ public class GLBuffers extends Buffers { power++; } return (1<<power); - } - + } + //---------------------------------------------------------------------- // Conversion routines // 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..cf88e7bf6 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -0,0 +1,192 @@ +/** + * 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.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 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(GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorStarted(GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; + } + + public static final boolean isAnimatorAnimatingOnOtherThread(GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorAnimating(GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; + } + + /** + * 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(GLAutoDrawable src, GLAutoDrawable dest, GLEventListener listener, boolean preserveInitState) { + final boolean initialized = src.getGLEventListenerInitState(listener); + src.removeGLEventListener(listener); + dest.addGLEventListener(listener); + if(preserveInitState && initialized) { + dest.setGLEventListenerInitState(listener, true); + dest.invoke(false, new GLEventListenerState.ReshapeGLEventListener(listener)); + } // else .. !init state is default + } + + /** + * 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(GLAutoDrawable src, GLAutoDrawable dest, boolean preserveInitState) { + for(int count = src.getGLEventListenerCount(); 0<count; count--) { + final GLEventListener listener = src.getGLEventListener(0); + moveGLEventListener(src, dest, listener, preserveInitState); + } + } + + /** + * 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> + * @param a + * @param b + * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. + */ + public static final void swapGLContextAndAllGLEventListener(GLAutoDrawable a, GLAutoDrawable b) { + final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a); + final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b); + + gllsA.moveTo(b); + gllsB.moveTo(a); + } + + /** + * 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> + * @param src + * @param dest + */ + public static final void swapGLContext(GLAutoDrawable src, GLAutoDrawable dest) { + final GLAnimatorControl aAnim = src.getAnimator(); + final GLAnimatorControl bAnim = dest.getAnimator(); + final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); + final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); + + for(int i = src.getGLEventListenerCount() - 1; 0 <= i; i--) { + src.disposeGLEventListener(src.getGLEventListener(i), false); + } + for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) { + dest.disposeGLEventListener(dest.getGLEventListener(i), false); + } + dest.setContext( src.setContext( dest.getContext(), false ), false ); + + src.invoke(true, GLEventListenerState.setViewport); + dest.invoke(true, GLEventListenerState.setViewport); + + if(aIsPaused) { aAnim.resume(); } + if(bIsPaused) { bAnim.resume(); } + } + + /** + * 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..223c23ebb --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelBuffer.java @@ -0,0 +1,488 @@ +/** + * 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.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; +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(boolean allowRowStride) { + this.allowRowStride = allowRowStride; + } + + @Override + public boolean getAllowRowStride() { return allowRowStride; } + + @Override + public GLPixelAttributes getAttributes(GL gl, 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 = GL2ES2.GL_ALPHA; + } + dType = GL.GL_UNSIGNED_BYTE; + } else if( 3 == componentCount ) { + dFormat = GL.GL_RGB; + dType = GL.GL_UNSIGNED_BYTE; + } else if( 4 == componentCount ) { + 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(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, int minByteSize) { + if( minByteSize > 0 ) { + return new GLPixelBuffer(pixelAttributes, width, height, depth, pack, Buffers.newDirectByteBuffer(minByteSize), getAllowRowStride()); + } else { + 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 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 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(int dataFormat, 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(int componentCount, int dataFormat, 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(PixelFormat pixFmt, 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 = GL2ES2.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(int componentCount, int dataFormat, int dataType, 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 GL2.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(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, Buffer buffer, 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(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(GL gl, int newWidth, 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..b6cff85f8 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java @@ -31,6 +31,7 @@ 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.GLException; @@ -39,64 +40,125 @@ import javax.media.opengl.GLException; * regardless of the GLProfile. */ public class GLPixelStorageModes { - private int[] savedGL2GL3Modes = new int[8]; - private int[] savedAlignment = new int[2]; + private final int[] savedGL2GL3Modes = new int[8]; + private final int[] savedAlignment = new int[2]; private boolean saved = false; + /** Create instance w/o {@link #save(GL)} */ + public GLPixelStorageModes() {} + + /** Create instance w/ {@link #save(GL)} */ + public GLPixelStorageModes(GL gl) { save(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 pixel storage modes if not saved yet. + * </p> */ public final void setPackAlignment(GL gl, int packAlignment) { - if(!saved) { save(gl); } - gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, packAlignment); + save(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 pixel storage modes if not saved yet. + * </p> */ public final void setUnpackAlignment(GL gl, int unpackAlignment) { - if(!saved) { save(gl); } - gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, unpackAlignment); + save(gl); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, unpackAlignment); } - + /** - * Sets the {@link GL2ES2.GL_PACK_ALIGNMENT} and {@link GL2ES2.GL_UNPACK_ALIGNMENT}. + * Sets the {@link GL#GL_PACK_ALIGNMENT} and {@link GL#GL_UNPACK_ALIGNMENT}. + * <p> * Saves the pixel storage modes if not saved yet. + * </p> */ public final void setAlignment(GL gl, int packAlignment, int unpackAlignment) { setPackAlignment(gl, packAlignment); setUnpackAlignment(gl, unpackAlignment); } - - private final void save(GL gl) { - if(gl.isGL2GL3()) { - if(gl.isGL2()) { + + /** + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH}. + * <p> + * Saves the pixel storage modes if not saved yet. + * </p> + */ + public final void setPackRowLength(GL2ES3 gl, int packRowLength) { + save(gl); + gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, packRowLength); + } + + /** + * Sets the {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. + * <p> + * Saves the pixel storage modes if not saved yet. + * </p> + */ + public final void setUnpackRowLength(GL2ES2 gl, int unpackRowLength) { + save(gl); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, unpackRowLength); + } + + /** + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. + * <p> + * Saves the pixel storage modes if not saved yet. + * </p> + */ + public final void setRowLength(GL2ES3 gl, int packRowLength, int unpackRowLength) { + setPackRowLength(gl, packRowLength); + setUnpackRowLength(gl, unpackRowLength); + } + + /** + * Save the pixel storage mode, if not saved yet. + * <p> + * Restore via {@link #restore(GL)} + * </p> + */ + public final void save(GL gl) { + if(saved) { + return; + } + + if( gl.isGL2ES3() ) { + 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); + gl.glGetIntegerv(GL2ES3.GL_PACK_ROW_LENGTH, savedGL2GL3Modes, 0); + gl.glGetIntegerv(GL2ES3.GL_PACK_SKIP_ROWS, savedGL2GL3Modes, 1); + gl.glGetIntegerv(GL2ES3.GL_PACK_SKIP_PIXELS, savedGL2GL3Modes, 2); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_ROW_LENGTH, savedGL2GL3Modes, 4); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_ROWS, savedGL2GL3Modes, 5); + gl.glGetIntegerv(GL2ES2.GL_UNPACK_SKIP_PIXELS, savedGL2GL3Modes, 6); + if( gl.isGL2GL3() ) { + gl.glGetIntegerv(GL2GL3.GL_PACK_SWAP_BYTES, savedGL2GL3Modes, 3); + gl.glGetIntegerv(GL2GL3.GL_UNPACK_SWAP_BYTES, savedGL2GL3Modes, 7); + } + } + gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); + if( gl.isGL2GL3() ) { + gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, 0); + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, 0); } - 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); } else { - // embedded deals with pack/unpack alignment only + // ES1 or ES2 deals with pack/unpack alignment only gl.glGetIntegerv(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment, 0); gl.glGetIntegerv(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment, 1); - } + } saved = true; } @@ -108,29 +170,31 @@ public class GLPixelStorageModes { if(!saved) { throw new GLException("pixel storage modes not saved"); } - - if(gl.isGL2GL3()) { - if(gl.isGL2()) { + + if( gl.isGL2ES3() ) { + if( gl.isGL2() ) { gl.getGL2().glPopClientAttrib(); } 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_PACK_ROW_LENGTH, savedGL2GL3Modes[0]); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, savedGL2GL3Modes[1]); + gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, savedGL2GL3Modes[2]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, savedGL2GL3Modes[4]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, savedGL2GL3Modes[5]); + gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, savedGL2GL3Modes[6]); + if( gl.isGL2GL3() ) { + gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, savedGL2GL3Modes[3]); + gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, savedGL2GL3Modes[7]); + } } } else { - // embedded deals with pack/unpack alignment only + // ES1 or ES2 deals with pack/unpack alignment only gl.glPixelStorei(GL2ES2.GL_PACK_ALIGNMENT, savedAlignment[0]); gl.glPixelStorei(GL2ES2.GL_UNPACK_ALIGNMENT, savedAlignment[1]); - } + } saved = false; - } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index 368cbc0a2..25a012bb9 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,23 +20,30 @@ * 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 javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; +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; /** @@ -44,34 +51,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(); + this(GLPixelBuffer.defaultProviderNoRowStride, alpha, write2Texture); } - + + public GLReadBufferUtil(GLPixelBufferProvider pixelBufferProvider, boolean alpha, 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 == 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 */ @@ -81,7 +100,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. @@ -102,93 +121,134 @@ public class GLReadBufferUtil { } /** - * Read the drawable's pixels to TextureData and Texture, if requested at construction - * - * @param gl the current GL object - * @param drawable the drawable to read from - * @param flip weather to flip the data vertically or not - * + * 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 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, GLDrawable drawable, boolean flip) { - 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("GL_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]; + public boolean readPixels(GL gl, 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(GL gl, int inX, int inY, int inWidth, int inHeight, boolean mustFlipVertically) { + final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + final int width, height; + if( 0 >= inWidth || drawable.getWidth() < inWidth ) { + width = drawable.getWidth(); + } else { + width = inWidth; + } + if( 0 >= inHeight || drawable.getHeight() < inHeight ) { + height = drawable.getHeight(); } 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; + 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 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 { + 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) { 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); + if(gl.isGL2ES3()) { + final GL2ES3 gl2es3 = gl.getGL2ES3(); + gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 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(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(); } @@ -197,16 +257,15 @@ public class GLReadBufferUtil { return res; } - public void dispose(GL gl) { + public void dispose(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..966781906 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,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/ImmModeSink.java b/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java index 3b817afcf..986f6d8d3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java +++ b/src/jogl/classes/com/jogamp/opengl/util/ImmModeSink.java @@ -1,58 +1,162 @@ package com.jogamp.opengl.util; -import com.jogamp.common.util.*; -import com.jogamp.opengl.util.glsl.ShaderState; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Iterator; -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.GLException; +import javax.media.opengl.fixedfunc.GLPointerFunc; -import java.nio.*; -import java.util.Iterator; -import java.util.ArrayList; +import jogamp.opengl.Debug; -public class ImmModeSink { +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.os.Platform; +import com.jogamp.opengl.util.glsl.ShaderState; - public static final boolean DEBUG_BEGIN_END = false; - public static final boolean DEBUG_DRAW = false; +/** + * <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 = Debug.isPropertyDefined("jogl.debug.ImmModeSink.BeginEnd", true); + DEBUG_DRAW = Debug.isPropertyDefined("jogl.debug.ImmModeSink.Draw", true); + DEBUG_BUFFER = Debug.isPropertyDefined("jogl.debug.ImmModeSink.Buffer", true); + } - // 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, + public static ImmModeSink createFixed(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); + int cComps, int cDataType, + int nComps, int nDataType, + int tComps, int tDataType, + 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, + public static ImmModeSink createGLSL(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); + int cComps, int cDataType, + int nComps, int nDataType, + int tComps, int tDataType, + int glBufferUsage, 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(int initialElementCount, + int vComps, int vDataType, + int cComps, int cDataType, + int nComps, int nDataType, + int tComps, int tDataType, + int glBufferUsage, int shaderProgram) { + return new ImmModeSink(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + true, glBufferUsage, null, shaderProgram); + } public void destroy(GL gl) { destroyList(gl); @@ -69,6 +173,7 @@ public class ImmModeSink { vboSet.reset(gl); } + @Override public String toString() { StringBuilder sb = new StringBuilder("ImmModeSink["); sb.append(",\n\tVBO list: "+vboSetList.size()+" ["); @@ -88,45 +193,38 @@ public class ImmModeSink { public void draw(GL gl, 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) { 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); } @@ -144,8 +242,7 @@ public class ImmModeSink { private void glEnd(GL gl, Buffer indices, 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); @@ -155,7 +252,10 @@ 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+")"); } } @@ -244,10 +344,18 @@ public class ImmModeSink { vboSet.glColor3b(x,y,z); } + public final void glColor3ub(byte x, byte y, byte z) { + vboSet.glColor3ub(x,y,z); + } + public final void glColor4b(byte x, byte y, byte z, byte a) { vboSet.glColor4b(x,y,z,a); } + public final void glColor4ub(byte x, byte y, byte z, byte a) { + vboSet.glColor4ub(x,y,z,a); + } + public final void glTexCoord2b(byte x, byte y) { vboSet.glTexCoord2b(x,y); } @@ -256,50 +364,94 @@ public class ImmModeSink { vboSet.glTexCoord3b(x,y,z); } - protected ImmModeSink(GL gl, int glBufferUsage, int initialElementCount, + protected ImmModeSink(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); + int cComps, int cDataType, + int nComps, int nDataType, + int tComps, int tDataType, + boolean useGLSL, int glBufferUsage, ShaderState st, int shaderProgram) { + vboSet = new VBOSet(initialElementCount, + vComps, vDataType, cComps, cDataType, nComps, nDataType, tComps, tDataType, + useGLSL, glBufferUsage, st, shaderProgram); this.vboSetList = new ArrayList<VBOSet>(); } + 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(int v) { vboSet.setResizeElementCount(v); } + private void destroyList(GL gl) { - for(Iterator<VBOSet> i=vboSetList.iterator(); i.hasNext() ; ) { - i.next().destroy(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, + protected VBOSet (int initialElementCount, int vComps, int vDataType, - int cComps, int cDataType, - int nComps, int nDataType, - int tComps, int tDataType, boolean useGLSL) { - this.gl=gl; + int cComps, int cDataType, + int nComps, int nDataType, + int tComps, int tDataType, + boolean useGLSL, int glBufferUsage, ShaderState st, 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; + + this.vCount=0; + this.cCount=0; + this.nCount=0; + this.tCount=0; + this.vElems=0; + this.cElems=0; + this.nElems=0; + this.tElems=0; - allocateBuffer(initialElementCount); + this.pageSize = Platform.getMachineDescription().pageSizeInBytes(); + + reallocateBuffer(initialElementCount); rewind(); this.sealed=false; @@ -308,304 +460,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(int v) { resizeElementCount=v; } + + protected boolean getUseVBO() { return useVBO; } + + protected final VBOSet regenerate(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 { 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); + } + } + } + + private boolean usingShaderProgram = false; + + protected void useShaderProgram(GL2ES2 gl, boolean force) { + if( force || !usingShaderProgram ) { + if(null != shaderState) { + shaderState.useProgram(gl, true); + } else /* if( 0 != shaderProgram) */ { + gl.glUseProgram(shaderProgram); } + usingShaderProgram = true; } } protected void draw(GL gl, Buffer indices, boolean disableBufferAfterDraw, 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) { - GL2ES1 glf = gl.getGL2ES1(); - if(null==indices) { - glf.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, (int)(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, (int)(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, (int)(0xffffffff & b.get(idx0+j)), 4); + } + } + } else { + ((GL2ES1)gl).glDrawElements(mode, idxLen, type, indices); + // GL2: gl.glDrawRangeElements(mode, 0, idxLen-1, idxLen, type, indices); } - glf.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) { checkSeal(false); - GLBuffers.put(vertexArray, v); + Buffers.put(vertexArray, v); } public void glNormalv(Buffer v) { checkSeal(false); - GLBuffers.put(normalArray, v); + Buffers.put(normalArray, v); } public void glColorv(Buffer v) { checkSeal(false); - GLBuffers.put(colorArray, v); + Buffers.put(colorArray, v); } public void glTexCoordv(Buffer v) { checkSeal(false); - GLBuffers.put(textCoordArray, v); + Buffers.put(textCoordArray, v); } public void glVertex2b(byte x, 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); + 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(byte x, byte y, 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); + 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(short x, 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); + 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(short x, short y, 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); + 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(float x, 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); + 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(float x, float y, 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) { 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); + 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(short x, short y, 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); + 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(float x, float y, 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) { 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); + 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(byte r, byte g, byte b) { + checkSeal(false); + 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(byte r, byte g, byte b, byte a) { 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); + 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(byte r, byte g, byte b, byte a) { + checkSeal(false); + 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(short r, short g, short b) { 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); + 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(short r, short g, short b, short 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); + 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(float r, float g, float 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); + 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(float r, float g, float b, float 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); + 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(byte x, byte y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.putb(textCoordArray, x); - if(tComps>1) - GLBuffers.putb(textCoordArray, y); - padding(TEXTCOORD, tComps-2); + 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(byte x, byte y, byte z) { 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); + 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(short x, short y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.puts(textCoordArray, x); - if(tComps>1) - GLBuffers.puts(textCoordArray, y); - padding(TEXTCOORD, tComps-2); + 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(short x, short y, short 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); + 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(float x, float y) { checkSeal(false); - growBufferIfNecessary(TEXTCOORD, 2); - if(tComps>0) - GLBuffers.putf(textCoordArray, x); - if(tComps>1) - GLBuffers.putf(textCoordArray, y); - padding(TEXTCOORD, tComps-2); + 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(float x, float y, float z) { 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); + if(tComps>2) + Buffers.putNf(textCoordArray, tDataTypeSigned, z); + countAndPadding(TEXTCOORD, tComps-3); } public void rewind() { @@ -623,13 +858,58 @@ public class ImmModeSink { } } + public void setShaderProgram(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(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(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) { @@ -646,8 +926,13 @@ 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) @@ -657,19 +942,25 @@ public class ImmModeSink { sealedGL = seal; 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) { + 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); @@ -682,126 +973,287 @@ public class ImmModeSink { sealed = seal; if(seal) { bufferWritten=false; + rewind(); } } public void enableBuffer(GL gl, boolean enable) { - /* if(enableBufferAlways && enable) { - bufferEnabled = false; - } */ - if( bufferEnabled != enable && count>0 ) { + 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) { + private final void writeBuffer(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(GL gl, boolean enable) { 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); - } + private void enableBufferGLSLShaderState(GL gl, boolean enable) { GL2ES2 glsl = gl.getGL2ES2(); - - if(enable) { - glsl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboName); - 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(GL gl, boolean enable) { + 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+ @@ -811,165 +1263,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+"]"; + } - count = elementCount; - bSize = count * ( vWidth + cWidth + nWidth + tWidth ) ; + protected boolean fitElementInBuffer(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"); + } + } + + protected boolean reallocateBuffer(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 ); + + 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; - buffer = GLBuffers.newDirectByteBuffer(bSize); + buffer = Buffers.newDirectByteBuffer( vBytes + cBytes + nBytes + tBytes ); + vOffset = 0; - int pos = 0; - int size= count * vWidth ; - if(size>0) { - vertexArray = GLBuffers.sliceGLBuffer(buffer, pos, size, vDataType); + 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(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(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..218897ffe 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,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. - * + * */ package com.jogamp.opengl.util; @@ -37,8 +37,6 @@ 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; @@ -46,37 +44,176 @@ import javax.media.opengl.fixedfunc.GLMatrixFunc; import jogamp.opengl.ProjectFloat; -import com.jogamp.opengl.FloatUtil; import com.jogamp.common.nio.Buffers; - +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.FloatStack; +import com.jogamp.opengl.math.FloatUtil; +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. + * </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 use a common FloatBuffer storage + * and are a {@link Buffers#slice2Float(Buffer, float[], int, int) sliced} representation of it. + * The common FloatBuffer and hence all matrices may use NIO direct storage or a {@link #usesBackingArray() backing float array}, + * depending how the instance if {@link #PMVMatrix(boolean) being constructed}. + * </p> + * <p> + * <b>Note:</b> + * <ul> + * <li>The matrix is a {@link Buffers#slice2Float(Buffer, 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 class PMVMatrix implements GLMatrixFunc { - protected final float[] matrixBufferArray; + /** 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; + + /** + * @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 static final boolean isMatrixModeName(final int matrixModeName) { + switch(matrixModeName) { + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + return true; + } + 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: + return GL_MODELVIEW_MATRIX; + case GL_PROJECTION: + return GL_PROJECTION_MATRIX; + case GL.GL_TEXTURE: + return GL_TEXTURE_MATRIX; + default: + throw new GLException("unsupported matrixName: "+matrixModeName); + } + } + + /** + * @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: + case GL_MODELVIEW_MATRIX: + case GL_PROJECTION_MATRIX: + case GL_TEXTURE_MATRIX: + return true; + } + 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: + return GL_MODELVIEW; + case GL_PROJECTION_MATRIX: + return GL_PROJECTION; + case GL_TEXTURE_MATRIX: + return GL.GL_TEXTURE; + default: + throw new GLException("unsupported matrixGetName: "+matrixGetName); + } + } + + /** + * @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 + */ + public static StringBuilder matrixToString(StringBuilder sb, String f, FloatBuffer a) { + return FloatUtil.matrixToString(sb, null, f, a, 0, 4, 4, false); + } + + /** + * @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 + */ + public static StringBuilder matrixToString(StringBuilder sb, String f, FloatBuffer a, FloatBuffer b) { + return FloatUtil.matrixToString(sb, null, f, a, 0, b, 0, 4, 4, false); + } /** * Creates an instance of PMVMatrix {@link #PMVMatrix(boolean) PMVMatrix(boolean useBackingArray)}, - * with <code>useBackingArray = true</code>. + * with <code>useBackingArray = true</code>. */ 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> + * 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, + * 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 @@ -91,24 +228,24 @@ public class PMVMatrix implements GLMatrixFunc { 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 + 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]; @@ -122,8 +259,10 @@ public class PMVMatrix implements GLMatrixFunc { FloatUtil.makeIdentityf(matrixOrtho, 0); FloatUtil.makeZero(matrixFrustum, 0); - matrixPStack = new ArrayList<float[]>(); - matrixMvStack= new ArrayList<float[]>(); + // 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) // default values and mode glMatrixMode(GL_PROJECTION); @@ -132,20 +271,26 @@ public class PMVMatrix implements GLMatrixFunc { glLoadIdentity(); glMatrixMode(GL.GL_TEXTURE); glLoadIdentity(); - setDirty(); - update(); + modifiedBits = MODIFIED_ALL; + dirtyBits = DIRTY_ALL; + requestMask = 0; + matrixMode = GL_MODELVIEW; + + mulPMV = null; + frustum = null; } - public final boolean usesBackingArray() { return usesBackingArray; } - - public void destroy() { + /** @see #PMVMatrix(boolean) */ + public final boolean usesBackingArray() { return usesBackingArray; } + + public final 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; + matrixBuffer=null; matrixPMvMvit=null; matrixPMvMvi=null; matrixPMv=null; + matrixP=null; matrixTex=null; matrixMv=null; matrixMvi=null; matrixMvit=null; vec3f = null; matrixMult = null; @@ -154,185 +299,178 @@ public class PMVMatrix implements GLMatrixFunc { matrixScale = null; matrixOrtho = null; matrixFrustum = null; - + if(null!=matrixPStack) { - matrixPStack.clear(); matrixPStack=null; + matrixPStack=null; } if(null!=matrixMvStack) { - matrixMvStack.clear(); matrixMvStack=null; + matrixMvStack=null; } if(null!=matrixPStack) { - matrixPStack.clear(); matrixPStack=null; + matrixPStack=null; } if(null!=matrixTStack) { - matrixTStack.clear(); matrixTStack=null; - } - } - - - public static final boolean isMatrixModeName(final int matrixModeName) { - switch(matrixModeName) { - case GL_MODELVIEW_MATRIX: - case GL_PROJECTION_MATRIX: - case GL_TEXTURE_MATRIX: - return true; + matrixTStack=null; } - return false; } - public static final int matrixModeName2MatrixGetName(final int matrixModeName) { - switch(matrixModeName) { - case GL_MODELVIEW: - return GL_MODELVIEW_MATRIX; - case GL_PROJECTION: - return GL_PROJECTION_MATRIX; - case GL.GL_TEXTURE: - return GL_TEXTURE_MATRIX; - default: - throw new GLException("unsupported matrixName: "+matrixModeName); - } - } - - public static final boolean isMatrixGetName(final int matrixGetName) { - switch(matrixGetName) { - case GL_MATRIX_MODE: - case GL_MODELVIEW_MATRIX: - case GL_PROJECTION_MATRIX: - case GL_TEXTURE_MATRIX: - return true; - } - return false; - } - - public static final int matrixGetName2MatrixModeName(final int matrixGetName) { - switch(matrixGetName) { - case GL_MODELVIEW_MATRIX: - return GL_MODELVIEW; - case GL_PROJECTION_MATRIX: - return GL_PROJECTION; - case GL_TEXTURE_MATRIX: - return GL.GL_TEXTURE; - default: - throw new GLException("unsupported matrixGetName: "+matrixGetName); - } - } - - public void setDirty() { - modified = DIRTY_MODELVIEW | DIRTY_PROJECTION | DIRTY_TEXTURE ; - matrixMode = GL_MODELVIEW; - } - - public int getDirtyBits() { - return modified; - } - - 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; - } - - public boolean isDirty() { - return modified!=0; - } - - /** - * Update the derived Mvi, Mvit and Pmv matrices - * in case Mv or P has changed. - * - * @return - */ - public boolean update() { - if(0==modified) return false; - - final int res = modified; - if( (res&DIRTY_MODELVIEW)!=0 ) { - setMviMvit(); - } - modified=0; - return res!=0; - } + /** 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 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 + */ 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 + // + // 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,27 +482,32 @@ public class PMVMatrix implements GLMatrixFunc { matrixMode = matrixName; } - public void glGetFloatv(int matrixGetName, FloatBuffer params) { + @Override + public final void glGetFloatv(int matrixGetName, FloatBuffer params) { int pos = params.position(); if(matrixGetName==GL_MATRIX_MODE) { params.put((float)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(int matrixGetName, float[] params, int params_offset) { if(matrixGetName==GL_MATRIX_MODE) { params[params_offset]=(float)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) { + + @Override + public final void glGetIntegerv(int pname, IntBuffer params) { int pos = params.position(); if(pname==GL_MATRIX_MODE) { params.put(matrixMode); @@ -373,7 +516,9 @@ public class PMVMatrix implements GLMatrixFunc { } params.position(pos); } - public void glGetIntegerv(int pname, int[] params, int params_offset) { + + @Override + public final void glGetIntegerv(int pname, int[] params, int params_offset) { if(pname==GL_MATRIX_MODE) { params[params_offset]=matrixMode; } else { @@ -381,115 +526,131 @@ 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; + } } + @Override public final void glLoadMatrixf(java.nio.FloatBuffer m) { 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); } + @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 ; + 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 ; + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matrixTex.put(matrixIdent); matrixTex.reset(); - modified |= DIRTY_TEXTURE ; - } + modifiedBits |= MODIFIED_TEXTURE; + } matrixIdent.reset(); } + @Override public final void glMultMatrixf(final FloatBuffer m) { if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrixf(matrixMv, m, matrixMv); - modified |= DIRTY_MODELVIEW ; + FloatUtil.multMatrixf(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.multMatrixf(matrixP, m); + dirtyBits |= DIRTY_FRUSTUM ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.multMatrixf(matrixTex, m, matrixTex); - modified |= DIRTY_TEXTURE ; - } + FloatUtil.multMatrixf(matrixTex, m); + modifiedBits |= MODIFIED_TEXTURE; + } } - public void glMultMatrixf(float[] m, int m_offset) { + @Override + public final void glMultMatrixf(float[] m, int m_offset) { if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrixf(matrixMv, m, m_offset, matrixMv); - modified |= DIRTY_MODELVIEW ; + FloatUtil.multMatrixf(matrixMv, 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.multMatrixf(matrixP, 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.multMatrixf(matrixTex, m, m_offset); + modifiedBits |= MODIFIED_TEXTURE; + } } + @Override public final void glTranslatef(final float x, final float y, final float z) { - // Translation matrix: + // Translation matrix: // 1 0 0 x // 0 1 0 y // 0 0 1 z @@ -500,10 +661,11 @@ public class PMVMatrix implements GLMatrixFunc { glMultMatrixf(matrixTrans, 0); } + @Override 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 ic= 1.0f - c; final float s = (float)Math.sin(angrad); vec3f[0]=x; vec3f[1]=y; vec3f[2]=z; @@ -536,8 +698,9 @@ public class PMVMatrix implements GLMatrixFunc { glMultMatrixf(matrixRot, 0); } + @Override public final void glScalef(final float x, final float y, final float z) { - // Scale matrix: + // Scale matrix: // x 0 0 0 // 0 y 0 0 // 0 0 z 0 @@ -549,8 +712,9 @@ public class PMVMatrix implements GLMatrixFunc { glMultMatrixf(matrixScale, 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: + // Ortho matrix: // 2/dx 0 0 tx // 0 2/dy 0 ty // 0 0 2/dz tz @@ -572,14 +736,7 @@ public class PMVMatrix implements GLMatrixFunc { 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); - } - + @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"); @@ -587,7 +744,7 @@ public class PMVMatrix implements GLMatrixFunc { if(left==right || top==bottom) { throw new GLException("GL_INVALID_VALUE: top,bottom and left,right must not be equal"); } - // Frustum matrix: + // Frustum matrix: // 2*zNear/dx 0 A 0 // 0 2*zNear/dy B 0 // 0 0 C D @@ -614,15 +771,34 @@ public class PMVMatrix implements GLMatrixFunc { glMultMatrixf(matrixFrustum, 0); } - public void gluLookAt(float eyex, float eyey, float eyez, + // + // Extra functionality + // + + /** + * {@link #glMultMatrixf(FloatBuffer) Multiply} the {@link #glGetMatrixMode() current matrix} with the perspective/frustum matrix. + */ + 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); + } + + /** + * {@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(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); } /** - * Uses this instance {@link #glGetMvMatrixf()} and {@link #glGetPMatrixf()} - * + * Map object coordinates to window coordinates. + * * @param objx * @param objy * @param objz @@ -632,27 +808,27 @@ public class PMVMatrix implements GLMatrixFunc { * @param win_pos_offset * @return */ - public boolean gluProject(float objx, float objy, float objz, + public final 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, + matrixMv.array(), matrixMv.position(), + matrixP.array(), matrixP.position(), + viewport, viewport_offset, win_pos, win_pos_offset); } else { return projectFloat.gluProject(objx, objy, objz, matrixMv, matrixP, - viewport, viewport_offset, + viewport, viewport_offset, win_pos, win_pos_offset); } } /** - * Uses this instance {@link #glGetMvMatrixf()} and {@link #glGetPMatrixf()} - * + * Map window coordinates to object coordinates. + * * @param winx * @param winy * @param winz @@ -662,58 +838,250 @@ public class PMVMatrix implements GLMatrixFunc { * @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) { + public final 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, + matrixMv.array(), matrixMv.position(), + matrixP.array(), matrixP.position(), + viewport, viewport_offset, obj_pos, obj_pos_offset); } else { return projectFloat.gluUnProject(winx, winy, winz, matrixMv, matrixP, - viewport, viewport_offset, + viewport, viewport_offset, obj_pos, obj_pos_offset); - } + } } - - public void gluPickMatrix(float x, float y, + + public final 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); } - + + public StringBuilder toString(StringBuilder sb, 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[backingArray ").append(this.usesBackingArray()); + sb.append(", 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); + sb.append("], Projection").append(Platform.NEWLINE); + matrixToString(sb, f, matrixP); + sb.append(", Modelview").append(Platform.NEWLINE); + matrixToString(sb, f, matrixMv); + sb.append(", Texture").append(Platform.NEWLINE); + matrixToString(sb, f, matrixTex); + if( 0 != ( requestMask & DIRTY_INVERSE_MODELVIEW ) ) { + sb.append(", Inverse Modelview").append(Platform.NEWLINE); + matrixToString(sb, f, matrixMvi); + } + if( 0 != ( requestMask & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) { + sb.append(", Inverse Transposed Modelview").append(Platform.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(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(boolean clearModBits) { + boolean mod = 0 != modifiedBits; + if(clearModBits) { + modifiedBits = 0; + } + + if( 0 != ( dirtyBits & ( DIRTY_FRUSTUM & requestMask ) ) ) { + if( null == frustum ) { + frustum = new Frustum(); + mulPMV = new float[16]; + } + FloatUtil.multMatrixf(matrixP, matrixMv, mulPMV, 0); + frustum.updateByPMV(mulPMV, 0); + dirtyBits &= ~DIRTY_FRUSTUM; + mod = true; + } + + if( 0 == ( dirtyBits & requestMask ) ) { + return mod; // nothing more requested which may have been dirty + } + + if(nioBackupArraySupported>=0) { + try { + nioBackupArraySupported = 1; + return setMviMvitNIOBackupArray() || mod; + } catch(UnsupportedOperationException uoe) { + nioBackupArraySupported = -1; + } + } + return setMviMvitNIODirectAccess() || mod; + } + // - // private + // 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; - } - } - setMviMvitNIODirectAccess(); - } - } - private final void setMviMvitNIOBackupArray() { + private final boolean setMviMvitNIOBackupArray() { final float[] _matrixMvi = matrixMvi.array(); final int _matrixMviOffset = matrixMvi.position(); - if(!projectFloat.gluInvertMatrixf(matrixMv.array(), matrixMv.position(), _matrixMvi, _matrixMviOffset)) { - throw new GLException(msgCantComputeInverse); + boolean res = false; + if( 0 != ( dirtyBits & DIRTY_INVERSE_MODELVIEW ) ) { // only if dirt; always requested at this point, see update() + if(!projectFloat.gluInvertMatrixf(matrixMv.array(), matrixMv.position(), _matrixMvi, _matrixMviOffset)) { + throw new GLException(msgCantComputeInverse); + } + dirtyBits &= ~DIRTY_INVERSE_MODELVIEW; + res = true; } - if( 0 != (usesMviMvit & 2) ) { - // transpose matrix + if( 0 != ( requestMask & ( dirtyBits & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty + // transpose matrix final float[] _matrixMvit = matrixMvit.array(); final int _matrixMvitOffset = matrixMvit.position(); for (int i = 0; i < 4; i++) { @@ -721,34 +1089,45 @@ public class PMVMatrix implements GLMatrixFunc { _matrixMvit[_matrixMvitOffset+j+i*4] = _matrixMvi[_matrixMviOffset+i+j*4]; } } - } + dirtyBits &= ~DIRTY_INVERSE_TRANSPOSED_MODELVIEW; + res = true; + } + return res; } - - private final void setMviMvitNIODirectAccess() { - if(!projectFloat.gluInvertMatrixf(matrixMv, matrixMvi)) { - throw new GLException(msgCantComputeInverse); + + private final boolean setMviMvitNIODirectAccess() { + boolean res = false; + if( 0 != ( dirtyBits & DIRTY_INVERSE_MODELVIEW ) ) { // only if dirt; always requested at this point, see update() + if(!projectFloat.gluInvertMatrixf(matrixMv, matrixMvi)) { + throw new GLException(msgCantComputeInverse); + } + dirtyBits &= ~DIRTY_INVERSE_MODELVIEW; + res = true; } - if( 0 != (usesMviMvit & 2) ) { - // transpose matrix + if( 0 != ( requestMask & ( dirtyBits & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty + // 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)); } } - } + dirtyBits &= ~DIRTY_INVERSE_TRANSPOSED_MODELVIEW; + res = true; + } + return res; } + protected final float[] matrixBufferArray; 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 FloatStack 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 int modifiedBits = MODIFIED_ALL; + protected int dirtyBits = DIRTY_ALL; // contains the dirty bits, i.e. hinting for update operation + protected int requestMask = 0; // may contain the requested dirty bits: DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW 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; + protected float[] mulPMV; // premultiplied PMV + protected 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..fd8f54152 --- /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(PixelFormat dest_fmt, ByteBuffer d, int dOff, byte lum, 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..1c87dad4e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/RandomTileRenderer.java @@ -0,0 +1,237 @@ +/** + * 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(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(int tX, int tY, int tWidth, 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(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( 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(); + + // save current glPixelStore values + psm.save(gl); + 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(int tX, int tY, int tWidth, 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 index b949f0e39..47d56bcb1 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/TGAWriter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/TGAWriter.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,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. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -46,7 +46,7 @@ import java.nio.channels.*; * 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; @@ -91,7 +91,7 @@ public class TGAWriter { 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 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..7f86b14c6 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java @@ -0,0 +1,537 @@ +/** + * 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(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(int width, 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(int width, 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(int width, int height, 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(int xoff, 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(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(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( 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 ); + + 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( 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(); + + // save current glPixelStore values + psm.save(gl); + 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..dabde83dd --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRendererBase.java @@ -0,0 +1,665 @@ +/** + * 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 javax.media.opengl.GLFBODrawable; +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(Object o) { + final int h = null != o ? o.hashCode() : 0; + return "0x"+Integer.toHexString(h); + } + protected StringBuilder tileDetails(StringBuilder sb) { + return sb.append("cur "+currentTileXPos+"/"+currentTileYPos+" "+currentTileWidth+"x"+currentTileHeight+", buffer "+hashStr(tileBuffer)); + } + public StringBuilder toString(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() { + 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(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(int width, 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(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(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(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(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(GLEventListener preTile, 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(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(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(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(GLAutoDrawable drawable, int x, int y, int width, 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..b29846d91 --- /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(int pts, 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(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(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..b964245ad --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java @@ -0,0 +1,444 @@ +/** + * 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(int sampleRate, int sampleSize, int channelCount, boolean signed, boolean fixedP, boolean planar, 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(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(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(int sampleCount) { + return ( 1000f * (float) sampleCount ) / (float)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(int millisecs, float frameDuration) { + return Math.max(1, (int) ( (float)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(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 sampleCount sample count + */ + public final int getBytesSampleCount(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(int pts, int duration, 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(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(int pts, int duration, ByteBuffer bytes, 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 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 the remaining bytes of the given {@link AudioDataFrame}'s direct 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}, which may differ from the input <code>audioDataFrame</code>. + * @deprecated User shall use {@link #enqueueData(int, ByteBuffer, int)}, which allows implementation + * to reuse specialized {@link AudioFrame} instances. + */ + public AudioFrame enqueueData(AudioDataFrame audioDataFrame); + + /** + * 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..2cfd40df7 --- /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, String implName) { + final AudioSink audioSink; + if(ReflectionUtil.isClassAvailable(implName, cl)){ + try { + audioSink = (AudioSink) ReflectionUtil.createInstance(implName, cl); + if( audioSink.isInitialized() ) { + return audioSink; + } + } catch (Throwable t) { + if(AudioSink.DEBUG) { System.err.println("Catched "+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..5a455c8bd 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,533 @@ * 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 java.net.URI; import javax.media.opengl.GL; +import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +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>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> */ 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#getScheme() URI scheme} name {@value} for camera input. E.g. <code>camera:/0</code> + * for the 1st camera device. + * <p> + * The {@link URI#getRawPath() 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#getRawQuery() 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 String CameraInputScheme = "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(Throwable cause) { + super(cause); + } + public StreamException(String message, 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){ 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 + */ + 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)}. */ + 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 +554,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..248e265f5 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, String implName) { + try { + if(((Boolean)ReflectionUtil.callStaticMethod(implName, isAvailableMethodName, null, null, cl)).booleanValue()) { + return (GLMediaPlayer) ReflectionUtil.createInstance(implName, cl); + } + } catch (Throwable t) { if(GLMediaPlayer.DEBUG) { System.err.println("Catched "+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..a33356067 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLPixelBuffer.java @@ -0,0 +1,248 @@ +/** + * 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.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(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, BufferedImage image, + Buffer buffer, 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(int width, 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 = WritableRaster.createWritableRaster(sppsm1, dataBuffer, null); + return new BufferedImage (cm, raster1, cm.isAlphaPremultiplied(), null); + } + } + + public final boolean isDataBufferSource(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(boolean allowRowStride) { + this.allowRowStride = allowRowStride; + } + @Override + public boolean getAllowRowStride() { return allowRowStride; } + + @Override + public GLPixelAttributes getAttributes(GL gl, 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(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, 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(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(GL gl, GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, 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(GLPixelAttributes pixelAttributes, int width, int height, int depth, boolean pack, 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(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(int componentCount, int width, int height, int depth, 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..9490e041b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/AWTGLReadBufferUtil.java @@ -0,0 +1,109 @@ +/** + * 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(GLProfile glp, boolean alpha) { + super(new AWTGLPixelBuffer.AWTGLPixelBufferProvider( glp.isGL2ES3() /* allowRowStride */ ), alpha, false); + } + + 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(GL gl, 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(GL gl, int inX, int inY, int inWidth, int inHeight, boolean awtOrientation) { + final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + final int width, height; + if( 0 >= inWidth || drawable.getWidth() < inWidth ) { + width = drawable.getWidth(); + } else { + width = inWidth; + } + if( 0 >= inHeight || drawable.getHeight() < inHeight ) { + height = drawable.getHeight(); + } 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..df3cc4a39 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. */ @@ -54,7 +54,7 @@ public class ImageUtil { 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); @@ -97,21 +97,21 @@ public class ImageUtil { 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(); 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(); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, @@ -120,7 +120,7 @@ public class ImageUtil { 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..931f59869 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. */ diff --git a/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java b/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java index fa66673fd..f686b672a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/Screenshot.java @@ -1,21 +1,22 @@ /* * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * + * Copyright (c) 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: - * + * * - 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. @@ -43,20 +44,30 @@ import java.io.IOException; import java.nio.ByteBuffer; import javax.imageio.ImageIO; +import javax.media.opengl.GL; import javax.media.opengl.GL2; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; 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. */ - +/** + * Utilities for taking screenshots of OpenGL applications. + * @deprecated Please consider using {@link com.jogamp.opengl.util.GLReadBufferUtil}, + * which is AWT independent and does not require a CPU based vertical image flip + * in case drawable {@link GLDrawable#isGLOriented() is in OpenGL orientation}. + * Further more you may use {@link AWTGLReadBufferUtil} to read out + * the framebuffer into a BufferedImage for further AWT processing. + */ 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, @@ -83,7 +94,7 @@ public class Screenshot { 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, @@ -111,7 +122,7 @@ public class Screenshot { 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, @@ -148,17 +159,17 @@ public class Screenshot { writer.open(file, width, height, alpha); ByteBuffer bgr = writer.getImageData(); - GL2 gl = GLUgl2.getCurrentGL2(); + GL gl = GLContext.getCurrentGL(); // Set up pixel storage modes GLPixelStorageModes psm = new GLPixelStorageModes(); psm.setPackAlignment(gl, 1); - int readbackType = (alpha ? GL2.GL_ABGR_EXT : GL2.GL_BGR); + int readbackType = (alpha ? GL2.GL_ABGR_EXT : GL2GL3.GL_BGR); // read the BGR values into the image buffer gl.glReadPixels(x, y, width, height, readbackType, - GL2.GL_UNSIGNED_BYTE, bgr); + GL.GL_UNSIGNED_BYTE, bgr); // Restore pixel storage modes psm.restore(gl); @@ -246,7 +257,7 @@ public class Screenshot { 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); + int readbackType = (alpha ? GL2.GL_ABGR_EXT : GL2GL3.GL_BGR); if (alpha) { checkExtABGR(); @@ -255,7 +266,8 @@ public class Screenshot { // Allocate necessary storage BufferedImage image = new BufferedImage(width, height, bufImgType); - GL2 gl = GLUgl2.getCurrentGL2(); + GLContext glc = GLContext.getCurrent(); + GL gl = glc.getGL(); // Set up pixel storage modes GLPixelStorageModes psm = new GLPixelStorageModes(); @@ -263,14 +275,16 @@ public class Screenshot { // read the BGR values into the image gl.glReadPixels(x, y, width, height, readbackType, - GL2.GL_UNSIGNED_BYTE, + GL.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); + if( glc.getGLDrawable().isGLOriented() ) { + // Must flip BufferedImage vertically for correct results + ImageUtil.flipImageVertically(image); + } return image; } @@ -391,9 +405,10 @@ public class Screenshot { } private static void checkExtABGR() { - GL2 gl = GLUgl2.getCurrentGL2(); - if (!gl.isExtensionAvailable("GL_EXT_abgr")) { + GL gl = GLContext.getCurrentGL(); + + 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 622ee1b79..46dc73003 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/awt/TextRenderer.java @@ -41,6 +41,7 @@ package com.jogamp.opengl.util.awt; import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.util.*; import com.jogamp.opengl.util.packrect.*; import com.jogamp.opengl.util.texture.*; @@ -127,7 +128,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 = Debug.isPropertyDefined("jogl.debug.TextRenderer", true); + } // These are occasionally useful for more in-depth debugging private static final boolean DISABLE_GLYPH_CACHE = false; @@ -153,20 +159,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; @@ -194,10 +200,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; @@ -329,9 +335,9 @@ public class TextRenderer { is made to ensure an accurate bound. */ public Rectangle2D getBounds(CharSequence str) { // FIXME: this should be more optimized and use the glyph cache - Rect r = null; + Rect r = stringLocations.get(str); - if ((r = (Rect) stringLocations.get(str)) != null) { + if (r != null) { TextData data = (TextData) r.getUserData(); // Reconstitute the Java 2D results based on the cached values @@ -701,7 +707,7 @@ 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 */ @@ -744,11 +750,12 @@ 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() { + @Override public void visit(Rect rect) { TextData data = (TextData) rect.getUserData(); @@ -760,8 +767,7 @@ public class TextRenderer { } }); - for (Iterator iter = deadRects.iterator(); iter.hasNext();) { - Rect r = (Rect) iter.next(); + for (Rect r : deadRects) { packer.remove(r); stringLocations.remove(((TextData) r.getUserData()).string()); @@ -800,9 +806,7 @@ 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(); + for (Glyph glyph : mGlyphProducer.getGlyphs(str)) { float advance = glyph.draw3D(x, y, z, scaleFactor); x += advance * scaleFactor; } @@ -824,7 +828,7 @@ 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 @@ -885,7 +889,7 @@ public class TextRenderer { data.markUsed(); 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, @@ -901,18 +905,20 @@ public class TextRenderer { private void debug(GL gl) { dbgFrame = new Frame("TextRenderer Debug Output"); - GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(gl.getGLProfile()), null, - GLContext.getCurrent(), null); + 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() { + @Override public void windowClosing(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(); } @@ -1003,12 +1009,14 @@ public class TextRenderer { 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; @@ -1017,36 +1025,43 @@ 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(); } + @Override public char setIndex(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); iter.mCurrentIndex = mCurrentIndex; @@ -1054,6 +1069,7 @@ public class TextRenderer { return iter; } + @Override public char first() { if (mLength == 0) { return CharacterIterator.DONE; @@ -1069,7 +1085,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 @@ -1080,7 +1096,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 @@ -1088,7 +1104,7 @@ 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 @@ -1137,6 +1153,7 @@ public class TextRenderer { class Manager implements BackingStoreManager { private Graphics2D g; + @Override public Object allocateBackingStore(int w, int h) { // FIXME: should consider checking Font's attributes to see // whether we're likely to need to support a full RGBA backing @@ -1159,10 +1176,12 @@ public class TextRenderer { return renderer; } + @Override public void deleteBackingStore(Object backingStore) { ((TextureRenderer) backingStore).dispose(); } + @Override public boolean preExpand(Rect cause, int attemptNumber) { // Only try this one time; clear out potentially obsolete entries // NOTE: this heuristic and the fact that it clears the used bit @@ -1198,6 +1217,7 @@ public class TextRenderer { return false; } + @Override public boolean additionFailed(Rect cause, int attemptNumber) { // Heavy hammer -- might consider doing something different packer.clear(); @@ -1216,10 +1236,12 @@ public class TextRenderer { return false; } + @Override public boolean canCompact() { return true; } + @Override public void beginMovement(Object oldBackingStore, Object newBackingStore) { // Exit the begin / end pair if necessary if (inBeginEndPair) { @@ -1253,6 +1275,7 @@ public class TextRenderer { g = newRenderer.createGraphics(); } + @Override public void move(Object oldBackingStore, Rect oldLocation, Object newBackingStore, Rect newLocation) { TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore; @@ -1274,6 +1297,7 @@ public class TextRenderer { } } + @Override public void endMovement(Object oldBackingStore, Object newBackingStore) { g.dispose(); @@ -1310,10 +1334,12 @@ public class TextRenderer { } public static class DefaultRenderDelegate implements RenderDelegate { + @Override public boolean intensityOnly() { return true; } + @Override public Rectangle2D getBounds(CharSequence str, Font font, FontRenderContext frc) { return getBounds(font.createGlyphVector(frc, @@ -1321,20 +1347,24 @@ public class TextRenderer { frc); } + @Override public Rectangle2D getBounds(String str, Font font, FontRenderContext frc) { return getBounds(font.createGlyphVector(frc, str), frc); } + @Override public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { return gv.getVisualBounds(); } + @Override public void drawGlyphVector(Graphics2D graphics, GlyphVector str, int x, int y) { graphics.drawGlyphVector(str, x, y); } + @Override public void draw(Graphics2D graphics, String str, int x, int y) { graphics.drawString(str, x, y); } @@ -1345,7 +1375,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 @@ -1467,10 +1497,10 @@ public class TextRenderer { int width = (int) origRect.getWidth(); int height = (int) origRect.getHeight(); - float tx1 = xScale * (float) texturex / (float) renderer.getWidth(); + float tx1 = xScale * texturex / renderer.getWidth(); float ty1 = yScale * (1.0f - ((float) texturey / (float) renderer.getHeight())); - float tx2 = xScale * (float) (texturex + width) / (float) renderer.getWidth(); + float tx2 = xScale * (texturex + width) / renderer.getWidth(); float ty2 = yScale * (1.0f - ((float) (texturey + height) / (float) renderer.getHeight())); @@ -1555,9 +1585,9 @@ 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>*/(); + 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 @@ -1571,10 +1601,10 @@ public class TextRenderer { clearAllCacheEntries(); } - public List/*<Glyph>*/ getGlyphs(CharSequence inString) { + public List<Glyph> getGlyphs(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); @@ -1591,7 +1621,7 @@ public class TextRenderer { int i = 0; while (i < lengthInGlyphs) { Character letter = CharacterCache.valueOf(inString.charAt(i)); - GlyphMetrics metrics = (GlyphMetrics) glyphMetricsCache.get(letter); + GlyphMetrics metrics = glyphMetricsCache.get(letter); if (metrics == null) { metrics = fullRunGlyphVector.getGlyphMetrics(i); glyphMetricsCache.put(letter, metrics); @@ -1709,7 +1739,7 @@ public class TextRenderer { return glyph; } } - + private static class CharacterCache { private CharacterCache() { } @@ -1799,7 +1829,7 @@ public class TextRenderer { GL2 gl = GLContext.getCurrentGL().getGL2(); TextureRenderer renderer = getBackingStore(); - Texture texture = renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? + renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? mVertCoords.rewind(); mTexCoords.rewind(); @@ -1842,7 +1872,7 @@ 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? + renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? GL2 gl = GLContext.getCurrentGL().getGL2(); gl.glBegin(GL2.GL_QUADS); @@ -1890,6 +1920,7 @@ public class TextRenderer { this.frame = frame; } + @Override public void display(GLAutoDrawable drawable) { GL2 gl = GLContext.getCurrentGL().getGL2(); gl.glClear(GL2.GL_DEPTH_BUFFER_BIT | GL2.GL_COLOR_BUFFER_BIT); @@ -1907,6 +1938,7 @@ public class TextRenderer { if ((frame.getWidth() != w) || (frame.getHeight() != h)) { EventQueue.invokeLater(new Runnable() { + @Override public void run() { frame.setSize(w, h); } @@ -1914,6 +1946,7 @@ public class TextRenderer { } } + @Override public void dispose(GLAutoDrawable drawable) { glu.destroy(); glu=null; @@ -1921,9 +1954,11 @@ public class TextRenderer { } // Unused methods + @Override public void init(GLAutoDrawable drawable) { } + @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } @@ -1976,7 +2011,7 @@ public class TextRenderer { private final boolean is15Available(GL gl) { if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) { - isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable("GL_VERSION_1_5"); + isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable(GLExtensions.VERSION_1_5); checkFor_isExtensionAvailable_GL_VERSION_1_5 = true; } return isExtensionAvailable_GL_VERSION_1_5; 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..26e1eb041 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. */ @@ -407,7 +407,7 @@ public class TextureRenderer { 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 @@ -437,7 +437,7 @@ 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 { @@ -459,7 +459,7 @@ 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, @@ -490,7 +490,7 @@ 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, @@ -518,7 +518,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 */ @@ -552,7 +552,7 @@ public class TextureRenderer { private void beginRendering(boolean ortho, int width, int height, boolean disableDepthTestForOrtho) { GL2 gl = GLContext.getCurrentGL().getGL2(); - int attribBits = + 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); gl.glPushAttrib(attribBits); @@ -622,7 +622,7 @@ public class TextureRenderer { // Infer the internal format if not an intensity texture int internalFormat = (intensity ? GL2.GL_INTENSITY : 0); - int imageType = + int imageType = (intensity ? BufferedImage.TYPE_BYTE_GRAY : (alpha ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_RGB)); image = new BufferedImage(width, height, imageType); 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..e8df6aaec 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 { 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..d4ee12b32 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 { 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..5e26e0d14 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,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 CoordRec { 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..42529f3f1 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. */ @@ -163,7 +163,7 @@ public class GLUT { public void glutSolidCylinder(double radius, double height, int slices, int stacks) { GL2 gl = GLUgl2.getCurrentGL2(); - + // Prepare table of points for drawing end caps double [] x = new double[slices]; double [] y = new double[slices]; @@ -174,7 +174,7 @@ public class GLUT { x[i] = Math.cos(angle) * radius; y[i] = Math.sin(angle) * radius; } - + // Draw bottom cap gl.glBegin(GL2.GL_TRIANGLE_FAN); gl.glNormal3d(0,0,-1); @@ -184,7 +184,7 @@ public class GLUT { } gl.glVertex3d(x[0], y[0], 0); gl.glEnd(); - + // Draw top cap gl.glBegin(GL2.GL_TRIANGLE_FAN); gl.glNormal3d(0,0,1); @@ -194,7 +194,7 @@ public class GLUT { } gl.glVertex3d(x[0], y[0], height); gl.glEnd(); - + // Draw walls quadObjInit(glu); glu.gluQuadricDrawStyle(quadObj, GLU.GLU_FILL); @@ -262,7 +262,7 @@ public class GLUT { /** * 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 */ @@ -278,7 +278,7 @@ 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 @@ -292,14 +292,14 @@ public class GLUT { /** * 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) { 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,7 +308,7 @@ 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 @@ -356,7 +356,7 @@ public class GLUT { int[] skiprows = new int[1]; int[] skippixels = new int[1]; int[] alignment = new int[1]; - beginBitmap(gl, + beginBitmap(gl, swapbytes, lsbfirst, rowlength, @@ -367,7 +367,7 @@ public class GLUT { for (int i = 0; i < len; i++) { bitmapCharacterImpl(gl, font, string.charAt(i)); } - endBitmap(gl, + endBitmap(gl, swapbytes, lsbfirst, rowlength, @@ -502,7 +502,7 @@ public class GLUT { gl.glEnd( ); } } - + /** This function draws a solid-shaded dodecahedron whose facets are rhombic and @@ -522,7 +522,7 @@ public class GLUT { } gl.glEnd( ); } - + //---------------------------------------------------------------------- // Internals only below this point // @@ -879,7 +879,7 @@ public class GLUT { } /* rhombic dodecahedron data: */ - + private static final double rdod_r[][] = { { 0.0, 0.0, 1.0 }, @@ -897,7 +897,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 +913,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 +929,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; @@ -1124,7 +1124,7 @@ public class GLUT { float[] r = new float[4*4*3]; 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); @@ -1183,7 +1183,7 @@ public class GLUT { gl.glPopMatrix(); gl.glPopAttrib(); } - + private static void evaluateTeapotMesh(GL2 gl, int grid, int type, 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..3587ca992 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. */ 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..cf51ddd3c 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. */ 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..515212f0e 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 { 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..5335c8523 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 { 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..b0c91c696 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,14 +41,14 @@ 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) { this.num_coords = num_coords; 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..6a64edeb5 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -45,13 +45,16 @@ import java.util.Set; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL3; import javax.media.opengl.GLES2; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import jogamp.opengl.Debug; 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,33 +62,38 @@ 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> */ 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> */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; - + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>bvp</code> */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; - + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>gp</code> */ + public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>bgp</code> */ + public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>fp</code> */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; - + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>bfp</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> */ 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} or {@link GL3#GL_GEOMETRY_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) { @@ -95,6 +103,7 @@ public class ShaderCode { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -113,14 +122,15 @@ 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} or {@link GL3#GL_GEOMETRY_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) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -136,19 +146,19 @@ 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} or {@link GL3#GL_GEOMETRY_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) */ @@ -181,16 +191,16 @@ 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} or {@link GL3#GL_GEOMETRY_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) { ByteBuffer shaderBinary = null; if(null!=binaryFile && 0<=binFormat) { @@ -214,16 +224,18 @@ 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></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></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} or {@link GL3#GL_GEOMETRY_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) { @@ -232,21 +244,23 @@ public class ShaderCode { 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; 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) { switch (binFormat) { case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV: @@ -257,42 +271,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,14 +317,14 @@ 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} or {@link GL3#GL_GEOMETRY_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> @@ -318,22 +332,22 @@ public class ShaderCode { * @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, + */ + public static ShaderCode create(GL2ES2 gl, int type, int count, Class<?> context, String srcRoot, String[] srcBasenames, String binRoot, String binBasename, boolean mutableStringBuilder) { ShaderCode res = null; @@ -376,28 +390,28 @@ public class ShaderCode { /** * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String)}. * <br> - * + * * 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,10 +421,10 @@ public class ShaderCode { * sp0.add(gl, fp0, System.err); * st.attachShaderProgram(gl, sp0, true); * </pre> - * + * * @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} or {@link GL3#GL_GEOMETRY_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 binRoot relative <i>root</i> path for <code>basename</code> @@ -418,20 +432,20 @@ public class ShaderCode { * @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, + */ + 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 ); + return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); } - + /** * returns the uniq shader id as an integer */ @@ -440,12 +454,14 @@ public class ShaderCode { public int shaderType() { return shaderType; } public String shaderTypeStr() { return shaderTypeStr(shaderType); } - public static String shaderTypeStr(int type) { + public static String shaderTypeStr(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"; } return "UNKNOWN_SHADER"; } @@ -467,6 +483,7 @@ public class ShaderCode { // 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, @@ -497,6 +514,7 @@ public class ShaderCode { id=-1; } + @Override public boolean equals(Object obj) { if(this==obj) { return true; } if(obj instanceof ShaderCode) { @@ -504,9 +522,11 @@ public class ShaderCode { } return false; } + @Override public int hashCode() { return id; } + @Override public String toString() { StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); for(int i=0; i<shader.remaining(); i++) { @@ -536,7 +556,7 @@ public class ShaderCode { } else { 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())); @@ -552,19 +572,19 @@ public class ShaderCode { 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) { @@ -578,7 +598,7 @@ public class ShaderCode { final int sourceCount = (null!=shaderSource)?shaderSource.length:0; 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 +606,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,15 +633,15 @@ 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) { @@ -658,18 +678,18 @@ public class ShaderCode { } return num; } - + /** * Adds <code>data</code> at <code>offset</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 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) { @@ -683,7 +703,7 @@ public class ShaderCode { final int sourceCount = (null!=shaderSource)?shaderSource.length:0; 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,8 +711,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(position < curEndIndex) { + curEndIndex += sb.length(); + if(position < curEndIndex) { sb.insert(position, data); return position+data.length(); } @@ -700,6 +720,7 @@ public class ShaderCode { return -1; } + @SuppressWarnings("resource") private static int readShaderSource(Class<?> context, URLConnection conn, StringBuilder result, int lineno) throws IOException { if(DEBUG_CODE) { if(0 == lineno) { @@ -716,12 +737,12 @@ public class ShaderCode { if (line.startsWith("#include ")) { 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 "); if (nextConn == null) { // Try relative of class and absolute - nextConn = IOUtil.getResource(context, includeFile); + nextConn = IOUtil.getResource(context, includeFile); } if (nextConn == null) { // Fail @@ -739,7 +760,7 @@ public class ShaderCode { } /** - * + * * @param context * @param conn * @param result @@ -748,7 +769,7 @@ public class ShaderCode { public static void readShaderSource(Class<?> context, URLConnection conn, 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,21 +777,21 @@ 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); + URLConnection conn = IOUtil.getResource(context, path); if (conn == null) { return null; } @@ -780,17 +801,17 @@ public class ShaderCode { } /** - * 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 { @@ -806,6 +827,186 @@ 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} */ + public static final String es3_default_precision_fp = es2_default_precision_fp; + + /** 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(String extensionName, String behavior) { + return "#extension " + extensionName + " : " + behavior; + } + + /** + * 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(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(GL2ES2 gl, int pos) { + final String defaultPrecision; + 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( 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( 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: + 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(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(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(GL2ES2 gl, boolean preludeVersion, 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(GL2ES2 gl, boolean preludeVersion, 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..b289b41e2 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,6 +50,7 @@ public class ShaderProgram { return programInUse; } + /** Returns the shader program name, which is non zero if valid. */ public int program() { return shaderProgram; } /** @@ -84,7 +85,9 @@ public class ShaderProgram { * If <code>destroyShaderCode</code> is true it destroys the shader codes as well. */ public synchronized void release(GL2ES2 gl, boolean destroyShaderCode) { - useProgram(gl, false); + if( programLinked ) { + useProgram(gl, false); + } for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { ShaderCode shaderCode = iter.next(); if(attachedShaderCode.remove(shaderCode)) { @@ -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,7 +111,7 @@ 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> */ @@ -119,7 +122,7 @@ public class ShaderProgram { public synchronized boolean contains(ShaderCode shaderCode) { return allShaderCode.contains(shaderCode); } - + /** * Warning slow O(n) operation .. * @param id @@ -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(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); + 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 @@ -190,31 +196,29 @@ public class ShaderProgram { * @see ShaderState#glResetAllVertexAttributes */ public synchronized boolean replaceShader(GL2ES2 gl, ShaderCode oldShader, ShaderCode newShader, PrintStream verboseOut) { - init(gl); - - if(!newShader.compile(gl, verboseOut)) { + if(!init(gl) || !newShader.compile(gl, verboseOut)) { return false; } - + 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); + if( !init(gl) ) { + programLinked = false; // mark unlinked due to user attempt to [re]link + return false; + } for(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,11 +258,12 @@ public class ShaderProgram { // Link the program gl.glLinkProgram(shaderProgram); - programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, System.err); + programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut); return programLinked; } + @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof ShaderProgram) { @@ -263,6 +272,7 @@ public class ShaderProgram { return false; } + @Override public int hashCode() { return id; } @@ -279,7 +289,8 @@ public class ShaderProgram { sb.append("]"); return sb; } - + + @Override public String toString() { return toString(null).toString(); } @@ -291,17 +302,20 @@ public class ShaderProgram { public synchronized boolean validateProgram(GL2ES2 gl, PrintStream verboseOut) { return ShaderUtil.isProgramExecStatusValid(gl, shaderProgram, verboseOut); } - + public synchronized void useProgram(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; } protected boolean programLinked = false; protected boolean programInUse = false; - protected int shaderProgram=-1; + protected int shaderProgram = 0; // non zero is valid! protected HashSet<ShaderCode> allShaderCode = new HashSet<ShaderCode>(); protected HashSet<ShaderCode> attachedShaderCode = new HashSet<ShaderCode>(); protected int id = -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..f60cb6088 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java @@ -35,66 +35,40 @@ 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.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 = Debug.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(boolean v) { verbose = DEBUG || v; } /** * Returns the attached user object for the given name to this ShaderState. @@ -106,7 +80,7 @@ public class ShaderState { /** * 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) { @@ -115,54 +89,30 @@ public class ShaderState { /** * @param name name of the mapped object to detach - * + * * @return the previous mapped object or null if none */ public final Object detachObject(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"); } + 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 +124,7 @@ public class ShaderState { resetAllUniforms(gl); } } - resetAllShaderData = false; + resetAllShaderData = false; } else { shaderProgram.useProgram(gl, false); } @@ -191,34 +141,38 @@ 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) { + public synchronized boolean attachShaderProgram(GL2ES2 gl, ShaderProgram prog, boolean enable) throws GLException { + if(verbose) { 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(); + 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) { @@ -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; } @@ -261,8 +216,7 @@ public class ShaderState { */ public synchronized void destroy(GL2ES2 gl) { release(gl, true, true, true); - attachedObjectsByString.clear(); - attachedObjectsByInt.clear(); + attachedObjectsByString.clear(); } /** @@ -282,13 +236,13 @@ public class ShaderState { * @see ShaderProgram#release(GL2ES2, boolean) */ public synchronized void release(GL2ES2 gl, boolean destroyBoundAttributes, boolean destroyShaderProgram, boolean destroyShaderCode) { - if(null!=shaderProgram) { + if(null!=shaderProgram && shaderProgram.linked() ) { shaderProgram.useProgram(gl, false); } if(destroyBoundAttributes) { for(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) @@ -316,7 +270,7 @@ public class ShaderState { Integer idx = activeAttribLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } - + /** * Get the previous cached vertex attribute data. * @@ -335,23 +289,30 @@ public class ShaderState { public GLArrayData getAttribute(String name) { return activeAttribDataMap.get(name); } - + + public boolean isActiveAttribute(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) { if(own) { @@ -363,12 +324,13 @@ public class ShaderState { } else { managedAttributes.remove(attribute); } + attribute.associate(this, own); } - + public boolean ownsAttribute(GLArrayData attribute) { return managedAttributes.contains(attribute); } - + /** * Binds a shader attribute to a location. * Multiple names can be bound to one location. @@ -377,14 +339,14 @@ 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) { if(null==shaderProgram) throw new GLException("No program is attached"); - if(shaderProgram.linked()) throw new GLException("Program is already linked"); + if(shaderProgram.linked()) throw new GLException("Program is already linked"); final Integer loc = new Integer(location); activeAttribLocationMap.put(name, loc); gl.glBindAttribLocation(shaderProgram.program(), location, name); @@ -399,25 +361,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); + 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, new Integer(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. @@ -434,14 +399,15 @@ public class ShaderState { 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, new Integer(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 +415,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. * @@ -467,12 +433,31 @@ public class ShaderState { * @see #getAttribute(String) */ public int getAttribLocation(GL2ES2 gl, GLArrayData data) { - int location = getAttribLocation(gl, data.getName()); - data.setLocation(location); + 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) { + Integer idx = new Integer(location); + activeAttribLocationMap.put(name, idx); + 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 // @@ -484,38 +469,40 @@ public class ShaderState { 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) { return isVertexAttribArrayEnabled(data.getName()); } - + private boolean enableVertexAttribArray(GL2ES2 gl, 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,7 +510,7 @@ 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 @@ -532,7 +519,7 @@ public class ShaderState { public boolean enableVertexAttribArray(GL2ES2 gl, String name) { return enableVertexAttribArray(gl, name, -1); } - + /** * Enables a vertex attribute array, usually invoked by {@link GLArrayDataEditable#enableBuffer(GL, boolean)}. @@ -541,7 +528,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. * @@ -560,36 +547,38 @@ public class ShaderState { 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) { 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. * @@ -614,7 +603,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. * @@ -634,19 +623,20 @@ public class ShaderState { } 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 @@ -656,11 +646,11 @@ public class ShaderState { 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; @@ -693,16 +683,16 @@ public class ShaderState { 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 @@ -727,34 +717,45 @@ public class ShaderState { } private final void relocateAttribute(GL2ES2 gl, GLArrayData attribute) { - // get new location .. + // 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, new Integer(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 * @@ -763,9 +764,9 @@ public class ShaderState { private final void resetAllAttributes(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++) { + ((GLArrayData)managedAttributes.get(i)).setLocation(-1); } for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { relocateAttribute(gl, iter.next()); @@ -778,21 +779,23 @@ public class ShaderState { 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) */ @@ -809,7 +812,7 @@ 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) { @@ -819,38 +822,38 @@ public class ShaderState { /** * 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) { 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) { 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, @@ -867,13 +870,16 @@ public class ShaderState { 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); } 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 +889,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, @@ -900,26 +906,36 @@ public class ShaderState { * @see ShaderProgram#glReplaceShader */ public int getUniformLocation(GL2ES2 gl, GLUniformData data) { - int location = getUniformLocation(gl, data.getName()); - data.setLocation(location); - activeUniformDataMap.put(data.getName(), 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, new Integer(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 @@ -936,11 +952,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; } /** @@ -961,37 +978,49 @@ public class ShaderState { 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"); + if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); activeUniformLocationMap.clear(); for(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); + 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(), new Integer(loc)); + if(DEBUG) { + System.err.println("ShaderState: resetAllUniforms: "+data); + } + gl.glUniform(data); + } } } - public StringBuilder toString(StringBuilder sb) { + public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) { if(null==sb) { sb = new StringBuilder(); } - + sb.append("ShaderState[ "); - + sb.append(Platform.getNewline()).append(" "); if(null != shaderProgram) { shaderProgram.toString(sb); @@ -1008,43 +1037,54 @@ public class ShaderState { } sb.append(Platform.getNewline()).append(" ],").append(" activeAttributes ["); for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + 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()); + 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()); + 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()); + 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 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..5cd384c58 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,6 +40,7 @@ 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) { @@ -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> @@ -149,7 +150,7 @@ 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)}. */ @@ -159,15 +160,21 @@ public class ShaderUtil { 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) { + 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])); + } } + } catch (GLException gle) { + System.err.println("Catched Exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); } } } @@ -180,17 +187,28 @@ public class ShaderUtil { 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 = new Boolean(v); + queryOK = true; + } catch (GLException gle) { + System.err.println("Catched Exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); + } + if(!queryOK) { + info.shaderCompilerAvailable = new Boolean(true); } - info.shaderCompilerAvailable = new Boolean(v); } else if( gl.isGL2ES2() ) { info.shaderCompilerAvailable = new Boolean(true); } else { @@ -200,6 +218,13 @@ public class ShaderUtil { return info.shaderCompilerAvailable.booleanValue(); } + /** Returns true if GeometryShader is supported, i.e. whether GLContext is ≥ 3.2 or ARB_geometry_shader4 extension is available. */ + public static boolean isGeometryShaderSupported(GL _gl) { + final GLContext ctx = _gl.getContext(); + return ctx.getGLVersionNumber().compareTo(GLContext.Version320) >= 0 || + ctx.isExtensionAvailable(GLExtensions.ARB_geometry_shader4); + } + public static void shaderSource(GL _gl, int shader, CharSequence[] source) { final GL2ES2 gl = _gl.getGL2ES2(); @@ -215,7 +240,7 @@ public class ShaderUtil { 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(); } } @@ -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)); } @@ -328,7 +353,7 @@ public class ShaderUtil { } public static boolean createAndCompileShader(GL _gl, IntBuffer shader, int shaderType, - CharSequence[][] sources, + CharSequence[][] sources, PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); @@ -338,32 +363,32 @@ 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) { final GLContext context = gl.getContext(); 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..2f8884a3a 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(GL gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix, boolean force, 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(GL gl, ShaderSelectionMode mode, 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 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..44fbf1c6d 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 @@ -55,8 +55,8 @@ public abstract class CompileShader { URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); 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); } @@ -137,7 +137,7 @@ public abstract class CompileShader { } String dirname; if (lastSlash < 0) { - dirname = new String(); + dirname = ""; } else { dirname = path.substring(0, lastSlash + 1); } @@ -161,6 +161,7 @@ public abstract class CompileShader { new Thread(this, "Output Reader Thread").start(); } + @Override public void run() { byte[] buffer = new byte[4096]; 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..215cf592b 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 @@ -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,10 +41,12 @@ public class CompileShaderNVidia extends CompileShader { return compilerDir; } + @Override public String getVertexShaderCompiler() { return "glslv.bat"; } + @Override public String getFragmentShaderCompiler() { return "glslf.bat"; } 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..9aadfba93 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. */ @@ -47,22 +47,22 @@ public class Level { private int yPos; private LevelSet holder; - private List/*<Rect>*/ rects = new ArrayList/*<Rect>*/(); - private List/*<Rect>*/ freeList; + private 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(Rect r1, Rect r2) { return r1.x() - r2.x(); } + @Override public boolean equals(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) { this.width = 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 (Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + 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; @@ -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(); @@ -171,8 +171,8 @@ public class Level { if (freeList == null) return false; int freeListWidth = 0; - for (Iterator iter = freeList.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + Rect cur = iter.next(); freeListWidth += cur.w(); } // Add on the remaining space at the end @@ -184,8 +184,8 @@ public class Level { Collections.sort(rects, rectXComparator); int nextCompactionDest = 0; manager.beginMovement(backingStore, backingStore); - for (Iterator iter = rects.iterator(); iter.hasNext(); ) { - Rect cur = (Rect) iter.next(); + for (Iterator<Rect> iter = rects.iterator(); iter.hasNext(); ) { + 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(); + for (Iterator<Rect> iter = rects.iterator(); iter.hasNext(); ) { + Rect rect = iter.next(); visitor.visit(rect); } } @@ -216,7 +216,7 @@ public class Level { original Rects. */ public void updateRectangleReferences() { for (int i = 0; i < rects.size(); i++) { - Rect cur = (Rect) rects.get(i); + Rect cur = rects.get(i); Rect next = cur.getNextLocation(); next.setPosition(cur.x(), cur.y()); if (cur.w() != next.w() || cur.h() != next.h()) @@ -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); + Rect r1 = freeList.get(i); + 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); + 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 (Iterator<Rect> iter = freeList.iterator(); iter.hasNext(); ) { + 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..433421f1a 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,7 +47,7 @@ import java.util.*; public class LevelSet { // Maintained in sorted order by increasing Y coordinate - private List/*<Level>*/ levels = new ArrayList/*<Level>*/(); + private List<Level> levels = new ArrayList<Level>(); private int nextAddY; private int w; private int h; @@ -73,7 +73,7 @@ public class LevelSet { // 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); + 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); + Level level = levels.get(i); if (level.couldAllocateIfCompacted(rect)) return false; } @@ -104,11 +104,11 @@ public class LevelSet { /** Removes the given Rect from this LevelSet. */ public boolean remove(Rect rect) { for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + Level level = levels.get(i); if (level.remove(rect)) return true; } - + return false; } @@ -120,7 +120,7 @@ public class LevelSet { Object backingStore, BackingStoreManager manager) { for (int i = levels.size() - 1; i >= 0; --i) { - Level level = (Level) levels.get(i); + Level level = levels.get(i); if (level.couldAllocateIfCompacted(rect)) { level.compact(backingStore, manager); boolean res = level.add(rect); @@ -173,8 +173,8 @@ public class LevelSet { int usedHeight = getUsedHeight(); if (usedHeight == 0) return 0.0f; - for (Iterator iter = iterator(); iter.hasNext(); ) { - Level level = (Level) iter.next(); + for (Iterator<Level> iter = iterator(); iter.hasNext(); ) { + 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(); + for (Iterator<Level> iter = levels.iterator(); iter.hasNext(); ) { + 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 (Iterator<Level> iter = levels.iterator(); iter.hasNext(); ) { + 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..89f594230 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,7 +72,7 @@ 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); } @@ -144,6 +144,7 @@ public class Rect { 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..44faa44b0 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. */ @@ -60,18 +60,18 @@ public class RectanglePacker { 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(Rect r1, Rect r2) { return r2.h() - r1.h(); } + @Override public boolean equals(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, @@ -203,13 +203,13 @@ 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(); + List<Rect> newRects = new ArrayList<Rect>(); + for (Iterator<Level> i1 = levels.iterator(); i1.hasNext(); ) { + Level level = i1.next(); + for (Iterator<Rect> i2 = level.iterator(); i2.hasNext(); ) { + Rect cur = i2.next(); Rect newRect = new Rect(0, 0, cur.w(), cur.h(), null); cur.setNextLocation(newRect); // Hook up the reverse mapping too for easier replacement @@ -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 (Iterator<Rect> iter = newRects.iterator(); iter.hasNext(); ) { + if (!nextLevelSet.add(iter.next())) { done = false; break; } @@ -271,10 +271,10 @@ public class RectanglePacker { 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 (Iterator<Level> i1 = levels.iterator(); i1.hasNext(); ) { + Level level = i1.next(); + for (Iterator<Rect> i2 = level.iterator(); i2.hasNext(); ) { + Rect cur = i2.next(); manager.move(backingStore, cur, newBackingStore, cur.getNextLocation()); } 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 a94b1f827..584cacf8c 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. @@ -44,24 +45,39 @@ import javax.media.nativewindow.NativeWindowFactory; import jogamp.opengl.*; +import com.jogamp.opengl.GLExtensions; 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 @@ -89,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); @@ -113,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 @@ -136,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. */ @@ -166,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; @@ -205,12 +234,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,21 +252,22 @@ public class Texture { * OpenGL-related errors occurred */ public void enable(GL gl) throws GLException { - if(GLES2.GL_TEXTURE_EXTERNAL_OES != target) { + 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,11 +279,11 @@ public class Texture { * OpenGL-related errors occurred */ public void disable(GL gl) throws GLException { - if(GLES2.GL_TEXTURE_EXTERNAL_OES != target) { + 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: @@ -262,22 +293,16 @@ 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 TODO * + * @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 { 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. * @@ -311,7 +336,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 @@ -321,9 +346,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 @@ -365,7 +390,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() { @@ -382,7 +407,7 @@ 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) { @@ -407,9 +432,9 @@ 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 { @@ -429,10 +454,24 @@ 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(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 { @@ -448,12 +487,12 @@ public class Texture { // See whether we have automatic mipmap generation support boolean haveAutoMipmapGeneration = - (gl.isExtensionAvailable("GL_VERSION_1_4") || - gl.isExtensionAvailable("GL_SGIS_generate_mipmap")); + (gl.isExtensionAvailable(GLExtensions.VERSION_1_4) || + gl.isExtensionAvailable(GLExtensions.SGIS_generate_mipmap)); // Indicate to the TextureData what functionality is available - data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr")); - data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2")); + data.setHaveEXTABGR(gl.isExtensionAvailable(GLExtensions.EXT_abgr)); + data.setHaveGL12(gl.isExtensionAvailable(GLExtensions.VERSION_1_2)); // Indicates whether both width and height are power of two boolean isPOT = isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight); @@ -646,7 +685,7 @@ public class Texture { int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR); int magFilter = GL.GL_LINEAR; - int wrapMode = (gl.isExtensionAvailable("GL_VERSION_1_2") || !gl.isGL2()) ? GL.GL_CLAMP_TO_EDGE : GL2.GL_CLAMP; + 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) { @@ -753,7 +792,7 @@ 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 */ @@ -767,7 +806,7 @@ 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, @@ -780,7 +819,7 @@ 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, @@ -796,7 +835,7 @@ 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, @@ -809,7 +848,7 @@ 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, @@ -822,7 +861,7 @@ 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, @@ -832,15 +871,34 @@ public class Texture { } /** - * 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) { 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 @@ -899,6 +957,9 @@ public class Texture { private void setImageSize(int width, int height, 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); @@ -907,12 +968,17 @@ public class Texture { } } else { if (mustFlipVertically) { - coords = new TextureCoords(0, (float) imgHeight / (float) texHeight, - (float) imgWidth / (float) texWidth, 0); + coords = new TextureCoords(0, // l + (float) imgHeight / (float) texHeight, // b + (float) imgWidth / (float) texWidth, // r + 0 // t + ); } else { - coords = new TextureCoords(0, 0, - (float) imgWidth / (float) texWidth, - (float) imgHeight / (float) texHeight); + coords = new TextureCoords(0, // l + 0, // b + (float) imgWidth / (float) texWidth, // r + (float) imgHeight / (float) texHeight // t + ); } } } @@ -920,8 +986,8 @@ public class Texture { private void updateSubImageImpl(GL gl, TextureData data, int newTarget, int mipmapLevel, int dstx, int dsty, int srcx, int srcy, int width, int height) throws GLException { - data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr")); - data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2")); + data.setHaveEXTABGR(gl.isExtensionAvailable(GLExtensions.EXT_abgr)); + data.setHaveGL12(gl.isExtensionAvailable(GLExtensions.VERSION_1_2)); Buffer buffer = data.getBuffer(); if (buffer == null && data.getMipmapData() == null) { @@ -1039,8 +1105,8 @@ public class Texture { case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc") && - !gl.isExtensionAvailable("GL_NV_texture_compression_vtc")) { + if (!gl.isExtensionAvailable(GLExtensions.EXT_texture_compression_s3tc) && + !gl.isExtensionAvailable(GLExtensions.NV_texture_compression_vtc)) { throw new GLException("DXTn compressed textures not supported by this graphics card"); } break; @@ -1076,14 +1142,14 @@ public class Texture { private static boolean haveTexRect(GL gl) { return (!disableTexRect && TextureIO.isTexRectEnabled() && - gl.isExtensionAvailable("GL_ARB_texture_rectangle")); + gl.isExtensionAvailable(GLExtensions.ARB_texture_rectangle)); } private static boolean preferTexRect(GL gl) { // Prefer GL_ARB_texture_rectangle on ATI hardware on Mac OS X // due to software fallbacks - if (NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) { + if (NativeWindowFactory.TYPE_MACOSX == NativeWindowFactory.getNativeWindowType(false)) { 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..ba59f89c5 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. @@ -76,7 +77,7 @@ public class TextureCoords { 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 928f91ce5..5d88a76c0 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. @@ -41,6 +42,7 @@ import java.nio.Buffer; import javax.media.opengl.GLProfile; 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 +56,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 +81,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,7 +123,7 @@ public class TextureData { * data were invalid, such as requesting mipmap generation for a * compressed texture */ - public TextureData(GLProfile glp, + public TextureData(GLProfile glp, int internalFormat, int width, int height, @@ -130,6 +135,60 @@ public class TextureData { boolean mustFlipVertically, Buffer buffer, 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(GLProfile glp, + int internalFormat, + int width, + int height, + int border, + GLPixelAttributes pixelAttributes, + boolean mipmap, + boolean dataIsCompressed, + boolean mustFlipVertically, + Buffer buffer, + Flusher flusher) throws IllegalArgumentException { if (mipmap && dataIsCompressed) { throw new IllegalArgumentException("Can not generate mipmaps for compressed textures"); } @@ -138,8 +197,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 +208,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 @@ -200,12 +258,63 @@ public class TextureData { boolean mustFlipVertically, Buffer[] mipmapData, 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(GLProfile glp, + int internalFormat, + int width, + int height, + int border, + GLPixelAttributes pixelAttributes, + boolean dataIsCompressed, + boolean mustFlipVertically, + Buffer[] mipmapData, + 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; @@ -217,41 +326,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(ColorSpace cs) { pixelCS = cs; } + /** Used only by subclasses */ - protected TextureData(GLProfile glp) { this.glProfile = glp; } + protected TextureData(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 pixel format of the texture data. */ + /** 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 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,18 +384,18 @@ 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. */ @@ -280,9 +405,29 @@ public class TextureData { /** Sets the border in pixels of the texture data. */ public void setBorder(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(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(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(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; } /** Sets whether mipmaps should be generated for the texture data. */ @@ -354,8 +499,10 @@ public class TextureData { public void flush(); } + @Override public String toString() { - return "TextureData["+width+"x"+height+", 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; } //---------------------------------------------------------------------- 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 d72210016..0f64fd007 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,11 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.util.IOUtil; +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; @@ -155,6 +163,14 @@ public class TextureIO { file. */ public static final String TIFF = "tiff"; + /** 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 @@ -410,7 +426,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. * @@ -423,7 +439,7 @@ public class TextureIO { return newTexture(GLContext.getCurrentGL(), data); } - /** + /** * Creates an OpenGL texture object from the specified TextureData * using the given OpenGL context. * @@ -438,8 +454,8 @@ public class TextureIO { } return new Texture(gl, data); } - - /** + + /** * Creates an OpenGL texture object from the specified file using * the current OpenGL context. * @@ -463,7 +479,7 @@ public class TextureIO { return texture; } - /** + /** * Creates an OpenGL texture object from the specified stream using * the current OpenGL context. * @@ -492,7 +508,7 @@ public class TextureIO { return texture; } - /** + /** * Creates an OpenGL texture object from the specified URL using the * current OpenGL context. * @@ -524,13 +540,13 @@ public class TextureIO { 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) { @@ -545,7 +561,7 @@ public class TextureIO { * undefined results. * * @param textureID the OpenGL texture object to wrap - * @param target the OpenGL texture target, eg GL.GL_TEXTURE_2D, + * @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 @@ -678,7 +694,7 @@ public class TextureIO { gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, packSkipRows); gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, packSkipPixels); gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, packSwapBytes); - + data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE, false, false, false, res, null); @@ -690,7 +706,7 @@ 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(); @@ -701,13 +717,17 @@ 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. */ + /** + * 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(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 @@ -715,8 +735,12 @@ public class TextureIO { textureProviders.add(0, provider); } - /** Adds a TextureWriter to support writing of a new file - format. */ + /** + * 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(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 @@ -779,6 +803,7 @@ 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 @@ -894,6 +919,7 @@ public class TextureIO { //---------------------------------------------------------------------- // DDS provider -- supports files only for now static class DDSTextureProvider implements TextureProvider { + @Override public TextureData newTextureData(GLProfile glp, File file, int internalFormat, int pixelFormat, @@ -908,6 +934,7 @@ public class TextureIO { return null; } + @Override public TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, @@ -924,6 +951,7 @@ public class TextureIO { return null; } + @Override public TextureData newTextureData(GLProfile glp, URL url, int internalFormat, int pixelFormat, @@ -979,6 +1007,7 @@ public class TextureIO { } } TextureData.Flusher flusher = new TextureData.Flusher() { + @Override public void flush() { image.close(); } @@ -1022,6 +1051,7 @@ public class TextureIO { //---------------------------------------------------------------------- // Base class for SGI RGB and TGA image providers static abstract class StreamBasedTextureProvider implements TextureProvider { + @Override public TextureData newTextureData(GLProfile glp, File file, int internalFormat, int pixelFormat, @@ -1042,6 +1072,7 @@ public class TextureIO { } } + @Override public TextureData newTextureData(GLProfile glp, URL url, int internalFormat, int pixelFormat, @@ -1059,6 +1090,7 @@ public class TextureIO { //---------------------------------------------------------------------- // SGI RGB image provider static class SGITextureProvider extends StreamBasedTextureProvider { + @Override public TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, @@ -1094,6 +1126,7 @@ public class TextureIO { //---------------------------------------------------------------------- // TGA (Targa) image provider static class TGATextureProvider extends StreamBasedTextureProvider { + @Override public TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, @@ -1105,8 +1138,8 @@ public class TextureIO { pixelFormat = image.getGLFormat(); } if (internalFormat == 0) { - if(glp.isGL2GL3()) { - internalFormat = GL.GL_RGBA8; + if(glp.isGL2ES3()) { + internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8; } else { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB; } @@ -1131,19 +1164,60 @@ public class TextureIO { //---------------------------------------------------------------------- // PNG image provider static class PNGTextureProvider extends StreamBasedTextureProvider { + @Override public TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, boolean mipmap, 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(GLProfile glp, InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (JPG.equals(fileSuffix)) { + JPEGImage image = JPEGImage.read(/*glp, */ stream); if (pixelFormat == 0) { pixelFormat = image.getGLFormat(); } if (internalFormat == 0) { - if(glp.isGL2GL3()) { - internalFormat = GL.GL_RGBA8; + if(glp.isGL2ES3()) { + internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA8:GL.GL_RGB8; } else { internalFormat = (image.getBytesPerPixel()==4)?GL.GL_RGBA:GL.GL_RGB; } @@ -1153,7 +1227,7 @@ public class TextureIO { image.getHeight(), 0, pixelFormat, - GL.GL_UNSIGNED_BYTE, + image.getGLType(), mipmap, false, false, @@ -1169,12 +1243,14 @@ public class TextureIO { // DDS texture writer // static class DDSTextureWriter implements TextureWriter { + @Override public boolean write(File file, 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"); @@ -1183,15 +1259,15 @@ public class TextureIO { int d3dFormat = 0; // FIXME: some of these are probably not completely correct and would require swizzling switch (pixelFormat) { - case GL.GL_RGB: d3dFormat = DDSImage.D3DFMT_R8G8B8; break; - case GL.GL_RGBA: d3dFormat = DDSImage.D3DFMT_A8R8G8B8; break; - case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: d3dFormat = DDSImage.D3DFMT_DXT1; break; - case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: throw new IOException("RGBA DXT1 not yet supported"); - case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: d3dFormat = DDSImage.D3DFMT_DXT3; break; - 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"); + case GL.GL_RGB: d3dFormat = DDSImage.D3DFMT_R8G8B8; break; + case GL.GL_RGBA: d3dFormat = DDSImage.D3DFMT_A8R8G8B8; break; + case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: d3dFormat = DDSImage.D3DFMT_DXT1; break; + case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: throw new IOException("RGBA DXT1 not yet supported"); + case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: d3dFormat = DDSImage.D3DFMT_DXT3; break; + 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]; @@ -1218,14 +1294,16 @@ public class TextureIO { // SGI (rgb) texture writer // static class SGITextureWriter implements TextureWriter { + @Override public boolean write(File file, TextureData data) throws IOException { 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 || @@ -1260,28 +1338,30 @@ public class TextureIO { //---------------------------------------------------------------------- // TGA (Targa) texture writer - + static class TGATextureWriter implements TextureWriter { + @Override public boolean write(File file, 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 == GL.GL_RGBA || pixelFormat == GL2.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); for (int i = 0; i < buf.remaining(); i += skip) { @@ -1304,64 +1384,60 @@ public class TextureIO { } return false; - } + } } //---------------------------------------------------------------------- // PNG texture writer - + static class PNGTextureWriter implements TextureWriter { + @Override public boolean write(File file, 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_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; - } - if ( 1 < bytesPerPixel && - (pixelType == GL.GL_BYTE || - pixelType == GL.GL_UNSIGNED_BYTE)) { - - ByteBuffer buf = (ByteBuffer) data.getBuffer(); - if (null == buf) { - buf = (ByteBuffer) data.getMipmapData()[0]; + 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)) { + 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 // 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 5ee2104a3..ab9d1f8f0 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,41 +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 GL_OES_EGL_image_external = "GL_OES_EGL_image_external"; 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 static class TextureFrame extends TimeFrameI { + public TextureFrame(Texture t, int pts, int duration) { + super(pts, duration); + texture = t; + } public TextureFrame(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(); @@ -143,59 +177,59 @@ public interface TextureSequence { * 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> @@ -208,14 +242,14 @@ 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 ; -}
\ No newline at end of file + public String getTextureLookupFragmentShaderImpl() throws IllegalStateException ; +} 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..d8320c690 --- /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(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 GL2GL3.GL_TEXTURE_2D_ARRAY: texBindQName = GL2GL3.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(int i) { return "0x"+Integer.toHexString(i); } + + private static final int activeTexture(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(GL gl, 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(GL gl, int textureUnit, 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(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..202c08e4e 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,7 +113,7 @@ public class AWTTextureData extends TextureData { * texture * @param image the image containing the texture data */ - public AWTTextureData(GLProfile glp, + public AWTTextureData(GLProfile glp, int internalFormat, int pixelFormat, boolean mipmap, @@ -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); @@ -150,7 +177,7 @@ public class AWTTextureData extends TextureData { } private void createFromImage(GLProfile glp, BufferedImage image) { - pixelType = 0; // Determine from image + pixelAttributes = GLPixelAttributes.UNDEF; // Determine from image mustFlipVertically = true; width = image.getWidth(); @@ -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(); + 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(); + 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 { @@ -375,6 +385,8 @@ public class AWTTextureData extends TextureData { private void setupLazyCustomConversion(BufferedImage image) { imageForLazyCustomConversion = image; boolean hasAlpha = image.getColorModel().hasAlpha(); + int pixelFormat = pixelAttributes.format; + int pixelType = pixelAttributes.type; if (pixelFormat == 0) { pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; } @@ -401,6 +413,7 @@ public class AWTTextureData extends TextureData { } else { throw new RuntimeException("Unexpected DataBuffer type?"); } + pixelAttributes = new GLPixelAttributes(pixelFormat, pixelType); } private void createFromCustom(BufferedImage image) { @@ -409,7 +422,7 @@ public class AWTTextureData extends TextureData { // create a temporary image that is compatible with OpenGL boolean hasAlpha = image.getColorModel().hasAlpha(); - ColorModel cm = null; + 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,12 +432,12 @@ 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); @@ -454,13 +467,12 @@ public class AWTTextureData extends TextureData { 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); } 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..c70f5d0f3 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. */ @@ -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. * @@ -119,7 +119,7 @@ public class AWTTextureIO extends TextureIO { return texture; } - private static TextureData newTextureDataImpl(GLProfile glp, + private static TextureData newTextureDataImpl(GLProfile glp, BufferedImage image, int internalFormat, int pixelFormat, 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..7311f20b3 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. */ @@ -166,7 +166,7 @@ public class DDSImage { public static DDSImage read(String filename) throws IOException { return read(new File(filename)); } - + /** Reads a DirectDraw surface from the specified file, returning the resulting DDSImage. @@ -212,7 +212,7 @@ public class DDSImage { } } - /** + /** * Creates a new DDSImage from data supplied by the user. The * resulting DDSImage can be written to disk using the write() * method. @@ -763,9 +763,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,7 +775,10 @@ 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(); } @@ -785,7 +790,7 @@ public class DDSImage { buf.put(mipmapData[i]); } this.buf = buf; - + // Allocate and initialize a Header header = new Header(); header.size = Header.size(); @@ -852,6 +857,32 @@ public class DDSImage { return blockSize; } + private static int computeBlockSize(int width, + int height, + int depth, + 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(int map) { int width = getWidth(); for (int i = 0; i < map; i++) { 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..2081788ba --- /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(InputStream in, 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(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(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(int width, int height, ColorSpace sourceCM, 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(int x, int y, byte r, byte g, 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(int x, int y, byte c1, byte c2) { + throw new RuntimeException("not supported yet"); + } + + @Override + public final void storeYCbCr(int x, int y, byte Y, byte Cb, 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(InputStream in, 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 JPEGColorSink pixelStorage; + private final int pixelWidth, pixelHeight, glFormat, bytesPerPixel; + private 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..3c90d96e4 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. */ @@ -82,38 +82,45 @@ public class LEDataInputStream extends FilterInputStream implements DataInput dataIn = new DataInputStream(in); } + @Override public void close() throws IOException { dataIn.close(); // better close as we create it. // this will close underlying as well. } + @Override public synchronized final int read(byte b[]) throws IOException { return dataIn.read(b, 0, b.length); } + @Override public synchronized final int read(byte b[], int off, int len) throws IOException { int rl = dataIn.read(b, off, len); return rl; } + @Override public final void readFully(byte b[]) throws IOException { dataIn.readFully(b, 0, b.length); } + @Override public final void readFully(byte b[], int off, int len) throws IOException { dataIn.readFully(b, off, len); } + @Override public final int skipBytes(int n) throws IOException { return dataIn.skipBytes(n); } + @Override public final boolean readBoolean() throws IOException { int ch = dataIn.read(); @@ -122,6 +129,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return (ch != 0); } + @Override public final byte readByte() throws IOException { int ch = dataIn.read(); @@ -130,6 +138,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return (byte)(ch); } + @Override public final int readUnsignedByte() throws IOException { int ch = dataIn.read(); @@ -138,6 +147,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return ch; } + @Override public final short readShort() throws IOException { int ch1 = dataIn.read(); @@ -147,8 +157,9 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return (short)((ch1 << 0) + (ch2 << 8)); } + @Override public final int readUnsignedShort() throws IOException - { + { int ch1 = dataIn.read(); int ch2 = dataIn.read(); if ((ch1 | ch2) < 0) @@ -156,6 +167,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return (ch1 << 0) + (ch2 << 8); } + @Override public final char readChar() throws IOException { int ch1 = dataIn.read(); @@ -165,6 +177,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput return (char)((ch1 << 0) + (ch2 << 8)); } + @Override public final int readInt() throws IOException { int ch1 = dataIn.read(); @@ -176,18 +189,21 @@ public class LEDataInputStream extends FilterInputStream implements DataInput 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); + return ((long)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,8 +211,9 @@ 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(); @@ -204,8 +221,9 @@ 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 readUTF() throws IOException { return new String(); @@ -213,7 +231,7 @@ public class LEDataInputStream extends FilterInputStream implements DataInput /** * dont call this it is not implemented - * @return empty new string + * @return empty new string **/ public final static String readUTF(DataInput in) throws IOException { 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..93b097500 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. */ @@ -78,43 +78,51 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput dataOut = new DataOutputStream(out); } + @Override public void close() throws IOException { dataOut.close(); // better close as we create it. // this will close underlying as well. } + @Override public synchronized final void write(byte b[]) throws IOException { dataOut.write(b, 0, b.length); } + @Override public synchronized final void write(byte b[], int off, int len) throws IOException { dataOut.write(b, off, len); } + @Override public final void write(int b) throws IOException { dataOut.write(b); } + @Override public final void writeBoolean(boolean v) throws IOException { dataOut.writeBoolean(v); } + @Override public final void writeByte(int v) throws IOException { dataOut.writeByte(v); } /** Don't call this -- not implemented */ + @Override public final void writeBytes(String s) throws IOException { throw new UnsupportedOperationException(); } + @Override public final void writeChar(int v) throws IOException { dataOut.writeChar(((v >> 8) & 0xff) | @@ -122,21 +130,25 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput } /** Don't call this -- not implemented */ + @Override public final void writeChars(String s) throws IOException { throw new UnsupportedOperationException(); } + @Override public final void writeDouble(double v) throws IOException { writeLong(Double.doubleToRawLongBits(v)); } + @Override public final void writeFloat(float v) throws IOException { writeInt(Float.floatToRawIntBits(v)); } + @Override public final void writeInt(int v) throws IOException { dataOut.writeInt((v >>> 24) | @@ -145,12 +157,14 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput (v << 24)); } + @Override public final void writeLong(long v) throws IOException { writeInt((int) v); writeInt((int) (v >>> 32)); } + @Override public final void writeShort(int v) throws IOException { dataOut.writeShort(((v >> 8) & 0xff) | @@ -158,6 +172,7 @@ public class LEDataOutputStream extends FilterOutputStream implements DataOutput } /** Don't call this -- not implemented */ + @Override public final void writeUTF(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 c2b131b97..cabf4ebc5 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. */ @@ -77,16 +77,19 @@ public class NetPbmTextureWriter implements TextureWriter { public int getMagic() { return magic; } - public static final String PPM = "ppm"; - public static final String PAM = "pam"; + /** @see TextureIO#PPM */ + public static final String PPM = TextureIO.PPM; + /** @see TextureIO#PAM */ + public static final String PAM = TextureIO.PAM; public String getSuffix() { return (magic==6)?PPM:PAM; } + @Override public boolean write(File file, 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; @@ -95,7 +98,7 @@ public class NetPbmTextureWriter implements TextureWriter { } else { return false; } - } + } try { res = writeImpl(file, data); } finally { @@ -103,7 +106,7 @@ public class NetPbmTextureWriter implements TextureWriter { } return res; } - + private boolean writeImpl(File file, TextureData data) throws IOException { int pixelFormat = data.getPixelFormat(); final int pixelType = data.getPixelType(); @@ -113,16 +116,16 @@ public class NetPbmTextureWriter implements TextureWriter { 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 ) { + + if( pixelFormat == GL2.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); @@ -139,7 +142,7 @@ public class NetPbmTextureWriter implements TextureWriter { } FileOutputStream fos = IOUtil.getFileOutputStream(file, true); - + StringBuilder header = new StringBuilder(); header.append("P"); header.append(magic); @@ -169,7 +172,7 @@ public class NetPbmTextureWriter implements TextureWriter { } fos.write(header.toString().getBytes()); - + FileChannel fosc = fos.getChannel(); fosc.write(buf); fosc.force(true); @@ -178,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 a89418f84..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/spi/PNGImage.java +++ /dev/null @@ -1,172 +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., 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. */ - 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 (3 > channels || channels > 4 ) { - throw new RuntimeException("PNGImage can only handle RGB/RGBA images for now. Channels "+channels); - } - bytesPerPixel=pngr.imgInfo.bytesPixel; - if (3 > bytesPerPixel || bytesPerPixel > 4 ) { - throw new RuntimeException("PNGImage can only handle RGB/RGBA images for now. 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]; - } - glFormat= ( 4 == bytesPerPixel ) ? GL.GL_RGBA : GL.GL_RGB; - data = Buffers.newDirectByteBuffer(bytesPerPixel * pixelWidth * pixelHeight); - reversedChannels = false; // RGB[A] - final boolean hasAlpha = 4 == bytesPerPixel; - 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 - 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 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 - 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..cbc8e652f 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. */ @@ -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 @@ -126,6 +126,7 @@ public class SGIImage { in.read(tmp); } + @Override public String toString() { return ("magic: " + magic + " storage: " + (int) storage + @@ -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,7 +235,7 @@ public class SGIImage { //---------------------------------------------------------------------- // Internals only below this point // - + private void decodeImage(DataInputStream in) throws IOException { if (header.storage == 1) { // Read RLE compression data; row starts and sizes @@ -478,7 +480,7 @@ public class SGIImage { 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; @@ -486,7 +488,7 @@ public class SGIImage { 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); @@ -581,8 +583,8 @@ 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))); @@ -604,7 +606,7 @@ public class SGIImage { byte[] dest = new byte[16384]; int pos = 0; int numRead = 0; - + boolean done = false; do { 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..15cd63eb3 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. */ @@ -146,9 +146,9 @@ public class TGAImage { 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 + @@ -273,21 +274,32 @@ 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 { + setupImage24_32(glp); + int i; // row index int y; // output row index - int rawWidth = header.width() * (header.pixelDepth() / 8); + int rawWidth = header.width() * bpp; byte[] rawBuf = new byte[rawWidth]; byte[] tmpData = new byte[rawWidth * header.height()]; @@ -302,31 +314,57 @@ 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(GLProfile glp, LEDataInputStream dIn) throws IOException { + setupImage24_32(glp); + + byte[] pixel = new byte[bpp]; + int rawWidth = header.width() * bpp; + 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(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) { 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..18ad429d2 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,6 +54,7 @@ import com.jogamp.opengl.util.texture.spi.*; public class IIOTextureProvider implements TextureProvider { private static final boolean DEBUG = Debug.debug("TextureIO"); + @Override public TextureData newTextureData(GLProfile glp, File file, int internalFormat, int pixelFormat, @@ -70,6 +71,7 @@ public class IIOTextureProvider implements TextureProvider { return new AWTTextureData(glp, internalFormat, pixelFormat, mipmap, img); } + @Override public TextureData newTextureData(GLProfile glp, InputStream stream, int internalFormat, int pixelFormat, @@ -86,6 +88,7 @@ public class IIOTextureProvider implements TextureProvider { return new AWTTextureData(glp, internalFormat, pixelFormat, mipmap, img); } + @Override public TextureData newTextureData(GLProfile glp, URL url, int internalFormat, int pixelFormat, 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..be82e4fb8 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,6 +53,7 @@ import com.jogamp.opengl.util.texture.*; import com.jogamp.opengl.util.texture.spi.*; public class IIOTextureWriter implements TextureWriter { + @Override public boolean write(File file, TextureData data) throws IOException { int pixelFormat = data.getPixelFormat(); @@ -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)"); } } |