diff options
author | phil <[email protected]> | 2016-10-11 13:01:43 +1300 |
---|---|---|
committer | phil <[email protected]> | 2016-10-11 13:01:43 +1300 |
commit | ef5f2dc1bc8c9af1f2af1a11d1d45e9157e32ec3 (patch) | |
tree | fc9cadeabd77f4acf75dec5a930ebdb3b6e27d74 | |
parent | 19d714dd3948ce6881152cd54c4233e93f596683 (diff) |
GL2ES2Pipeline first cut
Examples of use can be found at
https://github.com/philjord/java3d-examples/tree/master/src/classes/org/jdesktop/j3d/examples/gl2es2pipeline
-rw-r--r-- | src/main/java/org/jogamp/java3d/GeometryArrayRetained.java | 26 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Jogl2es2Context.java | 471 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Jogl2es2DEPPipeline.java | 488 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Jogl2es2MatrixUtil.java | 918 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Jogl2es2PerFrameStats.java | 208 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Jogl2es2Pipeline.java | 7858 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/MasterControl.java | 2 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/Pipeline.java | 10 | ||||
-rw-r--r-- | src/main/java/org/jogamp/java3d/SceneGraphObject.java | 2 |
9 files changed, 9980 insertions, 3 deletions
diff --git a/src/main/java/org/jogamp/java3d/GeometryArrayRetained.java b/src/main/java/org/jogamp/java3d/GeometryArrayRetained.java index 70d8073..0aa3e2b 100644 --- a/src/main/java/org/jogamp/java3d/GeometryArrayRetained.java +++ b/src/main/java/org/jogamp/java3d/GeometryArrayRetained.java @@ -412,7 +412,24 @@ ArrayList<ArrayList<MorphRetained>> morphUserLists = null; if (this.refCount <= 0) { isShared = false; } - } + //PJ: big ugly hack for buffers + // only Jogl2es2Pipeline uses buffers + if(Pipeline.getPipeline() instanceof Jogl2es2Pipeline) + { + for(Context ctx: ctxExecutedOn) + { + ((Jogl2es2Pipeline)Pipeline.getPipeline()).registerClearBuffers(ctx, this); + } + } + + ctxExecutedOn.clear(); + prevContext = null; + } + + //PJ: big ugly hack for buffers + protected HashSet<Context> ctxExecutedOn = new HashSet<Context>(); + protected Context prevContext = null; // as I only expect one check versus prev to see if something new is come along + @Override void computeBoundingBox() { @@ -2196,6 +2213,13 @@ ArrayList<ArrayList<MorphRetained>> morphUserLists = null; int screen, boolean ignoreVertexColors) { + //PJ: big ugly hack for buffers + if(cv.ctx != prevContext) + { + ctxExecutedOn.add(cv.ctx); + prevContext = cv.ctx; + } + int cdirty; boolean useAlpha = false; Object[] retVal; diff --git a/src/main/java/org/jogamp/java3d/Jogl2es2Context.java b/src/main/java/org/jogamp/java3d/Jogl2es2Context.java new file mode 100644 index 0000000..41c1511 --- /dev/null +++ b/src/main/java/org/jogamp/java3d/Jogl2es2Context.java @@ -0,0 +1,471 @@ +package org.jogamp.java3d;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.jogamp.vecmath.Matrix3d;
+import org.jogamp.vecmath.Matrix4d;
+import org.jogamp.vecmath.Vector3f;
+import org.jogamp.vecmath.Vector4f;
+
+import com.jogamp.opengl.GL2ES2;
+import com.jogamp.opengl.GL2ES3;
+import com.jogamp.opengl.GLContext;
+
+public class Jogl2es2Context extends JoglContext
+{
+ /*NOTES
+ * must carefully respond to deprecated methods with a detailed unsupported method
+ * must comment heavily in GeometryArrayRetained regarding the new use of buffers and contexts
+ * consider SceneGraphObject capabilitybits change
+ * SceneGraphObject setDefaultReadCapabilities significant change, what to do?
+ *
+ *
+ * consider that asking for a certain pipeline on command line, so get that prop in along with the default read!
+ * default pipeline is the new jogles
+ *
+ * Need to document the full range of options in a special default shader based on zz_default but possibly just in a java class
+ *
+ *
+ */
+
+ // Step to convert
+ // 1. replace all SpareArray< with HashMap<Integer, JoglesContext and JoglesPipeline
+ // 2. GeometryArrayRetained must add context tracking for buffers
+ // 3. remove all blocks relating to ATTEMPT_UBO
+ // 4. remove all calls relating to ATTEMPT_OPTIMIZED_VERTICES
+ // 5. swap tcBufIds.keyAt(i) for a key foreach of hashmap vaBufIds
+ // 6. update Pipeline to include the new type
+ // 7. update MasterControl to include jogles pipeline
+ // 8. JoglContext ctx = new JoglContext(glContext); in createNewContext becomes JoglesContext ctx = new JoglesContext(glContext);
+ // 9. and again for createQueryContext
+ // 10. rename jogles* to jogl2es2* and delete the triangle array
+ //
+ // 99. rename the classes Jogl2es3... remember to search cause of comments, and the Pipeline reflection call
+ //
+ //
+
+ //pre-casting for speed
+ public GL2ES2 gl2es2 = null;
+ public GL2ES3 gl2es3 = null;
+
+ public Jogl2es2Context(GLContext context)
+ {
+ super(context);
+ gl2es2 = context.getGL().getGL2ES2();
+ if (context.getGL().isGL2ES3())
+ gl2es3 = (GL2ES3) context.getGL();
+ }
+
+ public JoglShaderObject shaderProgram;
+ public int shaderProgramId = -1;
+ public ProgramData programData;
+
+ @Override
+ void setShaderProgram(JoglShaderObject object)
+ {
+ super.setShaderProgram(object);
+ shaderProgram = object;
+ shaderProgramId = object == null ? -1 : object.getValue();
+ programData = allProgramData.get(shaderProgramId);
+ if (programData == null)
+ {
+ programData = new ProgramData();
+ allProgramData.put(shaderProgramId, programData);
+ }
+
+ }
+
+ // all buffers created are recorded for each render pass, and for cleanup
+ public ArrayList<GeometryArrayRetained> geoToClearBuffers = new ArrayList<GeometryArrayRetained>();
+
+ public HashMap<Integer, GeometryData> allGeometryData = new HashMap<Integer, GeometryData>();
+
+ public static class GeometryData
+ {
+ public int nativeId = -1;
+
+ public int geoToIndBuf = -1;
+ public int geoToIndBufSize = -1;
+ public int[] geoToIndStripBuf = null;
+ //public int geoToIndStripSwappedSize = -1;// removed into j3dnotristrips
+ public int geoToCoordBuf = -1;
+ public int geoToCoordBuf1 = -2;// double buffered for updates
+ public int geoToCoordBuf2 = -3;
+ public int geoToCoordBuf3 = -3;
+ public int geoToCoordBufSize = -1;
+ public int geoToColorBuf = -1;
+ public int geoToNormalBuf = -1;
+ public HashMap<Integer, Integer> geoToTexCoordsBuf = new HashMap<Integer, Integer>();
+ public HashMap<Integer, Integer> geoToVertAttribBuf = new HashMap<Integer, Integer>();
+
+ //Every thing below relates to interleaved data
+ public int coordBufId = -1; // if separate
+ public int interleavedBufId = -1;
+ public int interleavedStride = 0;
+ public int geoToCoordOffset = -1;
+ public int geoToColorsOffset = -1;
+ public int geoToNormalsOffset = -1;
+ public int[] geoToVattrOffset = new int[10];
+ public int[] geoToTexCoordOffset = new int[10];
+
+ // vertex array object id for this geom
+ public int vaoId = -1;
+
+ //used to identify each geometry as we see it
+ private static int nextNativeId = 0;
+
+ public GeometryData()
+ {
+ nativeId = nextNativeId++;
+ nextNativeId = nextNativeId > Integer.MAX_VALUE - 10 ? 0 : nextNativeId;// desperate loop
+ }
+
+ }
+
+ public HashMap<Integer, ProgramData> allProgramData = new HashMap<Integer, ProgramData>();
+
+ public static class ProgramData
+ {
+ public HashMap<String, Integer> progToGenVertAttNameToGenVertAttIndex = new HashMap<String, Integer>();
+ public LocationData programToLocationData = null;// null to indicate need to load
+ public ByteBuffer programToUBOBB = null;
+ public int programToUBOBuf = -1;
+ }
+
+ //Light data recorded to be handed into shader as uniform on next update
+ //see https://www.opengl.org/sdk/docs/man2/ glLight
+ // for usage details
+ public static class LightData
+ {
+ public Vector4f ambient = new Vector4f();
+ public Vector4f diffuse = new Vector4f();
+ public Vector4f specular = new Vector4f();
+ public Vector4f pos = new Vector4f();
+ public Vector4f spotDir = new Vector4f();
+ public float GL_CONSTANT_ATTENUATION;
+ public float GL_LINEAR_ATTENUATION;
+ public float GL_QUADRATIC_ATTENUATION;
+ public float GL_SPOT_EXPONENT;
+ public float GL_SPOT_CUTOFF;
+ }
+
+ // should use getMaximumLights() in pipeline? though it's up to the shader
+ public static int MAX_LIGHTS = 8;
+ public LightData[] dirLight = new LightData[MAX_LIGHTS];
+ public LightData[] pointLight = new LightData[MAX_LIGHTS];
+ public LightData[] spotLight = new LightData[MAX_LIGHTS];
+
+ public static class FogData
+ {
+ public boolean enable = false;
+ public Vector3f expColor = new Vector3f();
+ public float expDensity = 0;
+ public Vector3f linearColor = new Vector3f();
+ public float linearStart = 0;
+ public float linearEnd = 0;
+ }
+
+ public FogData fogData = new FogData();
+
+ public static class MaterialData
+ {
+ public boolean lightEnabled = true;
+ public Vector3f emission = new Vector3f();
+ public Vector3f ambient = new Vector3f();
+ public Vector3f specular = new Vector3f();
+ public Vector4f diffuse = new Vector4f();
+ public float shininess;
+ }
+
+ public MaterialData materialData = new MaterialData();
+
+ //See here http://download.java.net/media/java3d/javadoc/1.3.2/javax/media/j3d/RenderingAttributes.html
+ // For coloring implementation details
+
+ //only for no lighting, materialDiffuse or vertex colors otherwise
+ public Vector4f objectColor = new Vector4f();
+
+ public float pointSize = 0;
+
+ public int polygonMode = PolygonAttributes.POLYGON_FILL;
+
+ public static class RenderingData
+ {
+ public boolean alphaTestEnabled = false;
+ public int alphaTestFunction = RenderingAttributes.ALWAYS;
+ public float alphaTestValue = 0;
+ public boolean ignoreVertexColors;
+ }
+
+ public RenderingData renderingData = new RenderingData();
+
+ public boolean[] enabledLights = new boolean[MAX_LIGHTS];
+
+ public Vector4f currentAmbientColor = new Vector4f();
+
+ public Matrix4d textureTransform = new Matrix4d();
+
+ //various ffp matrixes
+ public Matrix4d currentModelMat = new Matrix4d();
+ public Matrix4d currentViewMat = new Matrix4d();
+ public Matrix4d currentModelViewMat = new Matrix4d();
+ public Matrix4d currentModelViewMatInverse = new Matrix4d();
+ public Matrix4d currentModelViewProjMat = new Matrix4d();
+ public Matrix3d currentNormalMat = new Matrix3d();
+ public Matrix4d currentProjMat = new Matrix4d();
+ public Matrix4d currentProjMatInverse = new Matrix4d();
+
+ /**
+ * On shader creation the various FFP locations are discovered and recorded for use later
+ * @author phil
+ *
+ */
+
+ //TODO: maybe many bufers allows buffersubdata to run faster
+ public int globalUboBufId = -1; // the one buffer is bound once then reused, dear god
+
+ public static class LocationData
+ {
+ //UBO data
+ public int uboBufId = -1;
+ public int blockIndex = -1;
+ public int blockSize = -1;
+ public int glProjectionMatrixOffset = -1;
+ public int glProjectionMatrixInverseOffset = -1;
+ public int glViewMatrixOffset = -1;
+ public int glModelMatrixOffset = -1;
+ public int glModelViewMatrixOffset = -1;
+ public int glModelViewMatrixInverseOffset = -1;
+ public int glModelViewProjectionMatrixOffset = -1;
+ public int glNormalMatrixOffset = -1;
+ public int glFrontMaterialdiffuseOffset = -1;
+ public int glFrontMaterialemissionOffset = -1;
+ public int glFrontMaterialspecularOffset = -1;
+ public int glFrontMaterialshininessOffset = -1;
+ public int ignoreVertexColorsOffset = -1;
+ public int glLightModelambientOffset = -1;
+ public int objectColorOffset = -1;
+ public int glLightSource0positionOffset = -1;
+ public int glLightSource0diffuseOffset = -1;
+ public int textureTransformOffset = -1;
+ public int alphaTestEnabledOffset = -1;
+ public int alphaTestFunctionOffset = -1;
+ public int alphaTestValueOffset = -1;
+
+ //normal uniform data
+ public int glProjectionMatrix = -1;
+ public int glProjectionMatrixInverse = -1;
+ public int glModelMatrix = -1;
+ public int glViewMatrix = -1;
+ public int glModelViewMatrix = -1;
+ public int glModelViewMatrixInverse = -1;
+ public int glModelViewProjectionMatrix = -1;
+ public int glNormalMatrix = -1;
+ public int ignoreVertexColors = -1;
+ public int glFrontMaterialdiffuse = -1;
+ public int glFrontMaterialemission = -1;
+ public int glFrontMaterialspecular = -1;
+ public int glFrontMaterialshininess = -1;
+ public int glLightModelambient = -1;
+ public int objectColor = -1;
+ public int glLightSource0position = -1;
+ public int glLightSource0diffuse = -1;
+ public int alphaTestEnabled = -1;
+ public int alphaTestFunction = -1;
+ public int alphaTestValue = -1;
+ public int textureTransform = -1;
+ public int fogEnabled = -1;
+ public int expColor = -1;
+ public int expDensity = -1;
+ public int linearColor = -1;
+ public int linearStart = -1;
+ public int linearEnd = -1;
+
+ public int glVertex = -1;
+ public int glColor = -1;
+ public int glNormal = -1;
+
+ public int[] glMultiTexCoord = new int[16];
+ public HashMap<Integer, Integer> genAttIndexToLoc = new HashMap<Integer, Integer>();
+
+ }
+
+ /**
+ * below here are openGL state tracking to reduce unnecessary native calls
+ * Note this is NOT like the "new" or so called current staet above taht needs to be st in the FFP
+ * call, this is the old or previously set data, that might not need to be updated
+ * @author phil
+ *
+ */
+ public static class GL_State
+ {
+ public boolean depthBufferEnableOverride;
+ public boolean depthBufferEnable;
+ public int depthTestFunction;
+ public boolean depthBufferWriteEnableOverride;
+ public boolean depthBufferWriteEnable;
+ public boolean userStencilAvailable;
+ public boolean stencilEnable;
+ public boolean glDepthMask;
+ public boolean glEnableGL_STENCIL_TEST;
+ public int stencilFailOp;
+ public int stencilZFailOp;
+ public int stencilZPassOp;
+ public int stencilFunction;
+ public int stencilReferenceValue;
+ public int stencilCompareMask;
+ public int stencilWriteMask;
+ public int[] setGLSLUniform1i = new int[500];
+ public int[] clearer1 = new int[500];
+ public float[] setGLSLUniform1f = new float[500];
+ public float[] clearer2 = new float[500];
+ public boolean glEnableGL_BLEND;
+ public int srcBlendFunction;
+ public int dstBlendFunction;
+ public int glActiveTexture;
+ public int currentProgramId;
+ public int[] glBindTextureGL_TEXTURE_2D = new int[35000];// indexed based on current glActiveTexture
+ public int[] clearer3 = new int[35000];
+ public int cullFace;
+ public float polygonOffsetFactor;
+ public float polygonOffset;
+
+ public boolean ignoreVertexColors;
+ public Vector4f glFrontMaterialdiffuse = new Vector4f();
+ public Vector3f glFrontMaterialemission = new Vector3f();
+ public Vector3f glFrontMaterialspecular = new Vector3f();
+ public float glFrontMaterialshininess;
+ public Vector4f glLightModelambient = new Vector4f();
+ public Vector4f objectColor = new Vector4f();
+ public Matrix4d textureTransform = new Matrix4d();
+ public Matrix4d modelMatrix = new Matrix4d();
+ public Matrix4d glModelViewMatrix = new Matrix4d();
+ public Matrix4d glModelViewMatrixInverse = new Matrix4d();
+ public Matrix4d glModelViewProjectionMatrix = new Matrix4d();
+ public Matrix3d glNormalMatrix = new Matrix3d();
+ public Vector4f glLightSource0position = new Vector4f();
+ public Vector4f glLightSource0diffuse = new Vector4f();
+ public boolean alphaTestEnabled = false;
+ public int alphaTestFunction;
+ public float alphaTestValue;
+ public boolean fogEnabled = false;
+ public Vector3f expColor = new Vector3f();
+ public float expDensity;
+ public Vector3f linearColor = new Vector3f();
+ public float linearStart;
+ public float linearEnd;
+
+ public void clear()
+ {
+ depthBufferEnableOverride = false;
+ depthBufferEnable = false;
+ depthTestFunction = -1;
+ depthBufferWriteEnableOverride = false;
+ depthBufferWriteEnable = false;
+ userStencilAvailable = false;
+ stencilEnable = false;
+ glDepthMask = false;
+ glEnableGL_STENCIL_TEST = false;
+ stencilFailOp = -1;
+ stencilZFailOp = -1;
+ stencilZPassOp = -1;
+ stencilFunction = -1;
+ stencilReferenceValue = -1;
+ stencilCompareMask = -1;
+ stencilWriteMask = -1;
+ System.arraycopy(clearer1, 0, setGLSLUniform1i, 0, setGLSLUniform1i.length);
+ System.arraycopy(clearer2, 0, setGLSLUniform1f, 0, setGLSLUniform1f.length);
+ glEnableGL_BLEND = false;
+ srcBlendFunction = -1;
+ dstBlendFunction = -1;
+ glActiveTexture = -1;
+ currentProgramId = -1;
+ System.arraycopy(clearer3, 0, glBindTextureGL_TEXTURE_2D, 0, glBindTextureGL_TEXTURE_2D.length);
+ cullFace = -1;
+ polygonOffsetFactor = -1;
+ polygonOffset = -1;
+ ignoreVertexColors = false;
+ glFrontMaterialdiffuse.set(-999f, -999f, -999f, -999f);
+ glFrontMaterialemission.set(-999f, -999f, -999f);
+ glFrontMaterialspecular.set(-999f, -999f, -999f);
+ glFrontMaterialshininess = -99;
+ glLightModelambient.set(-999f, -999f, -999f, -999f);
+ objectColor.set(-999f, -999f, -999f, -999f);
+ textureTransform.setIdentity();
+ modelMatrix.setIdentity();
+ glModelViewMatrix.setIdentity();
+ glModelViewMatrixInverse.setIdentity();
+ glModelViewProjectionMatrix.setIdentity();
+ glNormalMatrix.setIdentity();
+ glLightSource0position.set(-999f, -999f, -999f, -999f);
+ glLightSource0diffuse.set(-999f, -999f, -999f, -999f);
+ alphaTestEnabled = false;
+ alphaTestFunction = -1;
+ alphaTestValue = -99f;
+ fogEnabled = false;
+ expColor.set(-999f, -999f, -999f);
+ expDensity = -99f;
+ linearColor.set(-999f, -999f, -999f);
+ linearStart = -99f;
+ linearEnd = -99f;
+ }
+ }
+
+ public GL_State gl_state = new GL_State();
+
+ /**
+ * program used on last run through of FFP, so nearly like gl_state above, just a desperate attempt
+ * to see if the uniform locations have changed even if a new shader is being used
+ */
+ public int prevShaderProgram;
+
+ public static class ShaderFFPLocations
+ {
+ public int glProjectionMatrix;
+ public int glProjectionMatrixInverse;
+ public int modelMatrix;
+ public int viewMatrix;
+ public int glModelViewMatrix;
+ public int glModelViewMatrixInverse;
+ public int glModelViewProjectionMatrix;
+ public int glNormalMatrix;
+ public int glFrontMaterialdiffuse;
+ public int glFrontMaterialemission;
+ public int glFrontMaterialspecular;
+ public int glFrontMaterialshininess;
+ public int ignoreVertexColors;
+ public int glLightModelambient;
+ public int glLightSource0position;
+ public int glLightSource0diffuse;
+ public int textureTransform;
+ }
+
+ //a plce to put teh previous shader locations for FFP minimize calls
+ public ShaderFFPLocations[] shaderFFPLocations = new ShaderFFPLocations[100];
+
+ // The per frame stats
+ public Jogl2es2PerFrameStats perFrameStats = new Jogl2es2PerFrameStats();
+
+ private int statsFrame = 0;
+ private int STATS_OUTPUT_FRAME_FREQ = 50;
+
+ public void outputPerFrameData()
+ {
+ statsFrame++;
+ if (statsFrame % STATS_OUTPUT_FRAME_FREQ == 0)
+ {
+ statsFrame = 0;
+ System.out.println("======================================================");
+ perFrameStats.outputPerFrameData();
+ }
+ // clear for next frame
+ perFrameStats = new Jogl2es2PerFrameStats();
+ perFrameStats.endOfPrevFrameTime = System.nanoTime();
+ }
+
+ // just a singleton of the handy matrix/array operations
+ public Jogl2es2MatrixUtil matrixUtil = new Jogl2es2MatrixUtil();
+
+}
diff --git a/src/main/java/org/jogamp/java3d/Jogl2es2DEPPipeline.java b/src/main/java/org/jogamp/java3d/Jogl2es2DEPPipeline.java new file mode 100644 index 0000000..0775ef6 --- /dev/null +++ b/src/main/java/org/jogamp/java3d/Jogl2es2DEPPipeline.java @@ -0,0 +1,488 @@ +package org.jogamp.java3d; + +import java.nio.FloatBuffer; + +/** + * Separated from the actual pipeline for clarity of features that are not supported from the + * Pipeline class for the JOGL rendering pipeline. + */ +abstract class Jogl2es2DEPPipeline extends Pipeline +{ + public static final String VALID_FORMAT_MESSAGE = "The Gl2ES2 pipeline only supports a subset of the Geometry data types and formats. \n"// + + "You can now only pass in a TriangleArray, TriangleStripArray, TriangleFanArray, \n"// + + "LineArray, LineStripArray, PointArray and the 6 Indexed equivilents (effectively QuadArray is removed). \n"// + + "Each Geomtry must have a format of GeometryArray.BY_REFERENCE = true and GeometryArray.INTERLEAVED = false. \n"// + + "Texture Coordinate generation is not supported, Texture Filter, Sharpen and combine are not supported. \n"// + + "Texture3D, TextureCubeMap are not supported. \n"// + + "Off screen buffers and decals are also not supported. \n"// + + "Coordinates must be defind and float type, colors if defined must be float type. \n"// + + "It is strongly recomended that you use the format GeometryArray.USE_NIO_BUFFER = true.";// + + /** + * Constructor for singleton JoglPipeline instance + */ + protected Jogl2es2DEPPipeline() + { + + } + + // --------------------------------------------------------------------- + + // + // GeometryArrayRetained methods + // + + // used for GeometryArrays by Copy or interleaved + @Override + @Deprecated + void execute(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean useAlpha, + boolean ignoreVertexColors, int startVIndex, int vcount, int vformat, int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, int[] texUnitOffset, int numActiveTexUnitState, int vertexAttrCount, int[] vertexAttrSizes, + float[] varray, float[] carray, int cDirty) + { + throw new UnsupportedOperationException("Use of GeometryArrays (un-indexed) by Copy or interleaved not allowed.\n" + VALID_FORMAT_MESSAGE); + } + + + + // used by GeometryArray by Reference in interleaved format with NIO buffer + @Override + @Deprecated + void executeInterleavedBuffer(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean useAlpha, + boolean ignoreVertexColors, int startVIndex, int vcount, int vformat, int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, int[] texUnitOffset, int numActiveTexUnit, FloatBuffer varray, float[] cdata, int cdirty) + { + throw new UnsupportedOperationException( + "Use of GeometryArray (un-indexed) by Reference in interleaved format with NIO buffer.\n" + VALID_FORMAT_MESSAGE); + } + + // used for GeometryArrays (PJ - I presume this means DList usage?) + @Override + @Deprecated + void buildGA(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean updateAlpha, float alpha, + boolean ignoreVertexColors, int startVIndex, int vcount, int vformat, int texCoordSetCount, int[] texCoordSetMap, + int texCoordSetMapLen, int[] texCoordSetMapOffset, int vertexAttrCount, int[] vertexAttrSizes, double[] xform, double[] nxform, + float[] varray) + { + throw new UnsupportedOperationException("DLists in use!.\n" + VALID_FORMAT_MESSAGE); + } + + // used to Build Dlist GeometryArray by Reference with java arrays + @Override + @Deprecated + void buildGAForByRef(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean updateAlpha, float alpha, + boolean ignoreVertexColors, int vcount, int vformat, int vdefined, int initialCoordIndex, float[] vfcoords, double[] vdcoords, + int initialColorIndex, float[] cfdata, byte[] cbdata, int initialNormalIndex, float[] ndata, int vertexAttrCount, + int[] vertexAttrSizes, int[] vertexAttrIndices, float[][] vertexAttrData, int texCoordMapLength, int[] tcoordsetmap, + int[] texIndices, int texStride, Object[] texCoords, double[] xform, double[] nxform) + { + throw new UnsupportedOperationException("DLists in use!.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // IndexedGeometryArrayRetained methods + // + + // by-copy or interleaved, by reference, Java arrays + @Override + @Deprecated + void executeIndexedGeometry(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean useAlpha, + boolean ignoreVertexColors, int initialIndexIndex, int indexCount, int vertexCount, int vformat, int vertexAttrCount, + int[] vertexAttrSizes, int texCoordSetCount, int[] texCoordSetMap, int texCoordSetMapLen, int[] texCoordSetOffset, + int numActiveTexUnitState, float[] varray, float[] carray, int cdirty, int[] indexCoord) + { + throw new UnsupportedOperationException( + "Use of IndexedGeometry by-copy or interleaved, by reference, Java arrays.\n" + VALID_FORMAT_MESSAGE); + } + + // interleaved, by reference, nio buffer + @Override + @Deprecated + void executeIndexedGeometryBuffer(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean useAlpha, + boolean ignoreVertexColors, int initialIndexIndex, int indexCount, int vertexCount, int vformat, int texCoordSetCount, + int[] texCoordSetMap, int texCoordSetMapLen, int[] texCoordSetOffset, int numActiveTexUnitState, FloatBuffer vdata, + float[] carray, int cDirty, int[] indexCoord) + { + throw new UnsupportedOperationException("Use of IndexedGeometry interleaved, by reference, nio buffer.\n" + VALID_FORMAT_MESSAGE); + } + + //PJ presume the word build means a draw list? + // by-copy geometry + @Override + @Deprecated + void buildIndexedGeometry(Context absCtx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean updateAlpha, + float alpha, boolean ignoreVertexColors, int initialIndexIndex, int validIndexCount, int vertexCount, int vformat, + int vertexAttrCount, int[] vertexAttrSizes, int texCoordSetCount, int[] texCoordSetMap, int texCoordSetMapLen, + int[] texCoordSetMapOffset, double[] xform, double[] nxform, float[] varray, int[] indexCoord) + { + throw new UnsupportedOperationException("DLists in use!.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // GraphicsContext3D methods + // + + // Native method for readRaster + // REMOVE FOR SIMPLICITY, POSSIBLY ADD BACK LATER + @Override + @Deprecated + void readRaster(Context ctx, int type, int xSrcOffset, int ySrcOffset, int width, int height, int hCanvas, int imageDataType, + int imageFormat, Object imageBuffer, int depthFormat, Object depthBuffer) + { + + throw new UnsupportedOperationException("Read Raster call not support in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // ModelClipRetained methods + // + @Override + @Deprecated + void updateModelClip(Context ctx, int planeNum, boolean enableFlag, double A, double B, double C, double D) + { + throw new UnsupportedOperationException("Model Clip call not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // TexCoordGenerationRetained methods + // + + /** + * This method updates the native context: + * trans contains eyeTovworld transform in d3d + * trans contains vworldToEye transform in ogl + */ + @Override + @Deprecated + void updateTexCoordGeneration(Context ctx, boolean enable, int genMode, int format, float planeSx, float planeSy, float planeSz, + float planeSw, float planeTx, float planeTy, float planeTz, float planeTw, float planeRx, float planeRy, float planeRz, + float planeRw, float planeQx, float planeQy, float planeQz, float planeQw, double[] vworldToEc) + { + throw new UnsupportedOperationException("Texture Coordinate generation is not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // TextureAttributesRetained methods + // + @Override + @Deprecated + void updateRegisterCombiners(Context absCtx, double[] transform, boolean isIdentity, int textureMode, int perspCorrectionMode, + float textureBlendColorRed, float textureBlendColorGreen, float textureBlendColorBlue, float textureBlendColorAlpha, + int textureFormat, int combineRgbMode, int combineAlphaMode, int[] combineRgbSrc, int[] combineAlphaSrc, int[] combineRgbFcn, + int[] combineAlphaFcn, int combineRgbScale, int combineAlphaScale) + { + throw new UnsupportedOperationException("RegisterCombiners is not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTextureColorTable(Context ctx, int numComponents, int colorTableSize, int[] textureColorTable) + { + throw new UnsupportedOperationException("TextureColorTable is not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateCombiner(Context ctx, int combineRgbMode, int combineAlphaMode, int[] combineRgbSrc, int[] combineAlphaSrc, + int[] combineRgbFcn, int[] combineAlphaFcn, int combineRgbScale, int combineAlphaScale) + { + throw new UnsupportedOperationException("GL combine not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // TextureRetained methods + // Texture2DRetained methods + + @Override + @Deprecated + void updateTexture2DLodOffset(Context ctx, float lodOffsetS, float lodOffsetT, float lodOffsetR) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture2DSharpenFunc(Context ctx, int numSharpenTextureFuncPts, float[] sharpenTextureFuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture2DFilter4Func(Context ctx, int numFilter4FuncPts, float[] filter4FuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // Texture3DRetained methods + // + @Override + @Deprecated + void bindTexture3D(Context ctx, int objectId, boolean enable) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DImage(Context ctx, int numLevels, int level, int textureFormat, int imageFormat, int width, int height, int depth, + int boundaryWidth, int dataType, Object data, boolean useAutoMipMap) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DSubImage(Context ctx, int level, int xoffset, int yoffset, int zoffset, int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, int imgZOffset, int tilew, int tileh, int width, int height, int depth, int dataType, + Object data, boolean useAutoMipMap) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DLodRange(Context ctx, int baseLevel, int maximumLevel, float minimumLod, float maximumLod) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DLodOffset(Context ctx, float lodOffsetS, float lodOffsetT, float lodOffsetR) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DBoundary(Context ctx, int boundaryModeS, int boundaryModeT, int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, float boundaryAlpha) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DFilterModes(Context ctx, int minFilter, int magFilter) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DSharpenFunc(Context ctx, int numSharpenTextureFuncPts, float[] sharpenTextureFuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DFilter4Func(Context ctx, int numFilter4FuncPts, float[] filter4FuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTexture3DAnisotropicFilter(Context ctx, float degree) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // TextureCubeMapRetained methods + + @Override + @Deprecated + void updateTextureCubeMapLodOffset(Context ctx, float lodOffsetS, float lodOffsetT, float lodOffsetR) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTextureCubeMapSharpenFunc(Context ctx, int numSharpenTextureFuncPts, float[] sharpenTextureFuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void updateTextureCubeMapFilter4Func(Context ctx, int numFilter4FuncPts, float[] filter4FuncPts) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // --------------------------------------------------------------------- + + // + // Canvas3D methods - native wrappers + // + + @Override + @Deprecated + void createQueryContext(Canvas3D cv, Drawable drawable, boolean offScreen, int width, int height) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // This is the native for creating an offscreen buffer + @Override + @Deprecated + Drawable createOffScreenBuffer(Canvas3D cv, Context ctx, int width, int height) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // 'destroyContext' is called first if context exists + @Override + @Deprecated + void destroyOffScreenBuffer(Canvas3D cv, Context ctx, Drawable drawable) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // This is the native for reading the image from the offscreen buffer + @Override + @Deprecated + void readOffScreenBuffer(Canvas3D cv, Context ctx, int format, int dataType, Object data, int width, int height) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // This is the native method for doing accumulation. + @Override + @Deprecated + void accum(Context ctx, float value) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void accumReturn(Context ctx) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void clearAccum(Context ctx) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + boolean decal1stChildSetup(Context ctx) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void decalNthChildSetup(Context ctx) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void decalReset(Context ctx, boolean depthBufferEnable) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + // The following three methods are used in multi-pass case + + @Override + @Deprecated + void textureFillBackground(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, float mapMinX, float mapMaxX, + float mapMinY, float mapMaxY, boolean useBilinearFilter) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + + } + + @Override + @Deprecated + void textureFillRaster(Context ctx, float texMinU, float texMaxU, float texMinV, float texMaxV, float mapMinX, float mapMaxX, + float mapMinY, float mapMaxY, float mapZ, float alpha, boolean useBilinearFilter) + { + + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void executeRasterDepth(Context ctx, float posX, float posY, float posZ, int srcOffsetX, int srcOffsetY, int rasterWidth, + int rasterHeight, int depthWidth, int depthHeight, int depthFormat, Object depthData) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + + } + + // used for display Lists + @Override + @Deprecated + void newDisplayList(Context ctx, int displayListId) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void endDisplayList(Context ctx) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void callDisplayList(Context ctx, int id, boolean isNonUniformScale) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + void freeDisplayList(Context ctx, int id) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + // nothing seems to call this in Canvas3D either + void texturemapping(Context ctx, int px, int py, int minX, int minY, int maxX, int maxY, int texWidth, int texHeight, int rasWidth, + int format, int objectId, byte[] imageYdown, int winWidth, int winHeight) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } + + @Override + @Deprecated + // nothing seems to call this in Canvas3D either + boolean initTexturemapping(Context ctx, int texWidth, int texHeight, int objectId) + { + throw new UnsupportedOperationException("Not supported in the GL2ES2 pipeline.\n" + VALID_FORMAT_MESSAGE); + } +} diff --git a/src/main/java/org/jogamp/java3d/Jogl2es2MatrixUtil.java b/src/main/java/org/jogamp/java3d/Jogl2es2MatrixUtil.java new file mode 100644 index 0000000..5237173 --- /dev/null +++ b/src/main/java/org/jogamp/java3d/Jogl2es2MatrixUtil.java @@ -0,0 +1,918 @@ +package org.jogamp.java3d;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import org.jogamp.vecmath.Matrix3d;
+import org.jogamp.vecmath.Matrix4d;
+import org.jogamp.vecmath.SingularMatrixException;
+
+/** class that demands single threading and uses deburners, don't touch if you don't understand, it will be bad...
+ *
+ * @author phil
+ *
+ */
+class Jogl2es2MatrixUtil
+{
+
+ /**
+ * Possibly faster
+ * http://stackoverflow.com/questions/983999/simple-3x3-matrix-inverse-code-c
+ */
+
+ public static void transposeInvert(Matrix3d m, Matrix3d out)
+ {
+ double determinant = m.determinant();
+ if (determinant > 0)
+ {
+ double invdet = 1 / determinant;
+ out.m00 = (m.m11 * m.m22 - m.m21 * m.m12) * invdet;
+ out.m10 = -(m.m01 * m.m22 - m.m02 * m.m21) * invdet;
+ out.m20 = (m.m01 * m.m12 - m.m02 * m.m11) * invdet;
+ out.m01 = -(m.m10 * m.m22 - m.m12 * m.m20) * invdet;
+ out.m11 = (m.m00 * m.m22 - m.m02 * m.m20) * invdet;
+ out.m21 = -(m.m00 * m.m12 - m.m10 * m.m02) * invdet;
+ out.m02 = (m.m10 * m.m21 - m.m20 * m.m11) * invdet;
+ out.m12 = -(m.m00 * m.m21 - m.m20 * m.m01) * invdet;
+ out.m22 = (m.m00 * m.m11 - m.m10 * m.m01) * invdet;
+ }
+ else
+ {
+ out.setIdentity();
+ }
+ }
+
+ /**
+ * Only upper left 3x3 copied and transformed
+ * @param m
+ * @param out
+ */
+
+ public static void transposeInvert(Matrix4d m, Matrix3d out)
+ {
+ double determinant = m.determinant();
+ if (determinant > 0)
+ {
+ double invdet = 1 / determinant;
+ out.m00 = (m.m11 * m.m22 - m.m21 * m.m12) * invdet;
+ out.m10 = -(m.m01 * m.m22 - m.m02 * m.m21) * invdet;
+ out.m20 = (m.m01 * m.m12 - m.m02 * m.m11) * invdet;
+ out.m01 = -(m.m10 * m.m22 - m.m12 * m.m20) * invdet;
+ out.m11 = (m.m00 * m.m22 - m.m02 * m.m20) * invdet;
+ out.m21 = -(m.m00 * m.m12 - m.m10 * m.m02) * invdet;
+ out.m02 = (m.m10 * m.m21 - m.m20 * m.m11) * invdet;
+ out.m12 = -(m.m00 * m.m21 - m.m20 * m.m01) * invdet;
+ out.m22 = (m.m00 * m.m11 - m.m10 * m.m01) * invdet;
+ }
+ else
+ {
+ out.setIdentity();
+ }
+ }
+
+ double result3[] = new double[9];
+ int row_perm3[] = new int[3];
+ double[] tmp3 = new double[9]; // scratch matrix
+ //@See Matrix3d
+
+ final void invertGeneral3(Matrix3d thisM, Matrix3d m1)
+ {
+ for (int i = 0; i < 3; i++)
+ row_perm3[i] = 0;
+
+ for (int i = 0; i < 9; i++)
+ result3[i] = 0.0;
+
+ // Use LU decomposition and backsubstitution code specifically
+ // for floating-point 3x3 matrices.
+
+ // Copy source matrix to t1tmp
+ tmp3[0] = m1.m00;
+ tmp3[1] = m1.m01;
+ tmp3[2] = m1.m02;
+
+ tmp3[3] = m1.m10;
+ tmp3[4] = m1.m11;
+ tmp3[5] = m1.m12;
+
+ tmp3[6] = m1.m20;
+ tmp3[7] = m1.m21;
+ tmp3[8] = m1.m22;
+
+ // Calculate LU decomposition: Is the matrix singular?
+ if (!luDecomposition3(tmp3, row_perm3))
+ {
+ // Matrix has no inverse
+ throw new SingularMatrixException("!luDecomposition(tmp, row_perm)");
+ }
+
+ // Perform back substitution on the identity matrix
+
+ result3[0] = 1.0;
+ result3[4] = 1.0;
+ result3[8] = 1.0;
+ luBacksubstitution3(tmp3, row_perm3, result3);
+
+ thisM.m00 = result3[0];
+ thisM.m01 = result3[1];
+ thisM.m02 = result3[2];
+
+ thisM.m10 = result3[3];
+ thisM.m11 = result3[4];
+ thisM.m12 = result3[5];
+
+ thisM.m20 = result3[6];
+ thisM.m21 = result3[7];
+ thisM.m22 = result3[8];
+
+ }
+
+ double row_scale3[] = new double[3];
+
+ //@See Matrix3d
+ boolean luDecomposition3(double[] matrix0, int[] row_perm)
+ {
+ for (int i = 0; i < 3; i++)
+ row_scale3[i] = 0.0;
+ // Determine implicit scaling information by looping over rows
+ {
+ int i, j;
+ int ptr, rs;
+ double big, temp;
+
+ ptr = 0;
+ rs = 0;
+
+ // For each row ...
+ i = 3;
+ while (i-- != 0)
+ {
+ big = 0.0;
+
+ // For each column, find the largest element in the row
+ j = 3;
+ while (j-- != 0)
+ {
+ temp = matrix0[ptr++];
+ temp = Math.abs(temp);
+ if (temp > big)
+ {
+ big = temp;
+ }
+ }
+
+ // Is the matrix singular?
+ if (big == 0.0)
+ {
+ return false;
+ }
+ row_scale4[rs++] = 1.0 / big;
+ }
+ }
+
+ {
+ int j;
+ int mtx;
+
+ mtx = 0;
+
+ // For all columns, execute Crout's method
+ for (j = 0; j < 3; j++)
+ {
+ int i, imax, k;
+ int target, p1, p2;
+ double sum, big, temp;
+
+ // Determine elements of upper diagonal matrix U
+ for (i = 0; i < j; i++)
+ {
+ target = mtx + (3 * i) + j;
+ sum = matrix0[target];
+ k = i;
+ p1 = mtx + (3 * i);
+ p2 = mtx + j;
+ while (k-- != 0)
+ {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 3;
+ }
+ matrix0[target] = sum;
+ }
+
+ // Search for largest pivot element and calculate
+ // intermediate elements of lower diagonal matrix L.
+ big = 0.0;
+ imax = -1;
+ for (i = j; i < 3; i++)
+ {
+ target = mtx + (3 * i) + j;
+ sum = matrix0[target];
+ k = j;
+ p1 = mtx + (3 * i);
+ p2 = mtx + j;
+ while (k-- != 0)
+ {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 3;
+ }
+ matrix0[target] = sum;
+
+ // Is this the best pivot so far?
+ if ((temp = row_scale3[i] * Math.abs(sum)) >= big)
+ {
+ big = temp;
+ imax = i;
+ }
+ }
+
+ if (imax < 0)
+ {
+ throw new RuntimeException("imax < 0");
+ }
+
+ // Is a row exchange necessary?
+ if (j != imax)
+ {
+ // Yes: exchange rows
+ k = 3;
+ p1 = mtx + (3 * imax);
+ p2 = mtx + (3 * j);
+ while (k-- != 0)
+ {
+ temp = matrix0[p1];
+ matrix0[p1++] = matrix0[p2];
+ matrix0[p2++] = temp;
+ }
+
+ // Record change in scale factor
+ row_scale3[imax] = row_scale3[j];
+ }
+
+ // Record row permutation
+ row_perm[j] = imax;
+
+ // Is the matrix singular
+ if (matrix0[(mtx + (3 * j) + j)] == 0.0)
+ {
+ return false;
+ }
+
+ // Divide elements of lower diagonal matrix L by pivot
+ if (j != (3 - 1))
+ {
+ temp = 1.0 / (matrix0[(mtx + (3 * j) + j)]);
+ target = mtx + (3 * (j + 1)) + j;
+ i = 2 - j;
+ while (i-- != 0)
+ {
+ matrix0[target] *= temp;
+ target += 3;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ //@See Matrix3d
+ void luBacksubstitution3(double[] matrix1, int[] row_perm, double[] matrix2)
+ {
+ int i, ii, ip, j, k;
+ int rp;
+ int cv, rv;
+
+ // rp = row_perm;
+ rp = 0;
+
+ // For each column vector of matrix2 ...
+ for (k = 0; k < 3; k++)
+ {
+ // cv = &(matrix2[0][k]);
+ cv = k;
+ ii = -1;
+
+ // Forward substitution
+ for (i = 0; i < 3; i++)
+ {
+ double sum;
+
+ ip = row_perm[rp + i];
+ sum = matrix2[cv + 3 * ip];
+ matrix2[cv + 3 * ip] = matrix2[cv + 3 * i];
+ if (ii >= 0)
+ {
+ // rv = &(matrix1[i][0]);
+ rv = i * 3;
+ for (j = ii; j <= i - 1; j++)
+ {
+ sum -= matrix1[rv + j] * matrix2[cv + 3 * j];
+ }
+ }
+ else if (sum != 0.0)
+ {
+ ii = i;
+ }
+ matrix2[cv + 3 * i] = sum;
+ }
+
+ // Backsubstitution
+ // rv = &(matrix1[3][0]);
+ rv = 2 * 3;
+ matrix2[cv + 3 * 2] /= matrix1[rv + 2];
+
+ rv -= 3;
+ matrix2[cv + 3 * 1] = (matrix2[cv + 3 * 1] - matrix1[rv + 2] * matrix2[cv + 3 * 2]) / matrix1[rv + 1];
+
+ rv -= 3;
+ matrix2[cv + 4 * 0] = (matrix2[cv + 3 * 0] - matrix1[rv + 1] * matrix2[cv + 3 * 1] - matrix1[rv + 2] * matrix2[cv + 3 * 2])
+ / matrix1[rv + 0];
+
+ }
+ }
+
+ double result4[] = new double[16];
+ int row_perm4[] = new int[4];
+ double[] tmp4 = new double[16]; // scratch matrix
+
+ final void invertGeneral4(Matrix4d thisM, Matrix4d m1)
+ {
+ for (int i = 0; i < 4; i++)
+ row_perm4[i] = 0;
+
+ for (int i = 0; i < 16; i++)
+ result4[i] = 0.0;
+
+ // Use LU decomposition and backsubstitution code specifically
+ // for floating-point 4x4 matrices.
+
+ // Copy source matrix to t1tmp
+ tmp4[0] = m1.m00;
+ tmp4[1] = m1.m01;
+ tmp4[2] = m1.m02;
+ tmp4[3] = m1.m03;
+
+ tmp4[4] = m1.m10;
+ tmp4[5] = m1.m11;
+ tmp4[6] = m1.m12;
+ tmp4[7] = m1.m13;
+
+ tmp4[8] = m1.m20;
+ tmp4[9] = m1.m21;
+ tmp4[10] = m1.m22;
+ tmp4[11] = m1.m23;
+
+ tmp4[12] = m1.m30;
+ tmp4[13] = m1.m31;
+ tmp4[14] = m1.m32;
+ tmp4[15] = m1.m33;
+
+ // Calculate LU decomposition: Is the matrix singular?
+ if (!luDecomposition4(tmp4, row_perm4))
+ {
+ // Matrix has no inverse
+ throw new SingularMatrixException("luDecomposition4(tmp4, row_perm4)");
+ }
+
+ // Perform back substitution on the identity matrix
+ result4[0] = 1.0;
+ result4[5] = 1.0;
+ result4[10] = 1.0;
+ result4[15] = 1.0;
+ luBacksubstitution4(tmp4, row_perm4, result4);
+
+ thisM.m00 = result4[0];
+ thisM.m01 = result4[1];
+ thisM.m02 = result4[2];
+ thisM.m03 = result4[3];
+
+ thisM.m10 = result4[4];
+ thisM.m11 = result4[5];
+ thisM.m12 = result4[6];
+ thisM.m13 = result4[7];
+
+ thisM.m20 = result4[8];
+ thisM.m21 = result4[9];
+ thisM.m22 = result4[10];
+ thisM.m23 = result4[11];
+
+ thisM.m30 = result4[12];
+ thisM.m31 = result4[13];
+ thisM.m32 = result4[14];
+ thisM.m33 = result4[15];
+
+ }
+
+ double row_scale4[] = new double[4];
+
+ boolean luDecomposition4(double[] matrix0, int[] row_perm)
+ {
+ for (int i = 0; i < 4; i++)
+ row_scale4[i] = 0.0;
+ // Determine implicit scaling information by looping over rows
+ {
+ int i, j;
+ int ptr, rs;
+ double big, temp;
+
+ ptr = 0;
+ rs = 0;
+
+ // For each row ...
+ i = 4;
+ while (i-- != 0)
+ {
+ big = 0.0;
+
+ // For each column, find the largest element in the row
+ j = 4;
+ while (j-- != 0)
+ {
+ temp = matrix0[ptr++];
+ temp = Math.abs(temp);
+ if (temp > big)
+ {
+ big = temp;
+ }
+ }
+
+ // Is the matrix singular?
+ if (big == 0.0)
+ {
+ return false;
+ }
+ row_scale4[rs++] = 1.0 / big;
+ }
+ }
+
+ {
+ int j;
+ int mtx;
+
+ mtx = 0;
+
+ // For all columns, execute Crout's method
+ for (j = 0; j < 4; j++)
+ {
+ int i, imax, k;
+ int target, p1, p2;
+ double sum, big, temp;
+
+ // Determine elements of upper diagonal matrix U
+ for (i = 0; i < j; i++)
+ {
+ target = mtx + (4 * i) + j;
+ sum = matrix0[target];
+ k = i;
+ p1 = mtx + (4 * i);
+ p2 = mtx + j;
+ while (k-- != 0)
+ {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 4;
+ }
+ matrix0[target] = sum;
+ }
+
+ // Search for largest pivot element and calculate
+ // intermediate elements of lower diagonal matrix L.
+ big = 0.0;
+ imax = -1;
+ for (i = j; i < 4; i++)
+ {
+ target = mtx + (4 * i) + j;
+ sum = matrix0[target];
+ k = j;
+ p1 = mtx + (4 * i);
+ p2 = mtx + j;
+ while (k-- != 0)
+ {
+ sum -= matrix0[p1] * matrix0[p2];
+ p1++;
+ p2 += 4;
+ }
+ matrix0[target] = sum;
+
+ // Is this the best pivot so far?
+ if ((temp = row_scale4[i] * Math.abs(sum)) >= big)
+ {
+ big = temp;
+ imax = i;
+ }
+ }
+
+ if (imax < 0)
+ {
+ throw new RuntimeException("(imax < 0)");
+ }
+
+ // Is a row exchange necessary?
+ if (j != imax)
+ {
+ // Yes: exchange rows
+ k = 4;
+ p1 = mtx + (4 * imax);
+ p2 = mtx + (4 * j);
+ while (k-- != 0)
+ {
+ temp = matrix0[p1];
+ matrix0[p1++] = matrix0[p2];
+ matrix0[p2++] = temp;
+ }
+
+ // Record change in scale factor
+ row_scale4[imax] = row_scale4[j];
+ }
+
+ // Record row permutation
+ row_perm[j] = imax;
+
+ // Is the matrix singular
+ if (matrix0[(mtx + (4 * j) + j)] == 0.0)
+ {
+ return false;
+ }
+
+ // Divide elements of lower diagonal matrix L by pivot
+ if (j != (4 - 1))
+ {
+ temp = 1.0 / (matrix0[(mtx + (4 * j) + j)]);
+ target = mtx + (4 * (j + 1)) + j;
+ i = 3 - j;
+ while (i-- != 0)
+ {
+ matrix0[target] *= temp;
+ target += 4;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void luBacksubstitution4(double[] matrix1, int[] row_perm, double[] matrix2)
+ {
+
+ int i, ii, ip, j, k;
+ int rp;
+ int cv, rv;
+
+ // rp = row_perm;
+ rp = 0;
+
+ // For each column vector of matrix2 ...
+ for (k = 0; k < 4; k++)
+ {
+ // cv = &(matrix2[0][k]);
+ cv = k;
+ ii = -1;
+
+ // Forward substitution
+ for (i = 0; i < 4; i++)
+ {
+ double sum;
+
+ ip = row_perm[rp + i];
+ sum = matrix2[cv + 4 * ip];
+ matrix2[cv + 4 * ip] = matrix2[cv + 4 * i];
+ if (ii >= 0)
+ {
+ // rv = &(matrix1[i][0]);
+ rv = i * 4;
+ for (j = ii; j <= i - 1; j++)
+ {
+ sum -= matrix1[rv + j] * matrix2[cv + 4 * j];
+ }
+ }
+ else if (sum != 0.0)
+ {
+ ii = i;
+ }
+ matrix2[cv + 4 * i] = sum;
+ }
+
+ // Backsubstitution
+ // rv = &(matrix1[3][0]);
+ rv = 3 * 4;
+ matrix2[cv + 4 * 3] /= matrix1[rv + 3];
+
+ rv -= 4;
+ matrix2[cv + 4 * 2] = (matrix2[cv + 4 * 2] - matrix1[rv + 3] * matrix2[cv + 4 * 3]) / matrix1[rv + 2];
+
+ rv -= 4;
+ matrix2[cv + 4 * 1] = (matrix2[cv + 4 * 1] - matrix1[rv + 2] * matrix2[cv + 4 * 2] - matrix1[rv + 3] * matrix2[cv + 4 * 3])
+ / matrix1[rv + 1];
+
+ rv -= 4;
+ matrix2[cv + 4 * 0] = (matrix2[cv + 4 * 0] - matrix1[rv + 1] * matrix2[cv + 4 * 1] - matrix1[rv + 2] * matrix2[cv + 4 * 2]
+ - matrix1[rv + 3] * matrix2[cv + 4 * 3]) / matrix1[rv + 0];
+ }
+ }
+
+ //Oh lordy lordy yo' betta swear yo' single freadin' !!!
+
+ public double[] deburnV2 = null;//deburners
+
+ public Matrix4d deburnV = new Matrix4d();//deburners
+ public Matrix4d deburnM = new Matrix4d();
+ public float[] tempMat9 = new float[9];
+ public float[] tempMat12 = new float[12];
+ public float[] tempMat16 = new float[16];
+ public double[] tempMatD9 = new double[9];
+
+ public float[] toArray(Matrix4d m)
+ {
+ tempMat16[0] = (float) m.m00;
+ tempMat16[1] = (float) m.m01;
+ tempMat16[2] = (float) m.m02;
+ tempMat16[3] = (float) m.m03;
+ tempMat16[4] = (float) m.m10;
+ tempMat16[5] = (float) m.m11;
+ tempMat16[6] = (float) m.m12;
+ tempMat16[7] = (float) m.m13;
+ tempMat16[8] = (float) m.m20;
+ tempMat16[9] = (float) m.m21;
+ tempMat16[10] = (float) m.m22;
+ tempMat16[11] = (float) m.m23;
+ tempMat16[12] = (float) m.m30;
+ tempMat16[13] = (float) m.m31;
+ tempMat16[14] = (float) m.m32;
+ tempMat16[15] = (float) m.m33;
+ return tempMat16;
+ }
+
+ public static float[] toArray(Matrix4d m, float[] a)
+ {
+ a[0] = (float) m.m00;
+ a[1] = (float) m.m01;
+ a[2] = (float) m.m02;
+ a[3] = (float) m.m03;
+ a[4] = (float) m.m10;
+ a[5] = (float) m.m11;
+ a[6] = (float) m.m12;
+ a[7] = (float) m.m13;
+ a[8] = (float) m.m20;
+ a[9] = (float) m.m21;
+ a[10] = (float) m.m22;
+ a[11] = (float) m.m23;
+ a[12] = (float) m.m30;
+ a[13] = (float) m.m31;
+ a[14] = (float) m.m32;
+ a[15] = (float) m.m33;
+
+ return a;
+ }
+
+ public float[] toArray(Matrix3d m)
+ {
+ tempMat9[0] = (float) m.m00;
+ tempMat9[1] = (float) m.m01;
+ tempMat9[2] = (float) m.m02;
+ tempMat9[3] = (float) m.m10;
+ tempMat9[4] = (float) m.m11;
+ tempMat9[5] = (float) m.m12;
+ tempMat9[6] = (float) m.m20;
+ tempMat9[7] = (float) m.m21;
+ tempMat9[8] = (float) m.m22;
+ return tempMat9;
+
+ }
+
+ public static float[] toArray(Matrix3d m, float[] a)
+ {
+ a[0] = (float) m.m00;
+ a[1] = (float) m.m01;
+ a[2] = (float) m.m02;
+ a[3] = (float) m.m10;
+ a[4] = (float) m.m11;
+ a[5] = (float) m.m12;
+ a[6] = (float) m.m20;
+ a[7] = (float) m.m21;
+ a[8] = (float) m.m22;
+
+ return a;
+ }
+
+ public float[] toArray3x4(Matrix3d m)
+ {
+ return toArray3x4(m, tempMat12);
+ }
+
+ public static float[] toArray3x4(Matrix3d m, float[] a)
+ {
+ a[0] = (float) m.m00;
+ a[1] = (float) m.m01;
+ a[2] = (float) m.m02;
+ a[3] = 0f;
+ a[4] = (float) m.m10;
+ a[5] = (float) m.m11;
+ a[6] = (float) m.m12;
+ a[7] = 0f;
+ a[8] = (float) m.m20;
+ a[9] = (float) m.m21;
+ a[10] = (float) m.m22;
+ a[11] = 0f;
+
+ return a;
+ }
+
+ public double[] toArray3x3(Matrix4d m)
+ {
+ return toArray3x3(m, tempMatD9);
+ }
+
+ public static double[] toArray3x3(Matrix4d m, double[] a)
+ {
+ a[0] = m.m00;
+ a[1] = m.m01;
+ a[2] = m.m02;
+ a[3] = m.m10;
+ a[4] = m.m11;
+ a[5] = m.m12;
+ a[6] = m.m20;
+ a[7] = m.m21;
+ a[8] = m.m22;
+
+ return a;
+ }
+
+ public void invert(Matrix3d m1)
+ {
+ try
+ {
+ invertGeneral3(m1, m1);
+ }
+ catch (Exception e)
+ {
+ //fine, move along
+ m1.setIdentity();
+ }
+ }
+
+ public void invert(Matrix4d m1)
+ {
+ try
+ {
+ invertGeneral4(m1, m1);
+ }
+ catch (Exception e)
+ {
+ //fine, move along
+ m1.setIdentity();
+ }
+ }
+
+ //More single threaded death-defying gear
+
+ private FloatBuffer matFB4x4;
+
+ public FloatBuffer toFB4(float[] f)
+ {
+ if (matFB4x4 == null)
+ {
+ ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
+ bb.order(ByteOrder.nativeOrder());
+ matFB4x4 = bb.asFloatBuffer();
+ }
+ matFB4x4.position(0);
+ matFB4x4.put(f);
+ matFB4x4.position(0);
+ return matFB4x4;
+ }
+
+ public FloatBuffer toFB3(float[] f)
+ {
+ if (matFB3x3 == null)
+ {
+ ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
+ bb.order(ByteOrder.nativeOrder());
+ matFB3x3 = bb.asFloatBuffer();
+ }
+ matFB3x3.position(0);
+ matFB3x3.put(f);
+ matFB3x3.position(0);
+ return matFB3x3;
+ }
+
+ public FloatBuffer toFB(Matrix4d m)
+ {
+ if (matFB4x4 == null)
+ {
+ ByteBuffer bb = ByteBuffer.allocateDirect(16 * 4);
+ bb.order(ByteOrder.nativeOrder());
+ matFB4x4 = bb.asFloatBuffer();
+ }
+ matFB4x4.position(0);
+ matFB4x4.put(toArray(m));
+ matFB4x4.position(0);
+ return matFB4x4;
+ }
+
+ private FloatBuffer matFB3x3;
+
+ public FloatBuffer toFB(Matrix3d m)
+ {
+ if (matFB3x3 == null)
+ {
+ ByteBuffer bb = ByteBuffer.allocateDirect(9 * 4);
+ bb.order(ByteOrder.nativeOrder());
+ matFB3x3 = bb.asFloatBuffer();
+ }
+ matFB3x3.position(0);
+ matFB3x3.put(toArray(m));
+ matFB3x3.position(0);
+ return matFB3x3;
+ }
+
+ // Not needed generally as transpose can be called on the inteface with gl
+ public static float[] transposeInPlace(float[] src)
+ {
+ float v1 = src[1];
+ float v2 = src[2];
+ float v3 = src[3];
+ float v6 = src[6];
+ float v7 = src[7];
+ float v11 = src[11];
+
+ //src[0] = src[0];
+ src[1] = src[4];
+ src[2] = src[8];
+ src[3] = src[12];
+ src[4] = v1;
+ //src[5] = src[5];
+ src[6] = src[9];
+ src[7] = src[13];
+ src[8] = v2;
+ src[9] = v6;
+ //src[10] = src[10];
+ src[11] = src[14];
+ src[12] = v3;
+ src[13] = v7;
+ src[14] = v11;
+ //src[15] = src[15];
+
+ return src;
+ }
+
+ // ignores the higher 16 bits
+ public static float halfToFloat(int hbits)
+ {
+ int mant = hbits & 0x03ff; // 10 bits mantissa
+ int exp = hbits & 0x7c00; // 5 bits exponent
+ if (exp == 0x7c00) // NaN/Inf
+ exp = 0x3fc00; // -> NaN/Inf
+ else if (exp != 0) // normalized value
+ {
+ exp += 0x1c000; // exp - 15 + 127
+ if (mant == 0 && exp > 0x1c400) // smooth transition
+ return Float.intBitsToFloat((hbits & 0x8000) << 16 | exp << 13 | 0x3ff);
+ }
+ else if (mant != 0) // && exp==0 -> subnormal
+ {
+ exp = 0x1c400; // make it normal
+ do
+ {
+ mant <<= 1; // mantissa * 2
+ exp -= 0x400; // decrease exp by 1
+ }
+ while ((mant & 0x400) == 0); // while not normal
+ mant &= 0x3ff; // discard subnormal bit
+ } // else +/-0 -> +/-0
+ return Float.intBitsToFloat( // combine all parts
+ (hbits & 0x8000) << 16 // sign << ( 31 - 15 )
+ | (exp | mant) << 13); // value << ( 23 - 10 )
+ }
+
+ // returns all higher 16 bits as 0 for all results
+ public static int halfFromFloat(float fval)
+ {
+ int fbits = Float.floatToIntBits(fval);
+ int sign = fbits >>> 16 & 0x8000; // sign only
+ int val = (fbits & 0x7fffffff) + 0x1000; // rounded value
+
+ if (val >= 0x47800000) // might be or become NaN/Inf
+ { // avoid Inf due to rounding
+ if ((fbits & 0x7fffffff) >= 0x47800000)
+ { // is or must become NaN/Inf
+ if (val < 0x7f800000) // was value but too large
+ return sign | 0x7c00; // make it +/-Inf
+ return sign | 0x7c00 | // remains +/-Inf or NaN
+ (fbits & 0x007fffff) >>> 13; // keep NaN (and Inf) bits
+ }
+ return sign | 0x7bff; // unrounded not quite Inf
+ }
+ if (val >= 0x38800000) // remains normalized value
+ return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
+ if (val < 0x33000000) // too small for subnormal
+ return sign; // becomes +/-0
+ val = (fbits & 0x7fffffff) >>> 23; // tmp exp for subnormal calc
+ return sign | ((fbits & 0x7fffff | 0x800000) // add subnormal bit
+ + (0x800000 >>> val - 102) // round depending on cut off
+ >>> 126 - val); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
+ }
+}
\ No newline at end of file diff --git a/src/main/java/org/jogamp/java3d/Jogl2es2PerFrameStats.java b/src/main/java/org/jogamp/java3d/Jogl2es2PerFrameStats.java new file mode 100644 index 0000000..ec9647a --- /dev/null +++ b/src/main/java/org/jogamp/java3d/Jogl2es2PerFrameStats.java @@ -0,0 +1,208 @@ +package org.jogamp.java3d;
+
+import java.util.HashSet;
+
+public class Jogl2es2PerFrameStats
+{
+ public long endOfPrevFrameTime;
+
+ public HashSet<ShaderProgramId> usedPrograms = new HashSet<ShaderProgramId>();
+ //public ArrayList<ShaderProgramId> usedPrograms = new ArrayList<ShaderProgramId>();
+ //public HashSet<String> usedProgramNames = new HashSet<String>();
+ //TODO: how do I get these?
+ //public HashMap<ShaderProgramId, String> usedProgramNames = new HashMap<ShaderProgramId, String>();
+
+ public int geoToClearBuffers;
+ public int glDrawStripArrays;
+ public int glDrawStripArraysStrips;
+ public int glDrawArrays;
+ public int glDrawStripElements;
+ public int glDrawStripElementsStrips;
+ public int glDrawElements;
+ public int setFFPAttributes;
+ public int programToLocationData;
+ public int enableTexCoordPointer;
+ public int createGLSLShader;
+ public int createGLSLShaderProgram;
+ public int compileGLSLShader;
+ public int destroyGLSLShader;
+ public int destroyGLSLShaderProgram;
+ public int linkGLSLShaderProgram;
+ public int useGLSLShaderProgram;
+ public int bindGLSLVertexAttrName;
+ public int lookupGLSLShaderAttrNames;
+ public int updateDirectionalLight;
+ public int updatePointLight;
+ public int updateSpotLight;
+ public int updateExponentialFog;
+ public int updateLinearFog;
+ public int disableFog;
+ public int setFogEnableFlag;
+ public int updateLineAttributes;
+ public int resetLineAttributes;
+ public int updateMaterial;
+ public int updateMaterialColor;
+ public int updateColoringAttributes;
+ public int resetColoringAttributes;
+ public int updatePointAttributes;
+ public int resetPointAttributes;
+ public int updatePolygonAttributes;
+ public int resetPolygonAttributes;
+ public int updateRenderingAttributes;
+ public int resetRenderingAttributes;
+ public int updateTransparencyAttributes;
+ public int resetTransparency;
+ public int updateTextureAttributes;
+ public int resetTextureAttributes;
+ public int resetTexCoordGeneration;
+ public int updateTextureUnitState;
+ public int bindTexture2D;
+ public int bindTextureCubeMap;
+ public int setBlendColor;
+ public int setBlendFunc;
+ public int setFullSceneAntialiasing;
+ public int setLightEnables;
+ public int setSceneAmbient;
+ public int activeTextureUnit;
+ public int resetTextureNative;
+ public int useCtx;
+ public int releaseCtx;
+ public int clear;
+ public int setModelViewMatrix;
+ public int setProjectionMatrix;
+ public int setViewport;
+ public int freeTexture;
+ public int generateTexID;
+ public int setDepthBufferWriteEnable;
+ public int redundantUseProgram;
+
+ public int coordCount;
+ public int indexCount;
+ public int glVertexAttribPointerNormals;
+ public int glVertexAttribPointerUserAttribs;
+ public int glVertexAttribPointerColor;
+ public int glVertexAttribPointerCoord;
+ public int glBufferData;
+ public int glBufferSubData;
+ public int glDisableVertexAttribArray;
+
+ public int modelMatrixUpdated;
+ public int glModelViewMatrixUpdated;
+ public int glModelViewProjectionMatrixUpdated;
+ public int glNormalMatrixUpdated;
+ public int glModelViewMatrixInverseUpdated;
+
+ public int modelMatrixSkipped;
+ public int glModelViewMatrixSkipped;
+ public int glModelViewProjectionMatrixSkipped;
+ public int glNormalMatrixSkipped;
+ public int glModelViewMatrixInverseSkipped;
+
+ public int interleavedBufferCreated;
+
+ public int glVertexAttribPointerInterleaved;
+
+ public long setViewportTime;
+
+ public long syncRenderTime;
+
+ public void outputPerFrameData()
+ {
+ boolean highInterestOnly = true;
+
+ System.out.println("coordCount " + coordCount + " indexCount " + indexCount);
+ System.out.println("glDrawStripArrays " + glDrawStripArrays + "\t made up of glDrawStripArraysStrips " + glDrawStripArraysStrips);
+ System.out.println("glDrawArrays " + glDrawArrays);
+ System.out.println(
+ "glDrawStripElements " + glDrawStripElements + "\t made up of glDrawStripElementsStrips " + glDrawStripElementsStrips);
+ System.out.println("glDrawElements " + glDrawElements);
+ System.out.println("glVertexAttribPointerCoord " + glVertexAttribPointerCoord);
+ System.out.println("glVertexAttribPointerNormals " + glVertexAttribPointerNormals);
+ System.out.println("glVertexAttribPointerColor " + glVertexAttribPointerColor);
+ System.out.println("glVertexAttribPointerUserAttribs " + glVertexAttribPointerUserAttribs);
+ System.out.println("enableTexCoordPointer " + enableTexCoordPointer);
+ System.out.println("glBufferData " + glBufferData + " glBufferSubData " + glBufferSubData);
+ System.out.println("glVertexAttribPointerInterleaved " + glVertexAttribPointerInterleaved);
+ System.out.println("interleavedBufferCreated " + interleavedBufferCreated);
+ System.out.println("---");
+ System.out.println("setModelViewMatrix " + setModelViewMatrix);
+ System.out.println("setFFPAttributes " + setFFPAttributes);
+ System.out.println("modelMatrixUpdated " + modelMatrixUpdated + " modelMatrixSkipped " + modelMatrixSkipped);
+ System.out
+ .println("glModelViewMatrixUpdated " + glModelViewMatrixUpdated + " glModelViewMatrixSkipped " + glModelViewMatrixSkipped);
+ System.out.println("glModelViewProjectionMatrixUpdated " + glModelViewProjectionMatrixUpdated
+ + " glModelViewProjectionMatrixSkipped " + glModelViewProjectionMatrixSkipped);
+ System.out.println("glNormalMatrixUpdated " + glNormalMatrixUpdated + " glNormalMatrixSkipped " + glNormalMatrixSkipped);
+ System.out.println("---");
+ if (!highInterestOnly)
+ {
+ System.out.println("glDisableVertexAttribArray " + glDisableVertexAttribArray + " note native called commented out, trouble?");
+ System.out.println("geoToClearBuffers " + geoToClearBuffers);
+ System.out.println("programToLocationData " + programToLocationData);
+ System.out.print("createGLSLShader " + createGLSLShader);
+ System.out.print("\tcreateGLSLShaderProgram " + createGLSLShaderProgram);
+ System.out.print("\tcompileGLSLShader " + compileGLSLShader);
+ System.out.print("\tdestroyGLSLShader " + destroyGLSLShader);
+ System.out.print("\tdestroyGLSLShaderProgram " + destroyGLSLShaderProgram);
+ System.out.print("\tlinkGLSLShaderProgram " + linkGLSLShaderProgram);
+ System.out.print("\tbindGLSLVertexAttrName " + bindGLSLVertexAttrName);
+ System.out.println("\tlookupGLSLShaderAttrNames " + lookupGLSLShaderAttrNames);
+ System.out.print("updateDirectionalLight " + updateDirectionalLight);
+ System.out.print("\tupdatePointLight " + updatePointLight);
+ System.out.println("\tupdateSpotLight " + updateSpotLight);
+ System.out.print("updateExponentialFog " + updateExponentialFog);
+ System.out.print("\tupdateLinearFog " + updateLinearFog);
+ System.out.print("\tdisableFog " + disableFog);
+ System.out.println("\tsetFogEnableFlag " + setFogEnableFlag);
+ System.out.print("updateLineAttributes " + updateLineAttributes);
+ System.out.println("\tresetLineAttributes " + resetLineAttributes);
+ System.out.print("updateMaterial " + updateMaterial);
+ System.out.println("\tupdateMaterialColor " + updateMaterialColor);
+ System.out.print("updateColoringAttributes " + updateColoringAttributes);
+ System.out.println("\tresetColoringAttributes " + resetColoringAttributes);
+ System.out.print("updatePointAttributes " + updatePointAttributes);
+ System.out.println("\tresetPointAttributes " + resetPointAttributes);
+ System.out.print("updatePolygonAttributes " + updatePolygonAttributes);
+ System.out.println("\tresetPolygonAttributes " + resetPolygonAttributes);
+ System.out.print("updateRenderingAttributes " + updateRenderingAttributes);
+ System.out.println("\tresetRenderingAttributes " + resetRenderingAttributes);
+ System.out.println("setBlendColor " + setBlendColor);
+ System.out.println("setFullSceneAntialiasing " + setFullSceneAntialiasing);
+ System.out.println("setLightEnables " + setLightEnables);
+ System.out.println("setSceneAmbient " + setSceneAmbient);
+ System.out.println("resetTexCoordGeneration " + resetTexCoordGeneration);
+ System.out.println("freeTexture " + freeTexture);
+ System.out.println("generateTexID " + generateTexID);
+ System.out.println("useCtx " + useCtx);
+ System.out.println("releaseCtx " + releaseCtx);
+ System.out.println("clear " + clear);
+ System.out.println("setViewport " + setViewport);
+ System.out.println("setProjectionMatrix " + setProjectionMatrix);
+ }
+
+ System.out.print("updateTransparencyAttributes " + updateTransparencyAttributes);
+ System.out.println("\tresetTransparency " + resetTransparency);
+ System.out.print("updateTextureAttributes " + updateTextureAttributes);
+ System.out.println("\tresetTextureAttributes " + resetTextureAttributes);
+ System.out.println("updateTextureUnitState " + updateTextureUnitState);
+ System.out.println("bindTexture2D " + bindTexture2D + "\tbindTextureCubeMap " + bindTextureCubeMap);
+ System.out.println("setBlendFunc " + setBlendFunc);
+ System.out.println("activeTextureUnit " + activeTextureUnit + "\tresetTextureNative " + resetTextureNative);
+ System.out.println("setDepthBufferWriteEnable " + setDepthBufferWriteEnable);
+ System.out.println("useGLSLShaderProgram " + useGLSLShaderProgram + " redundantUseProgram " + redundantUseProgram);
+
+ //for (ShaderProgramId id : usedPrograms)
+ // System.out.println("ShaderProgramId " + ((JoglShaderObject) id).getValue());
+ if ((syncRenderTime - setViewportTime) != 0)
+ {
+ System.out.println("time in frame (not in glFinish) " + (syncRenderTime - setViewportTime) + //
+ " = (ms) " + ((syncRenderTime - setViewportTime) / 1000000L));// + //
+ // " = fps: " + (1000 / ((syncRenderTime - setViewportTime) / 1000000L)));
+ }
+
+ long now = System.nanoTime();
+ System.out.println("time since end of previous frame (ns) " + (now - endOfPrevFrameTime) + //
+ " = (ms) " + ((now - endOfPrevFrameTime) / 1000000L) + //
+ " = fps: " + (1000 / ((now - endOfPrevFrameTime) / 1000000L)));
+ }
+}
\ No newline at end of file diff --git a/src/main/java/org/jogamp/java3d/Jogl2es2Pipeline.java b/src/main/java/org/jogamp/java3d/Jogl2es2Pipeline.java new file mode 100644 index 0000000..2aef9fa --- /dev/null +++ b/src/main/java/org/jogamp/java3d/Jogl2es2Pipeline.java @@ -0,0 +1,7858 @@ +package org.jogamp.java3d; + +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.GraphicsConfigTemplate; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jogamp.java3d.Jogl2es2Context.GeometryData; +import org.jogamp.java3d.Jogl2es2Context.LightData; +import org.jogamp.java3d.Jogl2es2Context.LocationData; +import org.jogamp.java3d.Jogl2es2Context.ProgramData; +import org.jogamp.vecmath.SingularMatrixException; +import org.jogamp.vecmath.Vector4f; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.AbstractGraphicsScreen; +import com.jogamp.nativewindow.CapabilitiesChooser; +import com.jogamp.nativewindow.GraphicsConfigurationFactory; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.ProxySurface; +import com.jogamp.nativewindow.UpstreamSurfaceHook; +import com.jogamp.nativewindow.VisualIDHolder; +import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; +import com.jogamp.nativewindow.awt.AWTGraphicsDevice; +import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.nativewindow.awt.JAWTWindow; +import com.jogamp.opengl.DefaultGLCapabilitiesChooser; +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GL2ES2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL3; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLCapabilitiesChooser; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLFBODrawable; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.Threading; + +/** + * Concrete implementation of Pipeline class for the JOGL rendering pipeline. + */ +class Jogl2es2Pipeline extends Jogl2es2DEPPipeline +{ + private static final boolean DO_OUTPUT_ERRORS = false; + // Currently prints for entry points already implemented + static final boolean VERBOSE = false; + // Debugging output for graphics configuration selection + private static final boolean DEBUG_CONFIG = false; + + private static final boolean OUTPUT_PER_FRAME_STATS = false; + + private static final boolean MINIMISE_NATIVE_CALLS_FFP = true; + + private static final boolean MINIMISE_NATIVE_CALLS_TRANSPARENCY = true; + private static final boolean MINIMISE_NATIVE_CALLS_TEXTURE = true; + + private static final boolean MINIMISE_NATIVE_SHADER = true; + private static final boolean MINIMISE_NATIVE_CALLS_OTHER = true; + + private static final boolean NEVER_RELEASE_CONTEXT = true; + + /** + * Constructor for singleton JoglPipeline instance + */ + protected Jogl2es2Pipeline() + { + + } + + /** + * Initialize the pipeline + */ + @Override + void initialize(Pipeline.Type pipelineType) + { + super.initialize(pipelineType); + + // Java3D maintains strict control over which threads perform OpenGL work + Threading.disableSingleThreading(); + + // profile = GLProfile.get(GLProfile.GLES2); // Profile GLES2 is not available on null, but: [GLProfile[GL3bc/GL4bc.hw],... + profile = GLProfile.get(GLProfile.GL2ES2); + // profile = GLProfile.getMaxProgrammable(true); + + } + + // --------------------------------------------------------------------- + + // + // GeometryArrayRetained methods + // + + // FIXME: big ugly hack for buffer clearing on removal of a geometry + public void registerClearBuffers(Context ctx, GeometryArrayRetained geo) + { + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + synchronized (joglesctx.geoToClearBuffers) + { + joglesctx.geoToClearBuffers.add(geo); + } + } + + private static void doClearBuffers(Context ctx) + { + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesctx.gl2es2; + if (joglesctx.geoToClearBuffers.size() > 0) + { + synchronized (joglesctx.geoToClearBuffers) + { + for (GeometryArrayRetained geo : joglesctx.geoToClearBuffers) + { + GeometryData gd = joglesctx.allGeometryData.get(geo.nativeId); + joglesctx.allGeometryData.remove(geo.nativeId); + geo.nativeId = -1; + + // TODO: why exactly is the same geo being removed twice? + if (gd != null) + { + if (gd.geoToIndBuf != -1) + gl.glDeleteBuffers(1, new int[] { gd.geoToIndBuf }, 0); + if (gd.geoToCoordBuf != -1) + gl.glDeleteBuffers(1, new int[] { gd.geoToCoordBuf }, 0); + if (gd.geoToColorBuf != -1) + gl.glDeleteBuffers(1, new int[] { gd.geoToColorBuf }, 0); + if (gd.geoToNormalBuf != -1) + gl.glDeleteBuffers(1, new int[] { gd.geoToNormalBuf }, 0); + + int[] bufIds = gd.geoToIndStripBuf; + if (bufIds != null && bufIds.length > 0) + { + gl.glDeleteBuffers(bufIds.length, bufIds, 0); + } + + HashMap<Integer, Integer> tcBufIds = gd.geoToTexCoordsBuf; + if (tcBufIds != null) + { + + for (Integer tcBufId : tcBufIds.keySet()) + { + + if (tcBufId != null) + gl.glDeleteBuffers(1, new int[] { tcBufId.intValue() }, 0); + } + tcBufIds.clear(); + } + + HashMap<Integer, Integer> vaBufIds = gd.geoToVertAttribBuf; + if (vaBufIds != null) + { + + for (Integer vaBufId : vaBufIds.keySet()) + { + if (vaBufId != null) + gl.glDeleteBuffers(1, new int[] { vaBufId.intValue() }, 0); + } + vaBufIds.clear(); + } + + if (gd.interleavedBufId != -1) + gl.glDeleteBuffers(1, new int[] { gd.interleavedBufId }, 0); + + if (gd.vaoId != -1) + ((GL2ES3) gl).glDeleteVertexArrays(1, new int[] { gd.vaoId }, 0); + + } + + } + + if (OUTPUT_PER_FRAME_STATS) + joglesctx.perFrameStats.geoToClearBuffers = joglesctx.geoToClearBuffers.size(); + + joglesctx.geoToClearBuffers.clear(); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + } + + // used by GeometryArray by Reference with NIO buffer + // non indexed + + @Override + void executeVABuffer(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean ignoreVertexColors, + int vcount, int vformat, int vdefined, int initialCoordIndex, Buffer vcoords, int initialColorIndex, Buffer cdataBuffer, + float[] cfdata, byte[] cbdata, int initialNormalIndex, FloatBuffer ndata, int vertexAttrCount, int[] vertexAttrSizes, + int[] vertexAttrIndices, FloatBuffer[] vertexAttrData, int texCoordMapLength, int[] texcoordoffset, int numActiveTexUnitState, + int[] texIndex, int texstride, Object[] texCoords, int cdirty) + { + + if (VERBOSE) + System.err.println("JoglPipeline.executeVABuffer() "); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) + vertexAttrBufs = vertexAttrData; + + // get coordinate array + if (floatCoordDefined) + { + fverts = (FloatBuffer) vcoords; + } + else if (doubleCoordDefined) + { + // FIXME: doubles not supported for now + throw new UnsupportedOperationException("Double coordinates in use.\n" + VALID_FORMAT_MESSAGE); + // dverts = (DoubleBuffer) vcoords; + } + + if (fverts == null && dverts == null) + { + return; + } + + // get color array + if (floatColorsDefined) + { + if (cdataBuffer != null) + fclrs = (FloatBuffer) cdataBuffer; + else + fclrs = getColorArrayBuffer(cfdata); + } + else if (byteColorsDefined) + { + // FIXME: doubles not supported for now + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + // if (cbdata != null) + // bclrs = getColorArrayBuffer(cbdata); + // else + // bclrs = (ByteBuffer) cdataBuffer; + } + + // get normal array + if (normalsDefined) + { + norms = ndata; + } + + int[] sarray = null; + int[] start_array = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) + { + sarray = ((GeometryStripArrayRetained) geo).stripVertexCounts; + strip_len = sarray.length; + start_array = ((GeometryStripArrayRetained) geo).stripStartOffsetIndices; + } + + executeGeometryArrayVA(ctx, geo, geo_type, isNonUniformScale, ignoreVertexColors, vcount, vformat, vdefined, initialCoordIndex, + fverts, dverts, initialColorIndex, fclrs, bclrs, initialNormalIndex, norms, vertexAttrCount, vertexAttrSizes, + vertexAttrIndices, vertexAttrBufs, texCoordMapLength, texcoordoffset, numActiveTexUnitState, texIndex, texstride, texCoords, + cdirty, sarray, strip_len, start_array); + } + + // used by GeometryArray by Reference with java arrays + // non indexed + @Override + @Deprecated + void executeVA(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, boolean ignoreVertexColors, int vcount, + int vformat, int vdefined, int initialCoordIndex, float[] vfcoords, double[] vdcoords, int initialColorIndex, float[] cfdata, + byte[] cbdata, int initialNormalIndex, float[] ndata, int vertexAttrCount, int[] vertexAttrSizes, int[] vertexAttrIndices, + float[][] vertexAttrData, int texCoordMapLength, int[] texcoordoffset, int numActiveTexUnitState, int[] texIndex, int texstride, + Object[] texCoords, int cdirty) + { + if (VERBOSE) + System.err.println("JoglPipeline.executeVA()"); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + FloatBuffer[] texCoordBufs = null; + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) + { + vertexAttrBufs = getVertexAttrSetBuffer(vertexAttrData); + } + + // get texture arrays + if (textureDefined) + { + texCoordBufs = getTexCoordSetBuffer(texCoords); + } + + int[] sarray = null; + int[] start_array = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) + { + sarray = ((GeometryStripArrayRetained) geo).stripVertexCounts; + strip_len = sarray.length; + start_array = ((GeometryStripArrayRetained) geo).stripStartOffsetIndices; + } + + // get coordinate array + if (floatCoordDefined) + { + fverts = getVertexArrayBuffer(vfcoords); + } + else if (doubleCoordDefined) + { + // FIXME: doubles not supported for now + throw new UnsupportedOperationException("doubleCoordDefined.\n" + VALID_FORMAT_MESSAGE); + // dverts = getVertexArrayBuffer(vdcoords); + } + + // get color array + if (floatColorsDefined) + { + fclrs = getColorArrayBuffer(cfdata); + } + else if (byteColorsDefined) + { + // FIXME: byte colors not supported for now + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + // bclrs = getColorArrayBuffer(cbdata); + } + + // get normal array + if (normalsDefined) + { + norms = getNormalArrayBuffer(ndata); + } + + executeGeometryArrayVA(ctx, geo, geo_type, isNonUniformScale, ignoreVertexColors, vcount, vformat, vdefined, initialCoordIndex, + fverts, dverts, initialColorIndex, fclrs, bclrs, initialNormalIndex, norms, vertexAttrCount, vertexAttrSizes, + vertexAttrIndices, vertexAttrBufs, texCoordMapLength, texcoordoffset, numActiveTexUnitState, texIndex, texstride, + texCoordBufs, cdirty, sarray, strip_len, start_array); + } + + private void executeGeometryArrayVA(Context absCtx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, + boolean ignoreVertexColors, int vertexCount, int vformat, int vdefined, int initialCoordIndex, FloatBuffer fverts, + DoubleBuffer dverts, int initialColorIndex, FloatBuffer fclrs, ByteBuffer bclrs, int initialNormalIndex, FloatBuffer norms, + int vertexAttrCount, int[] vertexAttrSizes, int[] vertexAttrIndices, FloatBuffer[] vertexAttrBufs, int texCoordMapLength, + int[] texCoordSetMap, int numActiveTexUnitState, int[] texindices, int texStride, Object[] texCoords, int cDirty, int[] sarray, + int strip_len, int[] start_array) + { + Jogl2es2Context ctx = (Jogl2es2Context) absCtx; + int shaderProgramId = ctx.shaderProgramId; + + if (shaderProgramId != -1) + { + GL2ES2 gl = ctx.gl2es2; + ProgramData pd = ctx.programData; + LocationData locs = pd.programToLocationData; + + setFFPAttributes(ctx, gl, shaderProgramId, pd, vdefined); + + // If any buffers need loading do that now and skip a render for + // this frame + GeometryData gd = loadAllBuffers(ctx, gl, geo, ignoreVertexColors, vertexCount, vformat, vdefined, fverts, dverts, fclrs, bclrs, + norms, vertexAttrCount, vertexAttrSizes, vertexAttrBufs, texCoordMapLength, texCoordSetMap, texStride, texCoords); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + // can it change ever? (GeometryArray.ALLOW_REF_DATA_WRITE is just my indicator of this feature) + boolean morphable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_REF_DATA_WRITE)) != 0L + || (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_COORDINATE_WRITE)) != 0L; + + // not required second time around for VAO (except morphable coords) + boolean bindingRequired = true; + if (ctx.gl2es3 != null) + { + if (gd.vaoId == -1) + { + int[] tmp = new int[1]; + ctx.gl2es3.glGenVertexArrays(1, tmp, 0); + gd.vaoId = tmp[0]; + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + bindingRequired = false; + } + ctx.gl2es3.glBindVertexArray(gd.vaoId); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // Define the data pointers + if (locs.glVertex != -1) + { + if (floatCoordDefined) + { + + if (gd.geoToCoordBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + + // a good cDirty + // if ((cDirty & GeometryArrayRetained.COORDINATE_CHANGED) != 0) + if (morphable) + { + int coordoff = 3 * initialCoordIndex; + fverts.position(coordoff); + // Sometime the FloatBuffer is swapped out for bigger or smaller! or is that ok? + if (gd.geoToCoordBufSize != fverts.remaining()) + { + System.err.println("Morphable buffer changed " + gd.geoToCoordBufSize + " != " + fverts.remaining() + + " un indexed ((GeometryArray) geo.source) " + ((GeometryArray) geo.source).getName() + " " + + geo.source + ", this is not nessasarily a problem"); + + int prevBufId1 = gd.geoToCoordBuf1;// record to delete after re-bind + int prevBufId2 = gd.geoToCoordBuf2; + + int[] tmp = new int[2]; + gl.glGenBuffers(2, tmp, 0); + gd.geoToCoordBuf = tmp[0]; + gd.geoToCoordBuf1 = tmp[0]; + gd.geoToCoordBuf2 = tmp[1]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf1); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, + GL2ES2.GL_DYNAMIC_DRAW); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf2); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, + GL2ES2.GL_DYNAMIC_DRAW); + + gd.geoToCoordBufSize = fverts.remaining(); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + //Notice no check for bindingRequired as we are altering the binding + //and previously used buffer is deleted AFTER re-bind + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf); + gl.glVertexAttribPointer(locs.glVertex, 3, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glVertex); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerCoord++; + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.coordCount += gd.geoToCoordBufSize; + + gl.glDeleteBuffers(1, new int[] { prevBufId1, prevBufId2 }, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + // work out the buffer to update and buffer to swap to + if (gd.geoToCoordBuf == gd.geoToCoordBuf1) + { + // update 1 but set to draw 2 + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf1); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, (fverts.remaining() * Float.SIZE / 8), fverts); + gd.geoToCoordBuf = gd.geoToCoordBuf2; + } + else + { + // update 2 but set to draw 1 + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf2); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, (fverts.remaining() * Float.SIZE / 8), fverts); + gd.geoToCoordBuf = gd.geoToCoordBuf1; + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferSubData++; + } + } + + } + + } + else if (doubleCoordDefined) + { + throw new UnsupportedOperationException("doubleCoordDefined.\n" + VALID_FORMAT_MESSAGE); + } + else + { + throw new UnsupportedOperationException("No coords defined.\n" + VALID_FORMAT_MESSAGE); + } + } + else + { + throw new UnsupportedOperationException("Shader has no glVertex.\n" + VALID_FORMAT_MESSAGE); + } + + // update other attributes if required + if (floatColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + // if ((cDirty & GeometryArrayRetained.COLOR_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_COLOR_WRITE)) != 0L; + if (changable) + { + fclrs.position(0); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToColorBuf); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, fclrs.remaining() * Float.SIZE / 8, fclrs); + } + } + if (normalsDefined && locs.glNormal != -1) + { + // if ((cDirty & GeometryArrayRetained.NORMAL_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_NORMAL_WRITE)) != 0L; + if (changable) + { + norms.position(0); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToNormalBuf); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, norms.remaining() * Float.SIZE / 8, norms); + } + } + + if (vattrDefined) + { + for (int index = 0; index < vertexAttrCount; index++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(index); + if (attribLoc != null && attribLoc.intValue() != -1) + { + // if ((cDirty & GeometryArrayRetained.VATTR_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits + & (1L << GeometryArray.ALLOW_VERTEX_ATTR_WRITE)) != 0L; + if (changable) + { + FloatBuffer vertexAttrs = vertexAttrBufs[index]; + vertexAttrs.position(0); + HashMap<Integer, Integer> bufIds = gd.geoToVertAttribBuf; + Integer bufId = bufIds.get(index); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, vertexAttrs.remaining() * Float.SIZE / 8, vertexAttrs); + } + } + } + } + + if (textureDefined) + { + boolean[] texSetsBound = new boolean[texCoords.length]; + for (int texUnit = 0; texUnit < numActiveTexUnitState && texUnit < texCoordMapLength; texUnit++) + { + int texSet = texCoordSetMap[texUnit]; + if (texSet != -1 && locs.glMultiTexCoord[texSet] != -1 && !texSetsBound[texSet]) + { + boolean changable = (((GeometryArray) geo.source).capabilityBits + & (1L << GeometryArray.ALLOW_TEXCOORD_WRITE)) != 0L; + if (changable) + { + FloatBuffer buf = (FloatBuffer) texCoords[texSet]; + buf.position(0); + HashMap<Integer, Integer> bufIds = gd.geoToTexCoordsBuf; + Integer bufId = bufIds.get(texUnit); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, buf.remaining() * Float.SIZE / 8, buf); + } + } + } + } + + // notice morphables must always rebind each frame as coord buffers are swapped + if (bindingRequired || morphable) + { + // always do coords + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf); + gl.glVertexAttribPointer(locs.glVertex, 3, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glVertex); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerCoord++; + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.coordCount += gd.geoToCoordBufSize; + } + + if (bindingRequired) + { + if (floatColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + if (gd.geoToColorBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + int coloroff; + int sz = ((vformat & GeometryArray.WITH_ALPHA) != 0) ? 4 : 3; + + coloroff = sz * initialColorIndex; + fclrs.position(coloroff); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToColorBuf); + gl.glVertexAttribPointer(locs.glColor, sz, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glColor);//must be called after Pointer above + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerColor++; + } + } + else if (byteColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + //FIXME: byteColors not supported for now, but I want them a lot + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + /*int coloroff; + int sz; + if ((vformat & GeometryArray.WITH_ALPHA) != 0) + { + coloroff = 4 * initialColorIndex; + sz = 4; + } + else + { + coloroff = 3 * initialColorIndex; + sz = 3; + } + bclrs.position(coloroff); + gl.glColorPointer(sz, GL2ES2.GL_UNSIGNED_BYTE, 0, bclrs);*/ + } + else if (locs.glColor != -1) + { + // ignoreVertexcolors willhave been set in FFP now as the glColors is unbound + gl.glDisableVertexAttribArray(locs.glColor); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + + if (normalsDefined && locs.glNormal != -1) + { + if (gd.geoToNormalBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToNormalBuf); + gl.glVertexAttribPointer(locs.glNormal, 3, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glNormal);//must be called after Pointer above + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerNormals++; + } + } + else + { + if (locs.glNormal != -1) + { + gl.glDisableVertexAttribArray(locs.glNormal); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + + if (vattrDefined) + { + for (int index = 0; index < vertexAttrCount; index++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(index); + if (attribLoc != null && attribLoc.intValue() != -1) + { + HashMap<Integer, Integer> bufIds = gd.geoToVertAttribBuf; + if (bufIds == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + + Integer bufId = bufIds.get(index); + if (bufId == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + int sz = vertexAttrSizes[index]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glVertexAttribPointer(attribLoc.intValue(), sz, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(attribLoc.intValue());//must be called after Pointer above + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerUserAttribs++; + } + } + } + } + + if (textureDefined) + { + boolean[] texSetsBound = new boolean[texCoords.length]; + for (int texUnit = 0; texUnit < numActiveTexUnitState && texUnit < texCoordMapLength; texUnit++) + { + int texSet = texCoordSetMap[texUnit]; + if (texSet != -1 && locs.glMultiTexCoord[texSet] != -1 && !texSetsBound[texSet]) + { + texSetsBound[texSet] = true; + // stupid interface... + FloatBuffer buf = (FloatBuffer) texCoords[texSet]; + buf.position(0); + + HashMap<Integer, Integer> bufIds = gd.geoToTexCoordsBuf; + if (bufIds == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + Integer bufId = bufIds.get(texUnit); + if (bufId == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glVertexAttribPointer(locs.glMultiTexCoord[texUnit], texStride, GL2ES2.GL_FLOAT, true, 0, 0); + gl.glEnableVertexAttribArray(locs.glMultiTexCoord[texUnit]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.enableTexCoordPointer++; + } + } + } + + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + if (geo_type == GeometryRetained.GEO_TYPE_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_LINE_STRIP_SET) + { + int primType = 0; + + //FIXME: GL_LINE and GL_LINE_STRIP simply go from one vertex to the next drawing a line between + // each pair, what I want is a line between each set of 3 (that are not jumpers) + + if (ctx.polygonMode == PolygonAttributes.POLYGON_LINE) + geo_type = GeometryRetained.GEO_TYPE_LINE_STRIP_SET; + + switch (geo_type) + { + case GeometryRetained.GEO_TYPE_TRI_STRIP_SET: + primType = GL2ES2.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_TRI_FAN_SET: + primType = GL2ES2.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_LINE_STRIP_SET: + primType = GL2ES2.GL_LINE_LOOP; + break; + } + + for (int i = 0; i < strip_len; i++) + { + if (sarray[i] > 0) + { + gl.glDrawArrays(primType, start_array[i], sarray[i]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawStripArraysStrips++; + } + } + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawStripArrays++; + } + else + { + // need to override if polygonAttributes says so + + if (ctx.polygonMode == PolygonAttributes.POLYGON_LINE) + geo_type = GeometryRetained.GEO_TYPE_LINE_SET; + else if (ctx.polygonMode == PolygonAttributes.POLYGON_POINT) + geo_type = GeometryRetained.GEO_TYPE_POINT_SET; + + switch (geo_type) + { + case GeometryRetained.GEO_TYPE_QUAD_SET: + throw new UnsupportedOperationException("QuadArray.\n" + VALID_FORMAT_MESSAGE); + case GeometryRetained.GEO_TYPE_TRI_SET: + gl.glDrawArrays(GL2ES2.GL_TRIANGLES, 0, vertexCount); + break; + case GeometryRetained.GEO_TYPE_POINT_SET: + gl.glDrawArrays(GL2ES2.GL_POINTS, 0, vertexCount); + break; + case GeometryRetained.GEO_TYPE_LINE_SET: + gl.glDrawArrays(GL2ES2.GL_LINES, 0, vertexCount); + break; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawArrays++; + } + + /* unbound in setRenderMode now + if (gl.isGL2ES3()) + { + GL2ES3 gl2es3 = (GL2ES3) gl; + gl2es3.glBindVertexArray(0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + }*/ + + //TODO: do I need to unbind these things, it seems fine not to + /* + if (vattrDefined) + { + for (int i = 0; i < vertexAttrCount; i++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(i); + if (attribLoc != null && attribLoc.intValue() != -1) + { + gl.glDisableVertexAttribArray(attribLoc); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + } + + if (textureDefined) + { + for (int i = 0; i < locs.glMultiTexCoord.length; i++) + { + if (locs.glMultiTexCoord[i] != -1) + { + gl.glDisableVertexAttribArray(locs.glMultiTexCoord[i]); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + }*/ + } + else + { + if (!NO_PROGRAM_WARNING_GIVEN) + System.err.println("Execute called with no shader Program in use!"); + NO_PROGRAM_WARNING_GIVEN = true; + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + } + + // --------------------------------------------------------------------- + + // + // IndexedGeometryArrayRetained methods + // + + // non interleaved, by reference, Java arrays + + @Override + void executeIndexedGeometryVA(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, + boolean ignoreVertexColors, int initialIndexIndex, int validIndexCount, int vertexCount, int vformat, int vdefined, + float[] vfcoords, double[] vdcoords, float[] cfdata, byte[] cbdata, float[] ndata, int vertexAttrCount, int[] vertexAttrSizes, + float[][] vertexAttrData, int texCoordMapLength, int[] texcoordoffset, int numActiveTexUnitState, int texStride, + Object[] texCoords, int cdirty, int[] indexCoord) + { + if (VERBOSE) + System.err.println("JoglPipeline.executeIndexedGeometryVA()"); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + FloatBuffer[] texCoordBufs = null; + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) + { + vertexAttrBufs = getVertexAttrSetBuffer(vertexAttrData); + } + + // get texture arrays + if (textureDefined) + { + texCoordBufs = getTexCoordSetBuffer(texCoords); + } + + int[] sarray = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) + { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // get coordinate array + if (floatCoordDefined) + { + fverts = getVertexArrayBuffer(vfcoords); + } + else if (doubleCoordDefined) + { + // FIXME: doubles not supported for now + throw new UnsupportedOperationException("doubleCoordDefined.\n" + VALID_FORMAT_MESSAGE); + // dverts = getVertexArrayBuffer(vdcoords); + } + + // get color array + if (floatColorsDefined) + { + fclrs = getColorArrayBuffer(cfdata); + } + else if (byteColorsDefined) + { + // FIXME: byte colors not supported for now + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + // bclrs = getColorArrayBuffer(cbdata); + } + + // get normal array + if (normalsDefined) + { + norms = getNormalArrayBuffer(ndata); + } + + executeIndexedGeometryArrayVA(ctx, geo, geo_type, isNonUniformScale, ignoreVertexColors, initialIndexIndex, validIndexCount, + vertexCount, vformat, vdefined, fverts, dverts, fclrs, bclrs, norms, vertexAttrCount, vertexAttrSizes, vertexAttrBufs, + texCoordMapLength, texcoordoffset, numActiveTexUnitState, texStride, texCoordBufs, cdirty, indexCoord, sarray, strip_len); + } + + // non interleaved, by reference, nio buffer + + @Override + void executeIndexedGeometryVABuffer(Context ctx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, + boolean ignoreVertexColors, int initialIndexIndex, int validIndexCount, int vertexCount, int vformat, int vdefined, + Buffer vcoords, Buffer cdataBuffer, float[] cfdata, byte[] cbdata, FloatBuffer ndata, int vertexAttrCount, + int[] vertexAttrSizes, FloatBuffer[] vertexAttrData, int texCoordMapLength, int[] texcoordoffset, int numActiveTexUnitState, + int texStride, Object[] texCoords, int cdirty, int[] indexCoord) + { + if (VERBOSE) + System.err.println("JoglPipeline.executeIndexedGeometryVABuffer() "); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + FloatBuffer fverts = null; + DoubleBuffer dverts = null; + FloatBuffer fclrs = null; + ByteBuffer bclrs = null; + + FloatBuffer norms = null; + FloatBuffer[] vertexAttrBufs = null; + + // Get vertex attribute arrays + if (vattrDefined) + { + vertexAttrBufs = vertexAttrData; + } + + // get coordinate array + if (floatCoordDefined) + { + fverts = (FloatBuffer) vcoords; + } + else if (doubleCoordDefined) + { + // FIXME: doubles not supported for now + throw new UnsupportedOperationException("doubleCoordDefined.\n" + VALID_FORMAT_MESSAGE); + // dverts = (DoubleBuffer) vcoords; + } + + if (fverts == null && dverts == null) + { + return; + } + + int[] sarray = null; + int strip_len = 0; + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) + { + sarray = ((IndexedGeometryStripArrayRetained) geo).stripIndexCounts; + strip_len = sarray.length; + } + + // get color array + if (floatColorsDefined) + { + if (cdataBuffer != null) + fclrs = (FloatBuffer) cdataBuffer; + else + fclrs = getColorArrayBuffer(cfdata); + + } + else if (byteColorsDefined) + { + // FIXME: not supported for now + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + + // if (cbdata != null) + // bclrs = getColorArrayBuffer(cbdata); + // else + // bclrs = (ByteBuffer) cdataBuffer; + } + + // get normal array + if (normalsDefined) + { + norms = ndata; + } + + executeIndexedGeometryArrayVA(ctx, geo, geo_type, isNonUniformScale, ignoreVertexColors, initialIndexIndex, validIndexCount, + vertexCount, vformat, vdefined, fverts, dverts, fclrs, bclrs, norms, vertexAttrCount, vertexAttrSizes, vertexAttrBufs, + texCoordMapLength, texcoordoffset, numActiveTexUnitState, texStride, texCoords, cdirty, indexCoord, sarray, strip_len); + } + + // ---------------------------------------------------------------------- + // + // Helper routines for IndexedGeometryArrayRetained + // + // careful - isNonUniformScale is always false regardless + private void executeIndexedGeometryArrayVA(Context absCtx, GeometryArrayRetained geo, int geo_type, boolean isNonUniformScale, + boolean ignoreVertexColors, int initialIndexIndex, int validIndexCount, int vertexCount, int vformat, int vdefined, + FloatBuffer fverts, DoubleBuffer dverts, FloatBuffer fclrs, ByteBuffer bclrs, FloatBuffer norms, int vertexAttrCount, + int[] vertexAttrSizes, FloatBuffer[] vertexAttrBufs, int texCoordMapLength, int[] texCoordSetMap, int numActiveTexUnitState, + int texStride, Object[] texCoords, int cDirty, int[] indexCoord, int[] sarray, int strip_len) + { + + // removed if (ATTEMPT_OPTIMIZED_VERTICES && + // executeIndexedGeometryOptimized(... + + Jogl2es2Context ctx = (Jogl2es2Context) absCtx; + int shaderProgramId = ctx.shaderProgramId; + + if (shaderProgramId != -1) + { + GL2ES2 gl = ctx.gl2es2; + ProgramData pd = ctx.programData; + LocationData locs = pd.programToLocationData; + + setFFPAttributes(ctx, gl, shaderProgramId, pd, vdefined); + + // If any buffers need loading do that now + GeometryData gd = loadAllBuffers(ctx, gl, geo, ignoreVertexColors, vertexCount, vformat, vdefined, fverts, dverts, fclrs, bclrs, + norms, vertexAttrCount, vertexAttrSizes, vertexAttrBufs, texCoordMapLength, texCoordSetMap, texStride, texCoords); + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + //can it change ever? (GeometryArray.ALLOW_REF_DATA_WRITE is just my indicator of this feature) + boolean morphable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_REF_DATA_WRITE)) != 0L + || (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_COORDINATE_WRITE)) != 0L; + + // not required second time around for VAO + // however as morphables coords are swapped they always get rebound each draw + boolean bindingRequired = true; + if (ctx.gl2es3 != null) + { + if (gd.vaoId == -1) + { + int[] tmp = new int[1]; + ctx.gl2es3.glGenVertexArrays(1, tmp, 0); + gd.vaoId = tmp[0]; + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + bindingRequired = false; + } + ctx.gl2es3.glBindVertexArray(gd.vaoId); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // Define the data pointers + if (locs.glVertex != -1) + { + if (floatCoordDefined) + { + // Building of buffers etc and index buffers should really take place not on the j3d thread if possible + + if (gd.geoToCoordBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + if (morphable) + { + fverts.position(0); + + // Sometime the FloatBuffer is swapped out for bigger or smaller! or is that ok? + if (gd.geoToCoordBufSize != fverts.remaining()) + { + System.err.println("Morphable buffer changed " + gd.geoToCoordBufSize + " != " + fverts.remaining() + + " ((GeometryArray) geo.source) " + ((GeometryArray) geo.source).getName() + " " + geo.source); + + int prevBufId1 = gd.geoToCoordBuf1;// keep to delete below + int prevBufId2 = gd.geoToCoordBuf2;// keep to delete below + + int[] tmp = new int[2]; + gl.glGenBuffers(2, tmp, 0); + gd.geoToCoordBuf = tmp[0]; + gd.geoToCoordBuf1 = tmp[0]; + gd.geoToCoordBuf2 = tmp[1]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf1); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, + GL2ES2.GL_STATIC_DRAW); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf2); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, + GL2ES2.GL_STATIC_DRAW); + + gd.geoToCoordBufSize = fverts.remaining(); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + // Notice no check for bindingRequired as we are altering the binding + // and previously used buffer is deleted AFTER re-bind + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf); + gl.glVertexAttribPointer(locs.glVertex, 3, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glVertex); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerCoord++; + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.coordCount += gd.geoToCoordBufSize; + + gl.glDeleteBuffers(1, new int[] { prevBufId1, prevBufId2 }, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + // work out the buffer to update and buffer to swap to + if (gd.geoToCoordBuf == gd.geoToCoordBuf1) + { + // update 1 but set to draw 2 + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf1); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, (fverts.remaining() * Float.SIZE / 8), fverts); + gd.geoToCoordBuf = gd.geoToCoordBuf2; + + } + else + { + // update 2 but set to draw 1 + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf2); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, (fverts.remaining() * Float.SIZE / 8), fverts); + gd.geoToCoordBuf = gd.geoToCoordBuf1; + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferSubData++; + } + } + + } + } + else if (doubleCoordDefined) + { + throw new UnsupportedOperationException("doubleCoordDefined.\n" + VALID_FORMAT_MESSAGE); + } + else + { + throw new UnsupportedOperationException("No coords defined.\n" + VALID_FORMAT_MESSAGE); + } + } + else + { + throw new UnsupportedOperationException("Shader has no glVertex.\n" + VALID_FORMAT_MESSAGE); + } + + // update other attributes if required + if (floatColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + // if ((cDirty & GeometryArrayRetained.COLOR_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_COLOR_WRITE)) != 0L; + if (changable) + { + fclrs.position(0); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToColorBuf); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, fclrs.remaining() * Float.SIZE / 8, fclrs); + } + } + if (normalsDefined && locs.glNormal != -1) + { + // if ((cDirty & GeometryArrayRetained.NORMAL_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_NORMAL_WRITE)) != 0L; + if (changable) + { + norms.position(0); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToNormalBuf); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, norms.remaining() * Float.SIZE / 8, norms); + } + } + + if (vattrDefined) + { + for (int index = 0; index < vertexAttrCount; index++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(index); + if (attribLoc != null && attribLoc.intValue() != -1) + { + // if ((cDirty & GeometryArrayRetained.VATTR_CHANGED) != 0) + boolean changable = (((GeometryArray) geo.source).capabilityBits + & (1L << GeometryArray.ALLOW_VERTEX_ATTR_WRITE)) != 0L; + if (changable) + { + FloatBuffer vertexAttrs = vertexAttrBufs[index]; + vertexAttrs.position(0); + HashMap<Integer, Integer> bufIds = gd.geoToVertAttribBuf; + Integer bufId = bufIds.get(index); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, vertexAttrs.remaining() * Float.SIZE / 8, vertexAttrs); + } + } + } + } + + if (textureDefined) + { + boolean[] texSetsBound = new boolean[texCoords.length]; + for (int texUnit = 0; texUnit < numActiveTexUnitState && texUnit < texCoordMapLength; texUnit++) + { + int texSet = texCoordSetMap[texUnit]; + if (texSet != -1 && locs.glMultiTexCoord[texSet] != -1 && !texSetsBound[texSet]) + { + boolean changable = (((GeometryArray) geo.source).capabilityBits + & (1L << GeometryArray.ALLOW_TEXCOORD_WRITE)) != 0L; + if (changable) + { + FloatBuffer buf = (FloatBuffer) texCoords[texSet]; + buf.position(0); + HashMap<Integer, Integer> bufIds = gd.geoToTexCoordsBuf; + Integer bufId = bufIds.get(texUnit); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, buf.remaining() * Float.SIZE / 8, buf); + } + } + } + } + + // binding is required for morphables coords as always swappping buffers each second frame + if (bindingRequired || morphable) + { + // always coords + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf); + gl.glVertexAttribPointer(locs.glVertex, 3, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(locs.glVertex); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerCoord++; + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.coordCount += gd.geoToCoordBufSize; + + } + + if (bindingRequired) + { + if (floatColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + if (gd.geoToColorBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + int sz = ((vformat & GeometryArray.WITH_ALPHA) != 0) ? 4 : 3; + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToColorBuf); + + gl.glVertexAttribPointer(locs.glColor, sz, GL2ES2.GL_FLOAT, true, 0, 0); + gl.glEnableVertexAttribArray(locs.glColor); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerColor++; + } + + } + else if (byteColorsDefined && locs.glColor != -1 && !ignoreVertexColors) + { + //FIXME: byteColors not supported for now + throw new UnsupportedOperationException("byteColorsDefined.\n" + VALID_FORMAT_MESSAGE); + + /*bclrs.position(0); + if ((vformat & GeometryArray.WITH_ALPHA) != 0) + { + gl.glColorPointer(4, GL2ES2.GL_UNSIGNED_BYTE, 0, bclrs); + } + else + { + gl.glColorPointer(3, GL2ES2.GL_UNSIGNED_BYTE, 0, bclrs); + }*/ + } + else if (locs.glColor != -1) + { + // ignoreVertexcolors will be set in FFP now as the glColors is unbound + gl.glDisableVertexAttribArray(locs.glColor); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + + if (normalsDefined) + { + if (locs.glNormal != -1) + { + if (gd.geoToNormalBuf == -1) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToNormalBuf); + + gl.glVertexAttribPointer(locs.glNormal, 3, GL2ES2.GL_FLOAT, true, 0, 0); + gl.glEnableVertexAttribArray(locs.glNormal);//must be called after Pointer above + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerNormals++; + } + } + } + else + { + if (locs.glNormal != -1) + { + gl.glDisableVertexAttribArray(locs.glNormal); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + + if (vattrDefined) + { + for (int index = 0; index < vertexAttrCount; index++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(index); + if (attribLoc != null && attribLoc.intValue() != -1) + { + HashMap<Integer, Integer> bufIds = gd.geoToVertAttribBuf; + if (bufIds == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + + Integer bufId = bufIds.get(index); + if (bufId == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + + int sz = vertexAttrSizes[index]; + + gl.glVertexAttribPointer(attribLoc.intValue(), sz, GL2ES2.GL_FLOAT, false, 0, 0); + gl.glEnableVertexAttribArray(attribLoc.intValue());//must be called after Pointer above + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glVertexAttribPointerUserAttribs++; + } + } + } + } + + if (textureDefined) + { + boolean[] texSetsBound = new boolean[texCoords.length]; + for (int texUnit = 0; texUnit < numActiveTexUnitState && texUnit < texCoordMapLength; texUnit++) + { + int texSet = texCoordSetMap[texUnit]; + if (texSet != -1 && locs.glMultiTexCoord[texSet] != -1 && !texSetsBound[texSet]) + { + texSetsBound[texSet] = true; + // stupid interface... + FloatBuffer buf = (FloatBuffer) texCoords[texSet]; + buf.position(0); + + HashMap<Integer, Integer> bufIds = gd.geoToTexCoordsBuf; + if (bufIds == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + Integer bufId = bufIds.get(texUnit); + if (bufId == null) + { + new Throwable("Buffer load issue!").printStackTrace(); + } + else + { + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glVertexAttribPointer(locs.glMultiTexCoord[texUnit], texStride, GL2ES2.GL_FLOAT, true, 0, 0); + gl.glEnableVertexAttribArray(locs.glMultiTexCoord[texUnit]);// must be called after Pointer above + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.enableTexCoordPointer++; + } + } + } + + } + + // general catch all + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + if (geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET || geo_type == GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET + || geo_type == GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET) + { + int primType = 0; + + // need to override if polygonAttributes says so + if (ctx.polygonMode == PolygonAttributes.POLYGON_LINE) + geo_type = GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET; + + switch (geo_type) + { + case GeometryRetained.GEO_TYPE_INDEXED_TRI_STRIP_SET: + primType = GL2ES2.GL_TRIANGLE_STRIP; + break; + case GeometryRetained.GEO_TYPE_INDEXED_TRI_FAN_SET: + primType = GL2ES2.GL_TRIANGLE_FAN; + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_STRIP_SET: + primType = GL2ES2.GL_LINES; + break; + } + + int[] stripInd = gd.geoToIndStripBuf; + // if no index buffers build build them now + if (stripInd == null) + { + stripInd = new int[strip_len]; + gl.glGenBuffers(strip_len, stripInd, 0); + + int offset = initialIndexIndex; + ByteBuffer bb = ByteBuffer.allocateDirect(indexCoord.length * 2); + bb.order(ByteOrder.nativeOrder()); + ShortBuffer indicesBuffer = bb.asShortBuffer(); + for (int s = 0; s < indexCoord.length; s++) + indicesBuffer.put(s, (short) indexCoord[s]); + for (int i = 0; i < strip_len; i++) + { + indicesBuffer.position(offset); + int count = sarray[i]; + int indBufId = stripInd[i]; + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, indBufId); + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, count * Short.SIZE / 8, indicesBuffer, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + offset += count; + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + } + + gd.geoToIndStripBuf = stripInd; + } + else + { + //a good cDirty and a DYNAMIC_DRAW call needed + /*if ((cDirty & GeometryArrayRetained.INDEX_CHANGED) != 0) + { + int offset = initialIndexIndex; + IntBuffer indicesBuffer = IntBuffer.wrap(indexCoord); + for (int i = 0; i < strip_len; i++) + { + indicesBuffer.position(offset); + int count = sarray[i]; + int indBufId = stripInd[i]; + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, indBufId); + gl.glBufferSubData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0, count * Integer.SIZE / 8, indicesBuffer); + //gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + offset += count; + }*/ + } + + for (int i = 0; i < strip_len; i++) + { + int count = sarray[i]; + int indBufId = stripInd[i]; + + //type Specifies the type of the values in indices. Must be + // GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT. + // Apparently ES3 has included this guy now, so I'm a bit commited to it + //https://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml + //This restriction is relaxed when GL_OES_element_index_uint is supported. + //GL_UNSIGNED_INT + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, indBufId); + gl.glDrawElements(primType, count, GL2ES2.GL_UNSIGNED_SHORT, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawStripElementsStrips++; + + } + // gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, 0); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawStripElements++; + + // note only the first count so multi strips is worng here, + // but... + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.indexCount += gd.geoToIndBufSize; + + } + else + { + // bind my indexes ready for the draw call + if (gd.geoToIndBuf == -1) + { + // create and fill index buffer + ByteBuffer bb = ByteBuffer.allocateDirect(indexCoord.length * 2); + bb.order(ByteOrder.nativeOrder()); + ShortBuffer indBuf = bb.asShortBuffer(); + for (int s = 0; s < indexCoord.length; s++) + indBuf.put(s, (short) indexCoord[s]); + indBuf.position(initialIndexIndex); + + int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + gd.geoToIndBuf = tmp[0];// about to add to map below + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, gd.geoToIndBuf); + gl.glBufferData(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, indBuf.remaining() * Short.SIZE / 8, indBuf, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + gd.geoToIndBufSize = indBuf.remaining(); + + } + else + { + //a good cDirty and a DYNAMIC_DRAW call needed + /*if ((cDirty & GeometryArrayRetained.INDEX_CHANGED) != 0) + { + IntBuffer indBuf = IntBuffer.wrap(indexCoord); + indBuf.position(initialIndexIndex); + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, indexBufId.intValue()); + gl.glBufferSubData(GL2ES2.GL_ARRAY_BUFFER, 0, indBuf.remaining() * Integer.SIZE / 8, indBuf); + }*/ + } + + gl.glBindBuffer(GL2ES2.GL_ELEMENT_ARRAY_BUFFER, gd.geoToIndBuf); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.indexCount += gd.geoToIndBufSize; + + // Need to override if polygonAttributes says we should be drawing lines + // Note these are not poly line just contiguous lines between each pair of points + // So it looks really rubbish + if (ctx.polygonMode == PolygonAttributes.POLYGON_LINE) + geo_type = GeometryRetained.GEO_TYPE_INDEXED_LINE_SET; + else if (ctx.polygonMode == PolygonAttributes.POLYGON_POINT) + geo_type = GeometryRetained.GEO_TYPE_INDEXED_POINT_SET; + + switch (geo_type) + { + case GeometryRetained.GEO_TYPE_INDEXED_QUAD_SET: + throw new UnsupportedOperationException("QuadArray.\n" + VALID_FORMAT_MESSAGE); + case GeometryRetained.GEO_TYPE_INDEXED_TRI_SET: + gl.glDrawElements(GL2ES2.GL_TRIANGLES, validIndexCount, GL2ES2.GL_UNSIGNED_SHORT, 0); + break; + case GeometryRetained.GEO_TYPE_INDEXED_POINT_SET: + gl.glDrawElements(GL2ES2.GL_POINTS, validIndexCount, GL2ES2.GL_UNSIGNED_SHORT, 0); + break; + case GeometryRetained.GEO_TYPE_INDEXED_LINE_SET: + gl.glDrawElements(GL2ES2.GL_LINES, validIndexCount, GL2ES2.GL_UNSIGNED_SHORT, 0); + break; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDrawElements++; + + } + + /* unbound in setRenderMode now + if (gl.isGL2ES3()) + { + GL2ES3 gl2es3 = (GL2ES3) gl; + gl2es3.glBindVertexArray(0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + }*/ + + //TODO: are these unbinds needed, seems fine without them + /* + if (vattrDefined) + { + for (int i = 0; i < vertexAttrCount; i++) + { + Integer attribLoc = locs.genAttIndexToLoc.get(i); + if (attribLoc != null && attribLoc.intValue() != -1) + { + gl.glDisableVertexAttribArray(attribLoc); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + } + + if (textureDefined) + { + for (int i = 0; i < locs.glMultiTexCoord.length; i++) + { + if (locs.glMultiTexCoord[i] != -1) + { + gl.glDisableVertexAttribArray(locs.glMultiTexCoord[i]); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glDisableVertexAttribArray++; + } + } + }*/ + + } + else + + { + if (!NO_PROGRAM_WARNING_GIVEN) + System.err.println("Execute called with no shader Program in use!"); + NO_PROGRAM_WARNING_GIVEN = true; + } + if (DO_OUTPUT_ERRORS) + + outputErrors(ctx); + } + + /** + * Over time we have had things recorded and in FFP they are considered + * current state in programmable we have to push them across manually each + * time recorded in JoglesContext + * + * @param gl + * @param vdefined + */ + + private static void setFFPAttributes(Jogl2es2Context ctx, GL2ES2 gl, int shaderProgramId, ProgramData pd, int vdefined) + { + + LocationData locs = pd.programToLocationData; + + //boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + //boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + //boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + //boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + //boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + // vertex colors MUST be ignored if no glColors set + boolean ignoreVertexColors = (!floatColorsDefined && !byteColorsDefined) || ctx.renderingData.ignoreVertexColors; + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.setFFPAttributes++; + + // removed if (ATTEMPT_UBO && gl.isGL2ES3())... + + // if shader hasn't changed location of uniform I don't need to reset these (they are cleared to -1 at the start of each swap) + if (locs.glProjectionMatrix != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram)) + { + gl.glUniformMatrix4fv(locs.glProjectionMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentProjMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + if (locs.glProjectionMatrixInverse != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram)) + { + // EXPENSIVE!!!!! only calc if asked for, and even then... + try + { + ctx.currentProjMatInverse.set(ctx.currentProjMat); + ctx.matrixUtil.invert(ctx.currentProjMatInverse); + } + catch (SingularMatrixException e) + { + System.err.println("" + e); + } + + gl.glUniformMatrix4fv(locs.glProjectionMatrixInverse, 1, true, ctx.matrixUtil.toArray(ctx.currentProjMatInverse), 0); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + if (locs.glViewMatrix != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram)) + { + gl.glUniformMatrix4fv(locs.glViewMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentViewMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + + if (locs.glModelMatrix != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.modelMatrix.equals(ctx.currentModelMat))) + { + gl.glUniformMatrix4fv(locs.glModelMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentModelMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.modelMatrix.set(ctx.currentModelMat); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.modelMatrixUpdated++; + } + else if (OUTPUT_PER_FRAME_STATS) + { + ctx.perFrameStats.modelMatrixSkipped++; + } + } + + if (locs.glModelViewMatrix != -1) + { + // minimise not working due to late calc of matrix + //if (!MINIMISE_NATIVE_CALLS_FFP + // || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glModelViewMatrix.equals(ctx.currentModelViewMat))) + // { + ctx.currentModelViewMat.mul(ctx.currentViewMat, ctx.currentModelMat); + + gl.glUniformMatrix4fv(locs.glModelViewMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentModelViewMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glModelViewMatrix.set(ctx.currentModelViewMat); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glModelViewMatrixUpdated++; + // } + // else if (OUTPUT_PER_FRAME_STATS) + // { + // ctx.perFrameStats.glModelViewMatrixSkipped++; + // } + } + if (locs.glModelViewMatrixInverse != -1) + {// minimise not working due to late calc of matrix + //if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + // || !ctx.gl_state.glModelViewMatrixInverse.equals(ctx.currentModelViewMatInverse))) + //{ + // Expensive, only calc if required + ctx.currentModelViewMatInverse.mul(ctx.currentViewMat, ctx.currentModelMat); + ctx.matrixUtil.invert(ctx.currentModelViewMatInverse); + + //gl.glUniformMatrix4fv(locs.glModelViewMatrixInverse, 1, false, ctx.toFB(ctx.currentModelViewMatInverse)); + gl.glUniformMatrix4fv(locs.glModelViewMatrixInverse, 1, true, ctx.matrixUtil.toArray(ctx.currentModelViewMatInverse), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glModelViewMatrixInverse.set(ctx.currentModelViewMatInverse); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glModelViewMatrixInverseUpdated++; + // } + // else if (OUTPUT_PER_FRAME_STATS) + // { + // ctx.perFrameStats.glModelViewMatrixInverseSkipped++; + // } + } + + if (locs.glModelViewProjectionMatrix != -1) + { + // minimise not working due to late calc of matrix + // if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + // || !ctx.gl_state.glModelViewProjectionMatrix.equals(ctx.currentModelViewProjMat))) + // { + ctx.currentModelViewMat.mul(ctx.currentViewMat, ctx.currentModelMat); + ctx.currentModelViewProjMat.mul(ctx.currentProjMat, ctx.currentModelViewMat); + + //gl.glUniformMatrix4fv(locs.glModelViewProjectionMatrix, 1, false, ctx.toFB(ctx.currentModelViewProjMat)); + gl.glUniformMatrix4fv(locs.glModelViewProjectionMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentModelViewProjMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glModelViewProjectionMatrix.set(ctx.currentModelViewProjMat); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glModelViewProjectionMatrixUpdated++; + // } + // else if (OUTPUT_PER_FRAME_STATS) + // { + // ctx.perFrameStats.glModelViewProjectionMatrixSkipped++; + // } + } + + if (locs.glNormalMatrix != -1) + { + // minimise not working due to late calc of matrix + //if (!MINIMISE_NATIVE_CALLS_FFP + // || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glNormalMatrix.equals(ctx.currentNormalMat))) + //{ + ctx.currentModelViewMat.mul(ctx.matrixUtil.deburnV, ctx.matrixUtil.deburnM); + Jogl2es2MatrixUtil.transposeInvert(ctx.currentModelViewMat, ctx.currentNormalMat); + + //gl.glUniformMatrix3fv(locs.glNormalMatrix, 1, false, ctx.toFB(ctx.currentNormalMat)); + gl.glUniformMatrix3fv(locs.glNormalMatrix, 1, true, ctx.matrixUtil.toArray(ctx.currentNormalMat), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glNormalMatrix.set(ctx.currentNormalMat); + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glNormalMatrixUpdated++; + //} + //else if (OUTPUT_PER_FRAME_STATS) + //{ + // ctx.perFrameStats.glNormalMatrixSkipped++; + //} + } + + // if set one of the 2 colors below should be used by the shader (material for lighting) + + if (locs.ignoreVertexColors != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.ignoreVertexColors != ignoreVertexColors)) + { + gl.glUniform1i(locs.ignoreVertexColors, ignoreVertexColors ? 1 : 0);// note local variable used + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.ignoreVertexColors = ignoreVertexColors; + } + } + + // send material data through + if (locs.glFrontMaterialdiffuse != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glFrontMaterialdiffuse.equals(ctx.materialData.diffuse))) + { + gl.glUniform4f(locs.glFrontMaterialdiffuse, ctx.materialData.diffuse.x, ctx.materialData.diffuse.y, + ctx.materialData.diffuse.z, ctx.materialData.diffuse.w); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glFrontMaterialdiffuse.set(ctx.materialData.diffuse); + } + } + if (locs.glFrontMaterialemission != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + || !ctx.gl_state.glFrontMaterialemission.equals(ctx.materialData.emission))) + { + gl.glUniform4f(locs.glFrontMaterialemission, ctx.materialData.emission.x, ctx.materialData.emission.y, + ctx.materialData.emission.z, 1f); // note extra alpha value to avoid errors + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glFrontMaterialemission.set(ctx.materialData.emission); + } + } + + if (locs.glFrontMaterialspecular != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + || !ctx.gl_state.glFrontMaterialspecular.equals(ctx.materialData.specular))) + { + gl.glUniform3f(locs.glFrontMaterialspecular, ctx.materialData.specular.x, ctx.materialData.specular.y, + ctx.materialData.specular.z); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glFrontMaterialspecular.set(ctx.materialData.specular); + } + } + if (locs.glFrontMaterialshininess != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.glFrontMaterialshininess != ctx.materialData.shininess)) + { + gl.glUniform1f(locs.glFrontMaterialshininess, ctx.materialData.shininess); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glFrontMaterialshininess = ctx.materialData.shininess; + } + } + + // ambient does not come from material notice + if (locs.glLightModelambient != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glLightModelambient.equals(ctx.currentAmbientColor))) + { + gl.glUniform4f(locs.glLightModelambient, ctx.currentAmbientColor.x, ctx.currentAmbientColor.y, ctx.currentAmbientColor.z, + ctx.currentAmbientColor.w); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glLightModelambient.set(ctx.currentAmbientColor); + } + } + + // always bind object color, the shader can decide to use it if it's no lighting and no vertex colors + if (locs.objectColor != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.objectColor.equals(ctx.objectColor))) + { + gl.glUniform4f(locs.objectColor, ctx.objectColor.x, ctx.objectColor.y, ctx.objectColor.z, ctx.objectColor.w); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.objectColor.set(ctx.objectColor); + } + } + + /*dirLight + pointLight + spotLight*/ + //currentEnabledLights + + // For now using first point light, but gonna need to put em all in + LightData l0 = null; + if (ctx.pointLight[0] != null) + l0 = ctx.pointLight[0]; + else if (ctx.dirLight[0] != null) + l0 = ctx.dirLight[0]; + + if (l0 != null) + { + if (locs.glLightSource0position != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glLightSource0position.equals(l0.pos))) + { + gl.glUniform4f(locs.glLightSource0position, l0.pos.x, l0.pos.y, l0.pos.z, l0.pos.w); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glLightSource0position.set(l0.pos); + } + } + if (locs.glLightSource0diffuse != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.glLightSource0diffuse.equals(l0.diffuse))) + { + gl.glUniform4f(locs.glLightSource0diffuse, l0.diffuse.x, l0.diffuse.y, l0.diffuse.z, l0.diffuse.w); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.glLightSource0diffuse.set(l0.diffuse); + } + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + if (locs.alphaTestEnabled != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.alphaTestEnabled != ctx.renderingData.alphaTestEnabled)) + { + gl.glUniform1i(locs.alphaTestEnabled, ctx.renderingData.alphaTestEnabled ? 1 : 0); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.alphaTestEnabled = ctx.renderingData.alphaTestEnabled; + + if (ctx.renderingData.alphaTestEnabled == true) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + || ctx.gl_state.alphaTestFunction != ctx.renderingData.alphaTestFunction)) + { + gl.glUniform1i(locs.alphaTestFunction, getFunctionValue(ctx.renderingData.alphaTestFunction)); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.alphaTestFunction = ctx.renderingData.alphaTestFunction; + } + + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram + || ctx.gl_state.alphaTestValue != ctx.renderingData.alphaTestValue)) + { + gl.glUniform1f(locs.alphaTestValue, ctx.renderingData.alphaTestValue); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.alphaTestValue = ctx.renderingData.alphaTestValue; + } + } + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + if (locs.textureTransform != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.textureTransform.equals(ctx.textureTransform))) + { + // gl.glUniformMatrix4fv(locs.textureTransform, 1, true, ctx.toFB(ctx.textureTransform)); + gl.glUniformMatrix4fv(locs.textureTransform, 1, true, ctx.matrixUtil.toArray(ctx.textureTransform), 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.textureTransform.set(ctx.textureTransform); + } + } + + // Fog + if (locs.fogEnabled != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.fogEnabled != ctx.fogData.enable)) + { + gl.glUniform1i(locs.fogEnabled, ctx.fogData.enable ? 1 : 0); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.fogEnabled = ctx.fogData.enable; + } + + if (ctx.fogData.enable == true) + { + if (locs.expColor != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.expColor.equals(ctx.fogData.expColor))) + { + gl.glUniform4f(locs.expColor, ctx.fogData.expColor.x, ctx.fogData.expColor.y, ctx.fogData.expColor.z, 1.0f); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.expColor.set(ctx.fogData.expColor); + } + } + + if (locs.expDensity != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.expDensity != ctx.fogData.expDensity)) + { + gl.glUniform1f(locs.expDensity, ctx.fogData.expDensity); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.expDensity = ctx.fogData.expDensity; + } + } + if (locs.linearColor != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || !ctx.gl_state.linearColor.equals(ctx.fogData.linearColor))) + { + gl.glUniform4f(locs.linearColor, ctx.fogData.linearColor.x, ctx.fogData.linearColor.y, ctx.fogData.linearColor.z, + 1.0f); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.linearColor.set(ctx.fogData.linearColor); + } + } + + if (locs.linearStart != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.linearStart != ctx.fogData.linearStart)) + { + gl.glUniform1f(locs.linearStart, ctx.fogData.linearStart); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.linearStart = ctx.fogData.linearStart; + } + } + if (locs.linearEnd != -1) + { + if (!MINIMISE_NATIVE_CALLS_FFP + || (shaderProgramId != ctx.prevShaderProgram || ctx.gl_state.linearEnd != ctx.fogData.linearEnd)) + { + gl.glUniform1f(locs.linearEnd, ctx.fogData.linearEnd); + if (MINIMISE_NATIVE_CALLS_FFP) + ctx.gl_state.linearEnd = ctx.fogData.linearEnd; + } + } + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // NOTE water app shows multiple light calculations + + // record for the next loop through FFP + ctx.prevShaderProgram = shaderProgramId; + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + } + + private boolean NO_PROGRAM_WARNING_GIVEN = false; + + // ---------------------------------------------------------------------- + // Private helper methods for GeometryArrayRetained and IndexedGeometryArrayRetained + // + + private static void loadLocs(Jogl2es2Context ctx, GL2ES2 gl) + { + ProgramData pd = ctx.programData; + int shaderProgramId = ctx.shaderProgramId; + if (pd.programToLocationData == null) + { + LocationData locs = new LocationData(); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.programToLocationData++; + + // Removed if(ATTEMPT_UBO && gl.isGL2ES3())... + + locs.glProjectionMatrix = gl.glGetUniformLocation(shaderProgramId, "glProjectionMatrix"); + locs.glProjectionMatrixInverse = gl.glGetUniformLocation(shaderProgramId, "glProjectionMatrixInverse"); + locs.glModelMatrix = gl.glGetUniformLocation(shaderProgramId, "glModelMatrix"); + locs.glViewMatrix = gl.glGetUniformLocation(shaderProgramId, "glViewMatrix"); + locs.glModelViewMatrix = gl.glGetUniformLocation(shaderProgramId, "glModelViewMatrix"); + locs.glModelViewMatrixInverse = gl.glGetUniformLocation(shaderProgramId, "glModelViewMatrixInverse"); + locs.glModelViewProjectionMatrix = gl.glGetUniformLocation(shaderProgramId, "glModelViewProjectionMatrix"); + locs.glNormalMatrix = gl.glGetUniformLocation(shaderProgramId, "glNormalMatrix"); + locs.ignoreVertexColors = gl.glGetUniformLocation(shaderProgramId, "ignoreVertexColors"); + locs.glFrontMaterialdiffuse = gl.glGetUniformLocation(shaderProgramId, "glFrontMaterialdiffuse"); + locs.glFrontMaterialemission = gl.glGetUniformLocation(shaderProgramId, "glFrontMaterialemission"); + locs.glFrontMaterialspecular = gl.glGetUniformLocation(shaderProgramId, "glFrontMaterialspecular"); + locs.glFrontMaterialshininess = gl.glGetUniformLocation(shaderProgramId, "glFrontMaterialshininess"); + locs.glLightModelambient = gl.glGetUniformLocation(shaderProgramId, "glLightModelambient"); + locs.objectColor = gl.glGetUniformLocation(shaderProgramId, "objectColor"); + locs.glLightSource0position = gl.glGetUniformLocation(shaderProgramId, "glLightSource0position"); + locs.glLightSource0diffuse = gl.glGetUniformLocation(shaderProgramId, "glLightSource0diffuse"); + locs.alphaTestEnabled = gl.glGetUniformLocation(shaderProgramId, "alphaTestEnabled"); + locs.alphaTestFunction = gl.glGetUniformLocation(shaderProgramId, "alphaTestFunction"); + locs.alphaTestValue = gl.glGetUniformLocation(shaderProgramId, "alphaTestValue"); + locs.textureTransform = gl.glGetUniformLocation(shaderProgramId, "textureTransform"); + locs.fogEnabled = gl.glGetUniformLocation(shaderProgramId, "fogEnabled"); + locs.expColor = gl.glGetUniformLocation(shaderProgramId, "expColor"); + locs.expDensity = gl.glGetUniformLocation(shaderProgramId, "expDensity"); + locs.linearColor = gl.glGetUniformLocation(shaderProgramId, "linearColor"); + locs.linearStart = gl.glGetUniformLocation(shaderProgramId, "linearStart"); + locs.linearEnd = gl.glGetUniformLocation(shaderProgramId, "linearEnd"); + + // attributes + locs.glVertex = gl.glGetAttribLocation(shaderProgramId, "glVertex"); + locs.glColor = gl.glGetAttribLocation(shaderProgramId, "glColor"); + locs.glNormal = gl.glGetAttribLocation(shaderProgramId, "glNormal"); + + // tex coords, notice the vertex attribute is made of a string concat + for (int i = 0; i < locs.glMultiTexCoord.length; i++) + { + locs.glMultiTexCoord[i] = gl.glGetAttribLocation(shaderProgramId, "glMultiTexCoord" + i); + } + + // generic attributes, notice allocated on a program basis not per geom + HashMap<String, Integer> attToIndex = pd.progToGenVertAttNameToGenVertAttIndex; + if (attToIndex != null) + { + for (String attrib : attToIndex.keySet()) + { + int index = attToIndex.get(attrib); + int attribLoc = gl.glGetAttribLocation(shaderProgramId, attrib); + locs.genAttIndexToLoc.put(index, new Integer(attribLoc)); + } + } + + pd.programToLocationData = locs; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + private static GeometryData loadAllBuffers(Jogl2es2Context ctx, GL2ES2 gl, GeometryArrayRetained geo, boolean ignoreVertexColors, + int vertexCount, int vformat, int vdefined, FloatBuffer fverts, DoubleBuffer dverts, FloatBuffer fclrs, ByteBuffer bclrs, + FloatBuffer norms, int vertexAttrCount, int[] vertexAttrSizes, FloatBuffer[] vertexAttrBufs, int texCoordMapLength, + int[] texCoordSetMap, int texStride, Object[] texCoords) + { + if (VERBOSE) + System.err.println("private static GeometryData loadAllBuffers"); + + GeometryData gd = ctx.allGeometryData.get(geo.nativeId); + if (gd == null) + { + gd = new GeometryData(); + geo.nativeId = gd.nativeId; + ctx.allGeometryData.put(geo.nativeId, gd); + } + + boolean floatCoordDefined = ((vdefined & GeometryArrayRetained.COORD_FLOAT) != 0); + boolean doubleCoordDefined = ((vdefined & GeometryArrayRetained.COORD_DOUBLE) != 0); + boolean floatColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_FLOAT) != 0); + boolean byteColorsDefined = ((vdefined & GeometryArrayRetained.COLOR_BYTE) != 0); + boolean normalsDefined = ((vdefined & GeometryArrayRetained.NORMAL_FLOAT) != 0); + boolean vattrDefined = ((vdefined & GeometryArrayRetained.VATTR_FLOAT) != 0); + boolean textureDefined = ((vdefined & GeometryArrayRetained.TEXCOORD_FLOAT) != 0); + + if (floatCoordDefined) + { + if (gd.geoToCoordBuf == -1) + { + // can it change ever? (GeometryArray.ALLOW_REF_DATA_WRITE is just my indicator of this feature) + boolean morphable = (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_REF_DATA_WRITE)) != 0L + || (((GeometryArray) geo.source).capabilityBits & (1L << GeometryArray.ALLOW_COORDINATE_WRITE)) != 0L; + + fverts.position(0); + + if (morphable) + { + int[] tmp = new int[2]; + gl.glGenBuffers(2, tmp, 0); + gd.geoToCoordBuf = tmp[0]; + gd.geoToCoordBuf1 = tmp[0]; + gd.geoToCoordBuf2 = tmp[1]; + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf1); + int usage = morphable ? GL2ES2.GL_DYNAMIC_DRAW : GL2ES2.GL_STATIC_DRAW; + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, usage); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf2); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, usage); + } + else + { + int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + gd.geoToCoordBuf = tmp[0]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToCoordBuf); + int usage = morphable ? GL2ES2.GL_DYNAMIC_DRAW : GL2ES2.GL_STATIC_DRAW; + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, (fverts.remaining() * Float.SIZE / 8), fverts, usage); + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + gd.geoToCoordBufSize = fverts.remaining(); + + if (ctx.allGeometryData.size() % 500 == 0) + { + System.out.println("Coord buffer count " + ctx.allGeometryData.size()); + } + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + } + } + + if (floatColorsDefined && !ignoreVertexColors) + { + if (gd.geoToColorBuf == -1) + { + fclrs.position(0); + int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + gd.geoToColorBuf = tmp[0]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToColorBuf); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, fclrs.remaining() * Float.SIZE / 8, fclrs, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + } + } + + if (normalsDefined) + { + if (gd.geoToNormalBuf == -1) + { + norms.position(0); + + int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + gd.geoToNormalBuf = tmp[0]; + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, gd.geoToNormalBuf); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, norms.remaining() * Float.SIZE / 8, norms, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + } + } + + if (vattrDefined) + { + for (int index = 0; index < vertexAttrCount; index++) + { + FloatBuffer vertexAttrs = vertexAttrBufs[index]; + vertexAttrs.position(0); + + HashMap<Integer, Integer> bufIds = gd.geoToVertAttribBuf; + if (bufIds == null) + { + bufIds = new HashMap<Integer, Integer>(); + gd.geoToVertAttribBuf = bufIds; + } + + Integer bufId = bufIds.get(index); + if (bufId == null) + { + int[] tmp2 = new int[1]; + gl.glGenBuffers(1, tmp2, 0); + bufId = new Integer(tmp2[0]); + bufIds.put(index, bufId); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, vertexAttrs.remaining() * Float.SIZE / 8, vertexAttrs, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + + } + } + } + + if (textureDefined) + { + boolean[] texSetsLoaded = new boolean[texCoords.length]; + for (int texUnit = 0; texUnit < texCoordMapLength; texUnit++) + { + int texSet = texCoordSetMap[texUnit]; + if (texSet != -1 && !texSetsLoaded[texSet]) + { + texSetsLoaded[texSet] = true; + // stupid interface... + FloatBuffer buf = (FloatBuffer) texCoords[texSet]; + buf.position(0); + + HashMap<Integer, Integer> bufIds = gd.geoToTexCoordsBuf; + if (bufIds == null) + { + bufIds = new HashMap<Integer, Integer>(); + gd.geoToTexCoordsBuf = bufIds; + } + + Integer bufId = bufIds.get(texUnit); + if (bufId == null) + { + int[] tmp = new int[1]; + gl.glGenBuffers(1, tmp, 0); + bufId = new Integer(tmp[0]); + + gl.glBindBuffer(GL2ES2.GL_ARRAY_BUFFER, bufId.intValue()); + gl.glBufferData(GL2ES2.GL_ARRAY_BUFFER, buf.remaining() * Float.SIZE / 8, buf, GL2ES2.GL_STATIC_DRAW); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + bufIds.put(texUnit, bufId); + + if (OUTPUT_PER_FRAME_STATS) + ctx.perFrameStats.glBufferData++; + } + } + } + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + return gd; + } + + // --Noop + @Override + void setVertexFormat(Context ctx, GeometryArrayRetained geo, int vformat, boolean useAlpha, boolean ignoreVertexColors) + { + // if (VERBOSE) + // System.err.println("JoglPipeline.setVertexFormat()"); + } + + // called by the 2 executes above + + // --------------------------------------------------------------------- + + // + // GLSLShaderProgramRetained methods + // + + // ShaderAttributeValue methods + + @Override + ShaderError setGLSLUniform1i(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform1i(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value=" + value + ")"); + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesctx.gl2es2; + int loc = unbox(uniformLocation); + if (!MINIMISE_NATIVE_SHADER || joglesctx.gl_state.setGLSLUniform1i[loc] != value) + { + gl.glUniform1i(loc, value); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_SHADER) + joglesctx.gl_state.setGLSLUniform1i[loc] = value; + } + return null; + } + + @Override + ShaderError setGLSLUniform1f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform1f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value=" + value + ")"); + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesctx.gl2es2; + int loc = unbox(uniformLocation); + if (!MINIMISE_NATIVE_SHADER || joglesctx.gl_state.setGLSLUniform1f[loc] != value) + { + gl.glUniform1f(loc, value); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_SHADER) + joglesctx.gl_state.setGLSLUniform1f[loc] = value; + } + return null; + } + + @Override + ShaderError setGLSLUniform2i(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform2i(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform2i(unbox(uniformLocation), value[0], value[1]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform2f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform2f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform2f(unbox(uniformLocation), value[0], value[1]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform3i(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform3i(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform3i(unbox(uniformLocation), value[0], value[1], value[2]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform3f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform3f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform3f(unbox(uniformLocation), value[0], value[1], value[2]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform4i(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform4i(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform4i(unbox(uniformLocation), value[0], value[1], value[2], value[3]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform4f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform4f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform4f(unbox(uniformLocation), value[0], value[1], value[2], value[3]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniformMatrix3f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniformMatrix3fv(unbox(uniformLocation), 1, false, ((Jogl2es2Context) ctx).matrixUtil.toFB3(value)); + // gl.glUniformMatrix3fv(unbox(uniformLocation), 1, true, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4f(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniformMatrix4f(shaderProgramId = " + unbox(shaderProgramId) + ",uniformLocation=" + + unbox(uniformLocation) + ",value[0]=" + value[0] + ")"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniformMatrix4fv(unbox(uniformLocation), 1, false, ((Jogl2es2Context) ctx).matrixUtil.toFB4(value)); + // gl.glUniformMatrix4fv(unbox(uniformLocation), 1, true, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + // ShaderAttributeArray methods + + @Override + ShaderError setGLSLUniform1iArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform1iArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform1iv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform1fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform1fArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform1fv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform2iArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform2iArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform2iv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform2fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform2fArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform2fv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform3iArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform3iArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform3iv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform3fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform3fArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform3fv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform4iArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + int[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform4iArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform4iv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniform4fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniform4fArray()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniform4fv(unbox(uniformLocation), numElements, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix3fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniformMatrix3fArray()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniformMatrix3fv(unbox(uniformLocation), numElements, true, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError setGLSLUniformMatrix4fArray(Context ctx, ShaderProgramId shaderProgramId, ShaderAttrLoc uniformLocation, int numElements, + float[] value) + { + if (VERBOSE) + System.err.println("JoglPipeline.setGLSLUniformMatrix4fArray()"); + + // Load attribute + // transpose is true : each matrix is supplied in row major order + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glUniformMatrix4fv(unbox(uniformLocation), numElements, true, value, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + // interfaces for shader compilation, etc. + @Override + ShaderError createGLSLShader(Context ctx, int shaderType, ShaderId[] shaderId) + { + if (VERBOSE) + System.err.println("JoglPipeline.createGLSLShader()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.createGLSLShader++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + int shaderHandle = 0; + if (shaderType == Shader.SHADER_TYPE_VERTEX) + { + shaderHandle = (int) gl.glCreateShader(GL2ES2.GL_VERTEX_SHADER); + } + else if (shaderType == Shader.SHADER_TYPE_FRAGMENT) + { + shaderHandle = (int) gl.glCreateShader(GL2ES2.GL_FRAGMENT_SHADER); + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (shaderHandle == 0) + { + return new ShaderError(ShaderError.COMPILE_ERROR, "Unable to create native shader object"); + } + + shaderId[0] = new JoglShaderObject(shaderHandle); + + return null; + } + + @Override + ShaderError destroyGLSLShader(Context ctx, ShaderId shaderId) + { + if (VERBOSE) + System.err.println("JoglPipeline.destroyGLSLShader()"); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.destroyGLSLShader++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glDeleteShader(unbox(shaderId)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + return null; + } + + @Override + ShaderError compileGLSLShader(Context ctx, ShaderId shaderId, String program) + { + if (VERBOSE) + System.err.println("JoglPipeline.compileGLSLShader()"); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.compileGLSLShader++; + + int id = unbox(shaderId); + if (id == 0) + { + throw new AssertionError("shaderId == 0"); + } + + if (program == null) + { + throw new AssertionError("shader program string is null"); + } + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + gl.glShaderSource(id, 1, new String[] { program }, null, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + gl.glCompileShader(id); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + int[] status = new int[1]; + gl.glGetShaderiv(id, GL2ES2.GL_COMPILE_STATUS, status, 0); + if (status[0] == 0) + { + String detailMsg = getShaderInfoLog(gl, id); + ShaderError res = new ShaderError(ShaderError.COMPILE_ERROR, "GLSL shader compile error"); + res.setDetailMessage(detailMsg); + return res; + } + return null; + } + + @Override + ShaderError createGLSLShaderProgram(Context ctx, ShaderProgramId[] shaderProgramId) + { + if (VERBOSE) + System.err.println("JoglPipeline.createGLSLShaderProgram()"); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.createGLSLShaderProgram++; + + int shaderProgramHandle = (int) gl.glCreateProgram(); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (shaderProgramHandle == 0) + { + return new ShaderError(ShaderError.LINK_ERROR, "Unable to create native shader program object"); + } + shaderProgramId[0] = new JoglShaderObject(shaderProgramHandle); + + return null; + } + + @Override + ShaderError destroyGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) + { + if (VERBOSE) + System.err.println("JoglPipeline.destroyGLSLShaderProgram()"); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.destroyGLSLShaderProgram++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glDeleteShader(unbox(shaderProgramId)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + // just dump data + ((Jogl2es2Context) ctx).allProgramData.remove(unbox(shaderProgramId)); + + return null; + } + + @Override + ShaderError linkGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId, ShaderId[] shaderIds) + { + if (VERBOSE) + System.err.println("JoglPipeline.linkGLSLShaderProgram()"); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.linkGLSLShaderProgram++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + int id = unbox(shaderProgramId); + for (int i = 0; i < shaderIds.length; i++) + { + gl.glAttachShader(id, unbox(shaderIds[i])); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + gl.glLinkProgram(id); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + int[] status = new int[1]; + gl.glGetProgramiv(id, GL2ES2.GL_LINK_STATUS, status, 0); + if (status[0] == 0) + { + String detailMsg = getProgramInfoLog(gl, id); + ShaderError res = new ShaderError(ShaderError.LINK_ERROR, "GLSL shader program link error"); + res.setDetailMessage(detailMsg); + return res; + } + return null; + } + + @Override + ShaderError bindGLSLVertexAttrName(Context ctx, ShaderProgramId shaderProgramId, String attrName, int attrIndex) + { + if (VERBOSE) + System.err.println("JoglPipeline.bindGLSLVertexAttrName()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.bindGLSLVertexAttrName++; + + // GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // gl.glBindAttribLocation(unbox(shaderProgramId), attrIndex + VirtualUniverse.mc.glslVertexAttrOffset, attrName); + + // record this for later, we'll get real locations in the locationData setup + int progId = unbox(shaderProgramId); + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + ProgramData pd = joglesContext.allProgramData.get(progId); + if (pd == null) + { + pd = new ProgramData(); + joglesContext.allProgramData.put(progId, pd); + } + + HashMap<String, Integer> attToIndex = pd.progToGenVertAttNameToGenVertAttIndex; + if (attToIndex == null) + { + attToIndex = new HashMap<String, Integer>(); + pd.progToGenVertAttNameToGenVertAttIndex = attToIndex; + } + + attToIndex.put(attrName, attrIndex); + + return null; + } + + @Override + void lookupGLSLShaderAttrNames(Context ctx, ShaderProgramId shaderProgramId, int numAttrNames, String[] attrNames, + ShaderAttrLoc[] locArr, int[] typeArr, int[] sizeArr, boolean[] isArrayArr) + { + if (VERBOSE) + System.err.println("JoglPipeline.lookupGLSLShaderAttrNames()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.lookupGLSLShaderAttrNames++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // set the loc, type, and size arrays to out-of-bound values + for (int i = 0; i < attrNames.length; i++) + { + locArr[i] = null; + typeArr[i] = -1; + sizeArr[i] = -1; + } + + // Loop through the list of active uniform variables, one at a + // time, searching for a match in the attrNames array. + // + // NOTE: Since attrNames isn't sorted, and we don't have a + // hashtable of names to index locations, we will do a + // brute-force, linear search of the array. This leads to an + // O(n^2) algorithm (actually O(n*m) where n is attrNames.length + // and m is the number of uniform variables), but since we expect + // N to be small, we will not optimize this at this time. + int id = unbox(shaderProgramId); + int[] tmp = new int[1]; + int[] tmp2 = new int[1]; + int[] tmp3 = new int[1]; + + gl.glGetProgramiv(id, GL2ES2.GL_ACTIVE_UNIFORMS, tmp, 0); + int numActiveUniforms = tmp[0]; + gl.glGetProgramiv(id, GL2ES2.GL_ACTIVE_UNIFORM_MAX_LENGTH, tmp, 0); + int maxStrLen = tmp[0]; + byte[] nameBuf = new byte[maxStrLen]; + + for (int i = 0; i < numActiveUniforms; i++) + { + gl.glGetActiveUniform(id, i, maxStrLen, tmp3, 0, tmp, 0, tmp2, 0, nameBuf, 0); + int size = tmp[0]; + int type = tmp2[0]; + String name = null; + try + { + name = new String(nameBuf, 0, tmp3[0], "US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + + // Issue 247 - we need to workaround an ATI bug where they erroneously + // report individual elements of arrays rather than the array itself + if (name.length() >= 3 && name.endsWith("]")) + { + if (name.endsWith("[0]")) + { + name = name.substring(0, name.length() - 3); + } + else + { + // Ignore this name + continue; + } + } + + // Now try to find the name + for (int j = 0; j < numAttrNames; j++) + { + if (name.equals(attrNames[j])) + { + sizeArr[j] = size; + isArrayArr[j] = (size > 1); + typeArr[j] = glslToJ3dType(type); + break; + } + } + } + + // Now lookup the location of each name in the attrNames array + for (int i = 0; i < numAttrNames; i++) + { + // Get uniform attribute location + int loc = gl.glGetUniformLocation(id, attrNames[i]); + locArr[i] = new JoglShaderObject(loc); + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // good ideas about uniform debugging + // http://stackoverflow.com/questions/26164602/unexplainable-gl-invalid-operation-from-gluniform1i-opengl-thinks-an-int-is-a-f + private boolean USE_NULL_SHADER_WARNING_GIVEN = false; + + @Override + ShaderError useGLSLShaderProgram(Context ctx, ShaderProgramId shaderProgramId) + { + if (VERBOSE) + System.err.println("JoglPipeline.useGLSLShaderProgram(shaderProgramId=" + unbox(shaderProgramId) + ")"); + + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + if (OUTPUT_PER_FRAME_STATS) + { + if (joglesContext.gl_state.currentProgramId == unbox(shaderProgramId)) + { + joglesContext.perFrameStats.redundantUseProgram++; + } + else + { + joglesContext.perFrameStats.useGLSLShaderProgram++; + joglesContext.perFrameStats.usedPrograms.add(shaderProgramId); + } + } + + if (!MINIMISE_NATIVE_SHADER || joglesContext.gl_state.currentProgramId != unbox(shaderProgramId)) + { + if (shaderProgramId == null) + { + if (!USE_NULL_SHADER_WARNING_GIVEN) + System.err.println("Null shader passed for use"); + USE_NULL_SHADER_WARNING_GIVEN = true; + } + + GL2ES2 gl = joglesContext.gl2es2; + + gl.glUseProgram(unbox(shaderProgramId)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + joglesContext.setShaderProgram((JoglShaderObject) shaderProgramId); + loadLocs(joglesContext, gl); + + if (MINIMISE_NATIVE_SHADER) + joglesContext.gl_state.currentProgramId = unbox(shaderProgramId); + + } + return null; + } + + // ---------------------------------------------------------------------- + // Helper methods for above shader routines + // + private static int unbox(ShaderAttrLoc loc) + { + if (loc == null) + return 0; + return ((JoglShaderObject) loc).getValue(); + } + + private static int unbox(ShaderProgramId id) + { + if (id == null) + return 0; + return ((JoglShaderObject) id).getValue(); + } + + private static int unbox(ShaderId id) + { + if (id == null) + return 0; + return ((JoglShaderObject) id).getValue(); + } + + private static String getShaderInfoLog(GL2ES2 gl, int id) + { + int[] infoLogLength = new int[1]; + gl.glGetShaderiv(id, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); + if (infoLogLength[0] > 0) + { + byte[] storage = new byte[infoLogLength[0]]; + int[] len = new int[1]; + gl.glGetShaderInfoLog(id, infoLogLength[0], len, 0, storage, 0); + try + { + return new String(storage, 0, len[0], "US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + return null; + } + + private static String getProgramInfoLog(GL2ES2 gl, int id) + { + int[] infoLogLength = new int[1]; + gl.glGetProgramiv(id, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); + if (infoLogLength[0] > 0) + { + byte[] storage = new byte[infoLogLength[0]]; + int[] len = new int[1]; + gl.glGetProgramInfoLog(id, infoLogLength[0], len, 0, storage, 0); + try + { + return new String(storage, 0, len[0], "US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + return null; + } + + private static int glslToJ3dType(int type) + { + switch (type) + { + case GL2ES2.GL_BOOL: + case GL2ES2.GL_INT: + case GL2ES2.GL_SAMPLER_2D: + case GL2ES2.GL_SAMPLER_3D: + case GL2ES2.GL_SAMPLER_CUBE: + return ShaderAttributeObjectRetained.TYPE_INTEGER; + + case GL2ES2.GL_FLOAT: + return ShaderAttributeObjectRetained.TYPE_FLOAT; + + case GL2ES2.GL_INT_VEC2: + case GL2ES2.GL_BOOL_VEC2: + return ShaderAttributeObjectRetained.TYPE_TUPLE2I; + + case GL2ES2.GL_FLOAT_VEC2: + return ShaderAttributeObjectRetained.TYPE_TUPLE2F; + + case GL2ES2.GL_INT_VEC3: + case GL2ES2.GL_BOOL_VEC3: + return ShaderAttributeObjectRetained.TYPE_TUPLE3I; + + case GL2ES2.GL_FLOAT_VEC3: + return ShaderAttributeObjectRetained.TYPE_TUPLE3F; + + case GL2ES2.GL_INT_VEC4: + case GL2ES2.GL_BOOL_VEC4: + return ShaderAttributeObjectRetained.TYPE_TUPLE4I; + + case GL2ES2.GL_FLOAT_VEC4: + return ShaderAttributeObjectRetained.TYPE_TUPLE4F; + + // case GL2ES2.GL_FLOAT_MAT2: + + case GL2ES2.GL_FLOAT_MAT3: + return ShaderAttributeObjectRetained.TYPE_MATRIX3F; + + case GL2ES2.GL_FLOAT_MAT4: + return ShaderAttributeObjectRetained.TYPE_MATRIX4F; + + // Java 3D does not support the following sampler types: + // + // case GL2ES2.GL_SAMPLER_1D_ARB: + // case GL2ES2.GL_SAMPLER_1D_SHADOW_ARB: + // case GL2ES2.GL_SAMPLER_2D_SHADOW_ARB: + // case GL2ES2.GL_SAMPLER_2D_RECT_ARB: + // case GL2ES2.GL_SAMPLER_2D_RECT_SHADOW_ARB: + } + + return -1; + } + + // --------------------------------------------------------------------- + + // + // DirectionalLightRetained methods + // + + private static final Vector4f black = new Vector4f(); + + @Override + void updateDirectionalLight(Context ctx, int lightSlot, float red, float green, float blue, float dirx, float diry, float dirz) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateDirectionalLight()"); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateDirectionalLight++; + + /* GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // OK ES2 requires lights to be handed across manually + // so once again just force it in there and assume shader has it + + int lightNum = GL2ES2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2ES2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_SPECULAR, values, 0); + values[0] = -dirx; + values[1] = -diry; + values[2] = -dirz; + values[3] = 0.0f; + gl.glLightfv(lightNum, GL2ES2.GL_POSITION, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_AMBIENT, black, 0); + gl.glLightf(lightNum, GL2ES2.GL_POSITION, 1.0f); + gl.glLightf(lightNum, GL2ES2.GL_LINEAR_ATTENUATION, 0.0f); + gl.glLightf(lightNum, GL2ES2.GL_QUADRATIC_ATTENUATION, 0.0f); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_EXPONENT, 0.0f); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_CUTOFF, 180.0f);*/ + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.dirLight[lightSlot] == null) + joglesctx.dirLight[lightSlot] = new LightData(); + + joglesctx.dirLight[lightSlot].diffuse.x = red; + joglesctx.dirLight[lightSlot].diffuse.y = green; + joglesctx.dirLight[lightSlot].diffuse.z = blue; + joglesctx.dirLight[lightSlot].diffuse.w = 1.0f; + joglesctx.dirLight[lightSlot].specular.x = red; + joglesctx.dirLight[lightSlot].specular.y = green; + joglesctx.dirLight[lightSlot].specular.z = blue; + joglesctx.dirLight[lightSlot].specular.w = 1.0f; + joglesctx.dirLight[lightSlot].pos.x = -dirx; + joglesctx.dirLight[lightSlot].pos.y = -diry; + joglesctx.dirLight[lightSlot].pos.z = -dirz; + joglesctx.dirLight[lightSlot].pos.w = 0.0f;// 0 means directional light + joglesctx.dirLight[lightSlot].ambient = black;// odd + // joglesctx.dirLight[lightSlot].GL_POSITION = 1.0f; // what is this? + joglesctx.dirLight[lightSlot].GL_CONSTANT_ATTENUATION = 1.0f; + joglesctx.dirLight[lightSlot].GL_LINEAR_ATTENUATION = 0.0f; + joglesctx.dirLight[lightSlot].GL_QUADRATIC_ATTENUATION = 0.0f; + joglesctx.dirLight[lightSlot].GL_SPOT_EXPONENT = 0.0f; + joglesctx.dirLight[lightSlot].GL_SPOT_CUTOFF = 180.0f; + } + + // --------------------------------------------------------------------- + + // + // PointLightRetained methods + // + + @Override + void updatePointLight(Context ctx, int lightSlot, float red, float green, float blue, float attenx, float atteny, float attenz, + float posx, float posy, float posz) + { + if (VERBOSE) + System.err.println("JoglPipeline.updatePointLight()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updatePointLight++; + + /* GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // OK ES2 requires lights to be handed across manually + // so once again just force it in there and assume shader has it + + int lightNum = GL2ES2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2ES2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_SPECULAR, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_AMBIENT, black, 0); + values[0] = posx; + values[1] = posy; + values[2] = posz; + gl.glLightfv(lightNum, GL2ES2.GL_POSITION, values, 0); + gl.glLightf(lightNum, GL2ES2.GL_CONSTANT_ATTENUATION, attenx); + gl.glLightf(lightNum, GL2ES2.GL_LINEAR_ATTENUATION, atteny); + gl.glLightf(lightNum, GL2ES2.GL_QUADRATIC_ATTENUATION, attenz); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_EXPONENT, 0.0f); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_CUTOFF, 180.0f);*/ + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.pointLight[lightSlot] == null) + joglesctx.pointLight[lightSlot] = new LightData(); + + joglesctx.pointLight[lightSlot].diffuse.x = red; + joglesctx.pointLight[lightSlot].diffuse.y = green; + joglesctx.pointLight[lightSlot].diffuse.z = blue; + joglesctx.pointLight[lightSlot].diffuse.w = 1.0f; + joglesctx.pointLight[lightSlot].specular.x = red; + joglesctx.pointLight[lightSlot].specular.y = green; + joglesctx.pointLight[lightSlot].specular.z = blue; + joglesctx.pointLight[lightSlot].specular.w = 1.0f; + joglesctx.pointLight[lightSlot].pos.x = posx; + joglesctx.pointLight[lightSlot].pos.y = posy; + joglesctx.pointLight[lightSlot].pos.z = posz; + joglesctx.pointLight[lightSlot].pos.w = 1.0f;// 1 mean pos not dir + joglesctx.pointLight[lightSlot].ambient = black;// odd + joglesctx.pointLight[lightSlot].GL_CONSTANT_ATTENUATION = attenx; + joglesctx.pointLight[lightSlot].GL_LINEAR_ATTENUATION = atteny; + joglesctx.pointLight[lightSlot].GL_QUADRATIC_ATTENUATION = attenz; + joglesctx.pointLight[lightSlot].GL_SPOT_EXPONENT = 0.0f; + joglesctx.pointLight[lightSlot].GL_SPOT_CUTOFF = 180.0f; + } + + // --------------------------------------------------------------------- + + // + // SpotLightRetained methods + // + + @Override + void updateSpotLight(Context ctx, int lightSlot, float red, float green, float blue, float attenx, float atteny, float attenz, + float posx, float posy, float posz, float spreadAngle, float concentration, float dirx, float diry, float dirz) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateSpotLight()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateSpotLight++; + + /* GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // OK ES2 requires lights to be handed across manually + // so once again just force it in there and assume shader has it + + int lightNum = GL2ES2.GL_LIGHT0 + lightSlot; + float[] values = new float[4]; + + values[0] = red; + values[1] = green; + values[2] = blue; + values[3] = 1.0f; + gl.glLightfv(lightNum, GL2ES2.GL_DIFFUSE, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_SPECULAR, values, 0); + gl.glLightfv(lightNum, GL2ES2.GL_AMBIENT, black, 0); + values[0] = posx; + values[1] = posy; + values[2] = posz; + gl.glLightfv(lightNum, GL2ES2.GL_POSITION, values, 0); + gl.glLightf(lightNum, GL2ES2.GL_CONSTANT_ATTENUATION, attenx); + gl.glLightf(lightNum, GL2ES2.GL_LINEAR_ATTENUATION, atteny); + gl.glLightf(lightNum, GL2ES2.GL_QUADRATIC_ATTENUATION, attenz); + values[0] = dirx; + values[1] = diry; + values[2] = dirz; + gl.glLightfv(lightNum, GL2ES2.GL_SPOT_DIRECTION, values, 0); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_EXPONENT, concentration); + gl.glLightf(lightNum, GL2ES2.GL_SPOT_CUTOFF, (float) (spreadAngle * 180.0f / Math.PI));*/ + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.spotLight[lightSlot] == null) + joglesctx.spotLight[lightSlot] = new LightData(); + + joglesctx.spotLight[lightSlot].diffuse.x = red; + joglesctx.spotLight[lightSlot].diffuse.y = green; + joglesctx.spotLight[lightSlot].diffuse.z = blue; + joglesctx.spotLight[lightSlot].diffuse.w = 1.0f; + joglesctx.spotLight[lightSlot].specular.x = red; + joglesctx.spotLight[lightSlot].specular.y = green; + joglesctx.spotLight[lightSlot].specular.z = blue; + joglesctx.spotLight[lightSlot].specular.w = 1.0f; + joglesctx.spotLight[lightSlot].pos.x = posx; + joglesctx.spotLight[lightSlot].pos.y = posy; + joglesctx.spotLight[lightSlot].pos.z = posz; + joglesctx.spotLight[lightSlot].pos.w = 1.0f;// 1 mean pos not dir + joglesctx.spotLight[lightSlot].ambient = black;// odd + joglesctx.spotLight[lightSlot].GL_CONSTANT_ATTENUATION = attenx; + joglesctx.spotLight[lightSlot].GL_LINEAR_ATTENUATION = atteny; + joglesctx.spotLight[lightSlot].GL_QUADRATIC_ATTENUATION = attenz; + joglesctx.spotLight[lightSlot].spotDir.x = dirx; + joglesctx.spotLight[lightSlot].spotDir.y = diry; + joglesctx.spotLight[lightSlot].spotDir.z = dirz; + joglesctx.spotLight[lightSlot].spotDir.w = 1.0f; + joglesctx.spotLight[lightSlot].GL_SPOT_EXPONENT = concentration; + joglesctx.spotLight[lightSlot].GL_SPOT_CUTOFF = (float) (spreadAngle * 180.0f / Math.PI); + + } + + // --------------------------------------------------------------------- + + // + // ExponentialFogRetained methods + // + + @Override + void updateExponentialFog(Context ctx, float red, float green, float blue, float density) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateExponentialFog()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateExponentialFog++; + + /* GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + + // apparently also lost... I don't know... + //https://www.opengl.org/discussion_boards/showthread.php/178629-How-to-create-fog-using-Open-GL-ES-2-0-or-WebGL + + float[] color = new float[3]; + color[0] = red; + color[1] = green; + color[2] = blue; + gl.glFogi(GL2ES2.GL_FOG_MODE, GL2ES2.GL_EXP); + gl.glFogfv(GL2ES2.GL_FOG_COLOR, color, 0); + gl.glFogf(GL2ES2.GL_FOG_DENSITY, density); + gl.glEnable(GL2ES2.GL_FOG);*/ + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.fogData.expColor.x = red; + joglesctx.fogData.expColor.y = green; + joglesctx.fogData.expColor.z = blue; + joglesctx.fogData.expDensity = density; + joglesctx.fogData.enable = true; + } + + // --------------------------------------------------------------------- + + // + // LinearFogRetained methods + // + + @Override + void updateLinearFog(Context ctx, float red, float green, float blue, double fdist, double bdist) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateLinearFog()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateLinearFog++; + + /* GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + //gone + + float[] color = new float[3]; + color[0] = red; + color[1] = green; + color[2] = blue; + gl.glFogi(GL2ES2.GL_FOG_MODE, GL2ES2.GL_LINEAR); + gl.glFogfv(GL2ES2.GL_FOG_COLOR, color, 0); + gl.glFogf(GL2ES2.GL_FOG_START, (float) fdist); + gl.glFogf(GL2ES2.GL_FOG_END, (float) bdist); + gl.glEnable(GL2ES2.GL_FOG);*/ + + // see + // https://www.opengl.org/discussion_boards/showthread.php/151415-Fog-with-pixel-shader-%28arb_fragment_program%29 + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.fogData.linearColor.x = red; + joglesctx.fogData.linearColor.y = green; + joglesctx.fogData.linearColor.z = blue; + joglesctx.fogData.linearStart = (float) fdist; + joglesctx.fogData.linearEnd = (float) bdist; + joglesctx.fogData.enable = true; + } + + // native method for disabling fog + @Override + + void disableFog(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.disableFog()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.disableFog++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.fogData.enable = false; + } + + // native method for setting fog enable flag + + @Override + void setFogEnableFlag(Context ctx, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.setFogEnableFlag()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setFogEnableFlag++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.fogData.enable = enable; + } + // --------------------------------------------------------------------- + + // + // LineAttributesRetained methods + // + @Override + void updateLineAttributes(Context ctx, float lineWidth, int linePattern, int linePatternMask, int linePatternScaleFactor, + boolean lineAntialiasing) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateLineAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateLineAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glLineWidth(lineWidth); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + } + + // native method for setting default LineAttributes + @Override + + void resetLineAttributes(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetLineAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetLineAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + gl.glLineWidth(1.0f); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // --------------------------------------------------------------------- + + // + // MaterialRetained methods + // + + @Override + void updateMaterial(Context ctx, float red, float green, float blue, float alpha, float aRed, float aGreen, float aBlue, float eRed, + float eGreen, float eBlue, float dRed, float dGreen, float dBlue, float sRed, float sGreen, float sBlue, float shininess, + int colorTarget, boolean lightEnable) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateMaterial()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateMaterial++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.objectColor.x = red; + joglesctx.objectColor.y = green; + joglesctx.objectColor.z = blue; + joglesctx.objectColor.w = alpha; + joglesctx.materialData.lightEnabled = lightEnable; + joglesctx.materialData.shininess = shininess; + joglesctx.materialData.emission.x = eRed; + joglesctx.materialData.emission.y = eGreen; + joglesctx.materialData.emission.z = eBlue; + joglesctx.materialData.ambient.x = aRed; + joglesctx.materialData.ambient.y = aGreen; + joglesctx.materialData.ambient.z = aBlue; + joglesctx.materialData.specular.x = sRed; + joglesctx.materialData.specular.y = sGreen; + joglesctx.materialData.specular.z = sBlue; + joglesctx.materialData.diffuse.x = dRed; + joglesctx.materialData.diffuse.y = dGreen; + joglesctx.materialData.diffuse.z = dBlue; + joglesctx.materialData.diffuse.w = alpha; + + } + + // native method for setting Material when no material is present + @Override + + void updateMaterialColor(Context ctx, float r, float g, float b, float a) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateMaterialColor()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateMaterialColor++; + + // update single color in case where material has color and there are no coloring attributes + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.objectColor.x = r; + joglesctx.objectColor.y = g; + joglesctx.objectColor.z = b; + joglesctx.objectColor.w = a; + + } + // --------------------------------------------------------------------- + + // + // ColoringAttributesRetained methods + @Override + void updateColoringAttributes(Context ctx, float dRed, float dGreen, float dBlue, float red, float green, float blue, float alpha, + boolean lightEnable, int shadeModel) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateColoringAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateColoringAttributes++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + // note we ignore lightEnabled and always pass the object color to the shader if it wants it + joglesctx.objectColor.x = red; + joglesctx.objectColor.y = green; + joglesctx.objectColor.z = blue; + joglesctx.objectColor.w = alpha; + + } + + // native method for setting default ColoringAttributes + @Override + + void resetColoringAttributes(Context ctx, float r, float g, float b, float a, boolean enableLight) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetColoringAttributes() " + r + " " + g + " " + b + " " + a + " " + enableLight); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetColoringAttributes++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.objectColor.x = r; + joglesctx.objectColor.y = g; + joglesctx.objectColor.z = b; + joglesctx.objectColor.w = a; + } + + // --------------------------------------------------------------------- + + // + // PointAttributesRetained methods + // + // interesting as Points are how particles are done properly!! + // http://stackoverflow.com/questions/3497068/textured-points-in-opengl-es-2-0 + // http://stackoverflow.com/questions/7237086/opengl-es-2-0-equivalent-for-es-1-0-circles-using-gl-point-smooth + + @Override + void updatePointAttributes(Context ctx, float pointSize, boolean pointAntialiasing) + { + if (VERBOSE) + System.err.println("JoglPipeline.updatePointAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updatePointAttributes++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.pointSize = pointSize; + + // one time enable call + if (!pointsEnabled) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + // bug in desktop requiring this to be set still + gl.glEnable(0x8642);// GL_VERTEX_PROGRAM_POINT_SIZE + gl.glEnable(34913);// GL.GL_POINT_SPRITE); + pointsEnabled = true; + } + } + + private boolean pointsEnabled = false; + + // native method for setting default PointAttributes + @Override + void resetPointAttributes(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetPointAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetPointAttributes++; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + joglesctx.pointSize = 1.0f; + // GL2ES2 gl = ((JoglesContext) ctx).gl2es2; + // bug in desktop requiring this to be set still + // gl.glDisable(0x8642);//GL_VERTEX_PROGRAM_POINT_SIZE + // gl.glDisable(34913);//GL.GL_POINT_SPRITE); + } + // --------------------------------------------------------------------- + + // + // PolygonAttributesRetained methods + // + + @Override + void updatePolygonAttributes(Context ctx, int polygonMode, int cullFace, boolean backFaceNormalFlip, float polygonOffset, + float polygonOffsetFactor) + { + if (VERBOSE) + System.err.println("JoglPipeline.updatePolygonAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updatePolygonAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.gl_state.cullFace != cullFace) + { + if (cullFace == PolygonAttributes.CULL_NONE) + { + gl.glDisable(GL2ES2.GL_CULL_FACE); + } + else + { + if (cullFace == PolygonAttributes.CULL_BACK) + { + gl.glCullFace(GL2ES2.GL_BACK); + } + else + { + gl.glCullFace(GL2ES2.GL_FRONT); + } + gl.glEnable(GL2ES2.GL_CULL_FACE); + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.cullFace = cullFace; + } + + if (joglesctx.gl_state.polygonOffsetFactor != polygonOffsetFactor || joglesctx.gl_state.polygonOffset != polygonOffset) + { + gl.glPolygonOffset(polygonOffsetFactor, polygonOffset); + + if ((polygonOffsetFactor != 0.0f) || (polygonOffset != 0.0f)) + { + gl.glEnable(GL2ES2.GL_POLYGON_OFFSET_FILL); + } + else + { + gl.glDisable(GL2ES2.GL_POLYGON_OFFSET_FILL); + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.polygonOffsetFactor = polygonOffsetFactor; + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.polygonOffset = polygonOffset; + } + joglesctx.polygonMode = polygonMode; + } + + // native method for setting default PolygonAttributes + @Override + + void resetPolygonAttributes(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetPolygonAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetPolygonAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.gl_state.cullFace != PolygonAttributes.CULL_BACK) + { + gl.glCullFace(GL2ES2.GL_BACK); + gl.glEnable(GL2ES2.GL_CULL_FACE); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.cullFace = PolygonAttributes.CULL_BACK; + } + + if (joglesctx.gl_state.polygonOffsetFactor != 0.0f || joglesctx.gl_state.polygonOffset != 0.0f) + { + gl.glPolygonOffset(0.0f, 0.0f); + gl.glDisable(GL2ES2.GL_POLYGON_OFFSET_FILL); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.polygonOffsetFactor = 0.0f; + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.polygonOffset = 0.0f; + } + + joglesctx.polygonMode = PolygonAttributes.POLYGON_FILL; + } + + // --------------------------------------------------------------------- + + // + // RenderingAttributesRetained methods + // + + @Override + void updateRenderingAttributes(Context ctx, boolean depthBufferWriteEnableOverride, boolean depthBufferEnableOverride, + boolean depthBufferEnable, boolean depthBufferWriteEnable, int depthTestFunction, float alphaTestValue, int alphaTestFunction, + boolean ignoreVertexColors, boolean rasterOpEnable, int rasterOp, boolean userStencilAvailable, boolean stencilEnable, + int stencilFailOp, int stencilZFailOp, int stencilZPassOp, int stencilFunction, int stencilReferenceValue, + int stencilCompareMask, int stencilWriteMask) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateRenderingAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateRenderingAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (joglesctx.gl_state.depthBufferEnableOverride != depthBufferEnable || joglesctx.gl_state.depthBufferEnable != depthBufferEnable + || joglesctx.gl_state.depthTestFunction != depthTestFunction) + { + if (!depthBufferEnableOverride) + { + if (depthBufferEnable) + { + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + gl.glDepthFunc(getFunctionValue(depthTestFunction)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + gl.glDisable(GL2ES2.GL_DEPTH_TEST); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.depthBufferEnableOverride = depthBufferEnable; + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.depthBufferEnable = depthBufferEnable; + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.depthTestFunction = depthTestFunction; + } + + if (!depthBufferWriteEnableOverride) + { + if (depthBufferWriteEnable) + { + if (joglesctx.gl_state.glDepthMask != true) + { + gl.glDepthMask(true); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glDepthMask = true; + } + } + else + { + if (joglesctx.gl_state.glDepthMask != false) + { + gl.glDepthMask(false); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glDepthMask = true; + } + } + } + + if (alphaTestFunction == RenderingAttributes.ALWAYS) + { + joglesctx.renderingData.alphaTestEnabled = false; + } + else + { + // TODO: simple test use alpha blending instead of testing + joglesctx.renderingData.alphaTestEnabled = true; + joglesctx.renderingData.alphaTestFunction = getFunctionValue(alphaTestFunction); + joglesctx.renderingData.alphaTestValue = alphaTestValue; + } + + joglesctx.renderingData.ignoreVertexColors = ignoreVertexColors; + + if (rasterOpEnable) + { + System.err.println("rasterOpEnable!!!! no no no!"); + } + + if (userStencilAvailable) + { + if (stencilEnable) + { + if (joglesctx.gl_state.glEnableGL_STENCIL_TEST == false || joglesctx.gl_state.stencilFailOp != stencilFailOp + || joglesctx.gl_state.stencilZFailOp != stencilZFailOp || joglesctx.gl_state.stencilZPassOp != stencilZPassOp + || joglesctx.gl_state.stencilFunction != stencilFunction + || joglesctx.gl_state.stencilReferenceValue != stencilReferenceValue + || joglesctx.gl_state.stencilCompareMask != stencilCompareMask + || joglesctx.gl_state.stencilWriteMask != stencilWriteMask) + { + gl.glEnable(GL2ES2.GL_STENCIL_TEST); + gl.glStencilOp(getStencilOpValue(stencilFailOp), getStencilOpValue(stencilZFailOp), getStencilOpValue(stencilZPassOp)); + gl.glStencilFunc(getFunctionValue(stencilFunction), stencilReferenceValue, stencilCompareMask); + gl.glStencilMask(stencilWriteMask); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glEnableGL_STENCIL_TEST = true; + } + + } + else + { + if (joglesctx.gl_state.glEnableGL_STENCIL_TEST == true) + { + gl.glDisable(GL2ES2.GL_STENCIL_TEST); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glEnableGL_STENCIL_TEST = false; + } + } + } + + } + + // native method for setting default RenderingAttributes + @Override + + void resetRenderingAttributes(Context ctx, boolean depthBufferWriteEnableOverride, boolean depthBufferEnableOverride) + { + + if (VERBOSE) + System.err.println("JoglPipeline.resetRenderingAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetRenderingAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (!depthBufferWriteEnableOverride) + { + if (joglesctx.gl_state.glDepthMask != true) + { + gl.glDepthMask(true); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glDepthMask = true; + } + } + if (!depthBufferEnableOverride) + { + if (joglesctx.gl_state.depthBufferEnable != true) + { + gl.glEnable(GL2ES2.GL_DEPTH_TEST); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.depthBufferEnable = true; + } + } + if (joglesctx.gl_state.depthTestFunction != RenderingAttributes.LESS_OR_EQUAL) + { + gl.glDepthFunc(GL2ES2.GL_LEQUAL); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.depthTestFunction = RenderingAttributes.LESS_OR_EQUAL; + } + + joglesctx.renderingData.alphaTestEnabled = false; + joglesctx.renderingData.alphaTestFunction = RenderingAttributes.ALWAYS; + joglesctx.renderingData.alphaTestValue = 0; + joglesctx.renderingData.ignoreVertexColors = false; + + // RAISE_BUG: yep only called on a null RenderingAttributes + // FIXME: this call does not set stencil test, so possibly this is why the rendering attributes + // caused such a mess when not present?? + + if (joglesctx.gl_state.glEnableGL_STENCIL_TEST == true) + { + gl.glDisable(GL2ES2.GL_STENCIL_TEST); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_OTHER) + joglesctx.gl_state.glEnableGL_STENCIL_TEST = false; + } + + } + + @Override + void updateTransparencyAttributes(Context ctx, float alpha, int geometryType, int polygonMode, boolean lineAA, boolean pointAA, + int transparencyMode, int srcBlendFunction, int dstBlendFunction) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTransparencyAttributes() " + alpha + " " + geometryType + " " + polygonMode + " " + + lineAA + " " + pointAA + " " + transparencyMode + " " + srcBlendFunction + " " + dstBlendFunction); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateTransparencyAttributes++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + + if ((transparencyMode < TransparencyAttributes.SCREEN_DOOR) + || ((((geometryType & RenderMolecule.LINE) != 0) || (polygonMode == PolygonAttributes.POLYGON_LINE)) && lineAA) + || ((((geometryType & RenderMolecule.POINT) != 0) || (polygonMode == PolygonAttributes.POLYGON_POINT)) && pointAA)) + { + if (!MINIMISE_NATIVE_CALLS_TRANSPARENCY || (joglesctx.gl_state.glEnableGL_BLEND != true + || joglesctx.gl_state.srcBlendFunction != srcBlendFunction || joglesctx.gl_state.dstBlendFunction != dstBlendFunction)) + { + gl.glEnable(GL2ES2.GL_BLEND); + // valid range of blendFunction 0..3 is already verified in + // shared code. + gl.glBlendFunc(blendFunctionTable[srcBlendFunction], blendFunctionTable[dstBlendFunction]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.glEnableGL_BLEND = true; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.srcBlendFunction = srcBlendFunction; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.dstBlendFunction = dstBlendFunction; + } + + } + else + { + if (!MINIMISE_NATIVE_CALLS_TRANSPARENCY || (joglesctx.gl_state.glEnableGL_BLEND != false)) + { + gl.glDisable(GL2ES2.GL_BLEND); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.glEnableGL_BLEND = false; + } + } + + } + + // native method for setting default TransparencyAttributes + @Override + void resetTransparency(Context ctx, int geometryType, int polygonMode, boolean lineAA, boolean pointAA) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetTransparency()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetTransparency++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + if (((((geometryType & RenderMolecule.LINE) != 0) || (polygonMode == PolygonAttributes.POLYGON_LINE)) && lineAA) + || ((((geometryType & RenderMolecule.POINT) != 0) || (polygonMode == PolygonAttributes.POLYGON_POINT)) && pointAA)) + { + if (!MINIMISE_NATIVE_CALLS_TRANSPARENCY || (joglesctx.gl_state.glEnableGL_BLEND != true + || joglesctx.gl_state.srcBlendFunction != TransparencyAttributes.BLEND_SRC_ALPHA + || joglesctx.gl_state.dstBlendFunction != TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA)) + { + gl.glEnable(GL2ES2.GL_BLEND); + gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.glEnableGL_BLEND = true; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.srcBlendFunction = TransparencyAttributes.BLEND_SRC_ALPHA; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.dstBlendFunction = TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA; + } + } + else + { + if (!MINIMISE_NATIVE_CALLS_TRANSPARENCY || (joglesctx.gl_state.glEnableGL_BLEND != false)) + { + gl.glDisable(GL2ES2.GL_BLEND); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.glEnableGL_BLEND = false; + } + + } + + } + + // + // TextureAttributesRetained methods + // + @Override + void updateTextureAttributes(Context ctx, double[] transform, boolean isIdentity, int textureMode, int perspCorrectionMode, + float textureBlendColorRed, float textureBlendColorGreen, float textureBlendColorBlue, float textureBlendColorAlpha, + int textureFormat) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureAttributes() " + lineString(transform)); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateTextureAttributes++; + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + joglesctx.textureTransform.set(transform); + } + + // native method for setting default TextureAttributes + @Override + + // part of TUS updateNative + void resetTextureAttributes(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetTextureAttributes()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetTextureAttributes++; + + // set Identity + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + joglesctx.textureTransform.setIdentity(); + + } + + // --------------------------------------------------------------------- + + // native method for setting default TexCoordGeneration -Noop + @Override + void resetTexCoordGeneration(Context ctx) + { + // if (VERBOSE) + // System.err.println("JoglPipeline.resetTexCoordGeneration()"); + // if (OUTPUT_PER_FRAME_STATS) + // ((JoglesContext) ctx).perFrameStats.resetTexCoordGeneration++; + + } + + // --------------------------------------------------------------------- + + // + // TextureUnitStateRetained methods + // + + @Override + void updateTextureUnitState(Context ctx, int index, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureUnitState(index=" + index + ",enable=" + enable + ")"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.updateTextureUnitState++; + + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesContext.gl2es2; + + if (index >= 0) + { + if (!MINIMISE_NATIVE_CALLS_TEXTURE || (joglesContext.gl_state.glActiveTexture != (index + GL2ES2.GL_TEXTURE0))) + { + gl.glActiveTexture(index + GL2ES2.GL_TEXTURE0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TEXTURE) + joglesContext.gl_state.glActiveTexture = (index + GL2ES2.GL_TEXTURE0); + } + } + + } + + // --------------------------------------------------------------------- + + // + // TextureRetained methods + // Texture2DRetained methods + // + + @Override + void bindTexture2D(Context ctx, int objectId, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.bindTexture2D(objectId=" + objectId + ",enable=" + enable + ")"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.bindTexture2D++; + + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesContext.gl2es2; + + if (enable) + { + if (!MINIMISE_NATIVE_CALLS_TEXTURE + || (joglesContext.gl_state.glBindTextureGL_TEXTURE_2D[joglesContext.gl_state.glActiveTexture] != objectId)) + { + gl.glBindTexture(GL2ES2.GL_TEXTURE_2D, objectId); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + if (MINIMISE_NATIVE_CALLS_TEXTURE) + joglesContext.gl_state.glBindTextureGL_TEXTURE_2D[joglesContext.gl_state.glActiveTexture] = objectId; + } + } + } + + @Override + void updateTexture2DImage(Context ctx, int numLevels, int level, int textureFormat, int imageFormat, int width, int height, + int boundaryWidth, int dataType, Object data, boolean useAutoMipMap) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DImage(width=" + width + ",height=" + height + ",level=" + level + ")"); + + updateTexture2DImage(ctx, GL2ES2.GL_TEXTURE_2D, numLevels, level, textureFormat, imageFormat, width, height, boundaryWidth, + dataType, data, useAutoMipMap); + } + + // oddly in use when I press escape twice??? + @Override + void updateTexture2DSubImage(Context ctx, int level, int xoffset, int yoffset, int textureFormat, int imageFormat, int imgXOffset, + int imgYOffset, int tilew, int width, int height, int dataType, Object data, boolean useAutoMipMap) + { + + /* Note: useAutoMipMap is not use for SubImage in the jogl pipe */ + + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DSubImage()"); + + updateTexture2DSubImage(ctx, GL2ES2.GL_TEXTURE_2D, level, xoffset, yoffset, textureFormat, imageFormat, imgXOffset, imgYOffset, + tilew, width, height, dataType, data); + } + + @Override + void updateTexture2DLodRange(Context ctx, int baseLevel, int maximumLevel, float minimumLOD, float maximumLOD) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DLodRange()"); + + updateTextureLodRange(ctx, GL2ES2.GL_TEXTURE_2D, baseLevel, maximumLevel, minimumLOD, maximumLOD); + } + + @Override + void updateTexture2DBoundary(Context ctx, int boundaryModeS, int boundaryModeT, float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DBoundary()"); + + updateTextureBoundary(ctx, GL2ES2.GL_TEXTURE_2D, boundaryModeS, boundaryModeT, -1, boundaryRed, boundaryGreen, boundaryBlue, + boundaryAlpha); + } + + @Override + void updateTexture2DFilterModes(Context ctx, int minFilter, int magFilter) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DFilterModes()"); + + updateTextureFilterModes(ctx, GL2ES2.GL_TEXTURE_2D, minFilter, magFilter); + } + + @Override + void updateTexture2DAnisotropicFilter(Context ctx, float degree) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTexture2DAnisotropicFilter()"); + + updateTextureAnisotropicFilter(ctx, GL2ES2.GL_TEXTURE_2D, degree); + } + + private static void updateTextureLodRange(Context ctx, int target, int baseLevel, int maximumLevel, float minimumLOD, float maximumLOD) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // I notice these 4 parameters don't appear under GL2ES2 + + // target is good, pname can't have any of the 4 given, though I wonder if this is an extension somehow? + // confirmed here http://stackoverflow.com/questions/34499219/correct-way-to-unbind-an-open-gl-es-texture + + // target Specifies the target texture of the active texture unit, + // which must be either GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP. + // pname Specifies the symbolic name of a single-valued texture/ parameter. + // pname can be one of the following: GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, + // GL_TEXTURE_WRAP_S, or GL_TEXTURE_WRAP_T. + + // checking of the availability of the extension is already done + // in the shared code + // Apparently these are available in ES3 + + // gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_BASE_LEVEL, baseLevel); + + // http://stackoverflow.com/questions/12767917/is-using-gl-nearest-mipmap-or-gl-linear-mipmap-for-gl-texture-min-filter-con + // so hopefully ES2 just assumes the mip maps given are correct and ignores this call + + gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, maximumLevel); + // gl.glTexParameterf(target, GL2ES3.GL_TEXTURE_MIN_LOD, minimumLOD); + // gl.glTexParameterf(target, GL2ES3.GL_TEXTURE_MAX_LOD, maximumLOD); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + private static void updateTextureAnisotropicFilter(Context ctx, int target, float degree) + { + // FIXME: is this a true thing to send in? + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // it appears GL_TEXTURE_MAX_ANISOTROPY_EXT is still part of ES2 + // but not allowed for glTexParameterf + // here http://www.informit.com/articles/article.aspx?p=770639&seqNum=2 suggest the + // parameter may still be passed if the EXT is enabled + + // checking of the availability of anisotropic filter functionality + // is already done in the shared code + gl.glTexParameterf(target, GL2ES2.GL_TEXTURE_MAX_ANISOTROPY_EXT, degree); + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // --------------------------------------------------------------------- + + // + // TextureCubeMapRetained methods + // + + @Override + void bindTextureCubeMap(Context ctx, int objectId, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.bindTextureCubeMap()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.bindTextureCubeMap++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // TextureCubeMap will take precedents over 3D Texture so + // there is no need to disable 3D Texture here. + if (!enable) + { + // gl.glDisable(GL2ES2.GL_TEXTURE_CUBE_MAP); + } + else + { + gl.glBindTexture(GL2ES2.GL_TEXTURE_CUBE_MAP, objectId); + // gl.glEnable(GL2ES2.GL_TEXTURE_CUBE_MAP); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + + @Override + void updateTextureCubeMapImage(Context ctx, int face, int numLevels, int level, int textureFormat, int imageFormat, int width, + int height, int boundaryWidth, int dataType, Object data, boolean useAutoMipMap) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureCubeMapImage()"); + + updateTexture2DImage(ctx, _gl_textureCubeMapFace[face], numLevels, level, textureFormat, imageFormat, width, height, boundaryWidth, + dataType, data, useAutoMipMap); + } + + @Override + void updateTextureCubeMapSubImage(Context ctx, int face, int level, int xoffset, int yoffset, int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, int tilew, int width, int height, int dataType, Object data, boolean useAutoMipMap) + { + throw new UnsupportedOperationException(); + } + + @Override + void updateTextureCubeMapLodRange(Context ctx, int baseLevel, int maximumLevel, float minimumLod, float maximumLod) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureCubeMapLodRange()"); + + updateTextureLodRange(ctx, GL2ES2.GL_TEXTURE_CUBE_MAP, baseLevel, maximumLevel, minimumLod, maximumLod); + } + + @Override + void updateTextureCubeMapBoundary(Context ctx, int boundaryModeS, int boundaryModeT, float boundaryRed, float boundaryGreen, + float boundaryBlue, float boundaryAlpha) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureCubeMapBoundary()"); + + updateTextureBoundary(ctx, GL2ES2.GL_TEXTURE_CUBE_MAP, boundaryModeS, boundaryModeT, -1, boundaryRed, boundaryGreen, boundaryBlue, + boundaryAlpha); + } + + @Override + void updateTextureCubeMapFilterModes(Context ctx, int minFilter, int magFilter) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureCubeMapFilterModes()"); + + updateTextureFilterModes(ctx, GL2ES2.GL_TEXTURE_CUBE_MAP, minFilter, magFilter); + } + + @Override + void updateTextureCubeMapAnisotropicFilter(Context ctx, float degree) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateTextureCubeMapAnisotropicFilter()"); + + updateTextureAnisotropicFilter(ctx, GL2ES2.GL_TEXTURE_CUBE_MAP, degree); + } + + // ---------------------------------------------------------------------- + // + // Helper routines for above texture methods + // + + private void updateTexture2DImage(Context ctx, int target, int numLevels, int level, int textureFormat, int imageFormat, int width, + int height, int boundaryWidth, int dataType, Object data, boolean useAutoMipMap) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // FIXME: there is a new call glGenerateMipmap() which is only in ES2 not GL2 so on pure ES2 + // add back in checking for mipmap support under properties, then add that call after bind texture + // which mean the gl.glTexParameteri(target, GL2ES2.GL_GENERATE_MIPMAP, GL2ES2.GL_TRUE); are gone + //the extensions need to have an ES2 version + // the glPixelTransferf alpha madness which I don't understand can be deleted along with the texture formats + // the other look like they are ok, though the supported compressed will be different + + /* from ES2 spec + format + Specifies the format of the pixel data. + The following symbolic values are accepted: + GL_ALPHA, + GL_RGB, + GL_RGBA, + GL_LUMINANCE, and + GL_LUMINANCE_ALPHA. + type + Specifies the data type of the pixel data. + The following symbolic values are accepted: + GL_UNSIGNED_BYTE, + GL_UNSIGNED_SHORT_5_6_5, + GL_UNSIGNED_SHORT_4_4_4_4, and + GL_UNSIGNED_SHORT_5_5_5_1.*/ + + int internalFormat = 0; + + switch (textureFormat) + { + case Texture.INTENSITY: + new Throwable("Texture.INTENSITY not supported").printStackTrace(); + // internalFormat = GL2.GL_INTENSITY; + break; + case Texture.LUMINANCE: + internalFormat = GL2ES2.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL2ES2.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL2ES2.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL2ES2.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL2ES2.GL_RGBA; + break; + default: + assert false; + } + + // FIXME: sky goes black if this is the case (no mipmap) + // so to disable automipmap, must set it to false in texture I guess? + // see above glGenMipMap once on pure ES2 (if on pure ES2?) + if (useAutoMipMap) + { + throw new UnsupportedOperationException("Disable auto mip map generation!\n" + VALID_FORMAT_MESSAGE); + // gl.glTexParameteri(target, GL2ES2.GL_GENERATE_MIPMAP, GL2ES2.GL_TRUE); + } + else + { + // should default to false + // gl.glTexParameteri(target, GL2ES2.GL_GENERATE_MIPMAP, GL2ES2.GL_FALSE); + } + + int format = 0; + + if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) + { + + switch (imageFormat) + { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2ES2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL2ES2.GL_RGB; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (isExtensionAvailable.GL_EXT_abgr(gl)) + { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + } + else + { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL2ES2.GL_RGBA; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL2ES2.GL_LUMINANCE_ALPHA; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL2ES2.GL_ALPHA) + { + format = GL2ES2.GL_ALPHA; + } + else + { + format = GL2ES2.GL_LUMINANCE; + } + break; + /////////////////////////////////////////////////// PJPJPJ//////////////////// + // DXT uncompressed D3DFMT_A8R8G8B8 indicator + case GL2.GL_RGBA_S3TC: + internalFormat = GL2ES2.GL_RGBA; + format = GL2ES2.GL_RGBA; + break; + // notice fall through + // DXT + case GL2ES2.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL2ES2.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL2ES2.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + case GL2.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + // ETC2 + // https://www.khronos.org/opengles/sdk/docs/man3/html/glCompressedTexImage2D.xhtml + case GL3.GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL3.GL_COMPRESSED_RGB8_ETC2: + case GL3.GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL3.GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL3.GL_COMPRESSED_SRGB8_ETC2: + case GL3.GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + // ASTC + case GL3.GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + case GL3.GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + internalFormat = imageFormat; + format = -1;// indicate compressed + break; + ///////////////////////////////////// PJPJPJ////////////////////////////// + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + if (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) + { + gl.glTexImage2D(target, level, internalFormat, width, height, boundaryWidth, format, GL2ES2.GL_UNSIGNED_BYTE, + ByteBuffer.wrap((byte[]) data)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + + if (format == -1) + { + ByteBuffer bb = (ByteBuffer) data; + + gl.glCompressedTexImage2D(target, level, internalFormat, width, height, boundaryWidth, bb.limit(), bb); + + if (DO_OUTPUT_ERRORS) + { + int err = gl.glGetError(); + if (err != GL2ES2.GL_NO_ERROR) + { + System.out.println("glCompressedTexImage2D Error " + err + " target " + target + " level " + level + + " internalFormat " + internalFormat); + System.out.println("width " + width + " height " + height + " boundaryWidth " + boundaryWidth + " bb.limit() " + + bb.limit()); + // https://www.khronos.org/opengles/sdk/docs/man3/html/glCompressedTexImage2D.xhtml + } + } + } + else + { + gl.glTexImage2D(target, level, internalFormat, width, height, boundaryWidth, format, GL2ES2.GL_UNSIGNED_BYTE, + (Buffer) data); + if (DO_OUTPUT_ERRORS) + { + int err = gl.glGetError(); + if (err != GL2ES2.GL_NO_ERROR) + { + System.out.println("glTexImage2D Error " + err + " target " + target + " level " + level + " internalFormat " + + internalFormat); + System.out.println("width " + width + " height " + height + " boundaryWidth " + boundaryWidth + " format " + + format + " bb.limit() " + ((Buffer) data).limit()); + // https://www.khronos.org/opengles/sdk/docs/man3/html/glCompressedTexImage2D.xhtml + } + } + } + + } + } + else if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) + + { + + // FIXME: I suspect I will only support byte buffer images so perhaps the INT type can be deprecated? + System.out.println("IMAGE_DATA_TYPE_INT_ in use!"); + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + switch (imageFormat) + { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL2ES2.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2ES2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* + * This method only supports 3 and 4 components formats and INT + * types. + */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + if (forceAlphaToOne) + { + new Throwable("forceAlphaToOne").printStackTrace(); + } + + if (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) + { + gl.glTexImage2D(target, level, internalFormat, width, height, boundaryWidth, format, type, IntBuffer.wrap((int[]) data)); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + gl.glTexImage2D(target, level, internalFormat, width, height, boundaryWidth, format, type, (Buffer) data); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + } + else + { + assert false; + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + private void updateTexture2DSubImage(Context ctx, int target, int level, int xoffset, int yoffset, int textureFormat, int imageFormat, + int imgXOffset, int imgYOffset, int tilew, int width, int height, int dataType, Object data) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + if (imgXOffset > 0 || (width < tilew)) + { + new Throwable("forceAlphaToOne").printStackTrace(); + } + + int internalFormat = 0; + + switch (textureFormat) + { + case Texture.INTENSITY: + // internalFormat = GL2.GL_INTENSITY; + new Throwable("Texture.INTENSITY not supported").printStackTrace(); + break; + case Texture.LUMINANCE: + internalFormat = GL2ES2.GL_LUMINANCE; + break; + case Texture.ALPHA: + internalFormat = GL2ES2.GL_ALPHA; + break; + case Texture.LUMINANCE_ALPHA: + internalFormat = GL2ES2.GL_LUMINANCE_ALPHA; + break; + case Texture.RGB: + internalFormat = GL2ES2.GL_RGB; + break; + case Texture.RGBA: + internalFormat = GL2ES2.GL_RGBA; + break; + default: + assert false; + } + + if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) + { + int format = 0; + int numBytes = 0; + + switch (imageFormat) + { + case ImageComponentRetained.TYPE_BYTE_BGR: + format = GL2ES2.GL_BGR; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + format = GL2ES2.GL_RGB; + numBytes = 3; + break; + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (isExtensionAvailable.GL_EXT_abgr(gl)) + { // If its zero, should never come here! + format = GL2.GL_ABGR_EXT; + numBytes = 4; + } + else + { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + // all RGB types are stored as RGBA + format = GL2ES2.GL_RGBA; + numBytes = 4; + break; + case ImageComponentRetained.TYPE_BYTE_LA: + // all LA types are stored as LA8 + format = GL2ES2.GL_LUMINANCE_ALPHA; + numBytes = 2; + break; + case ImageComponentRetained.TYPE_BYTE_GRAY: + if (internalFormat == GL2ES2.GL_ALPHA) + { + format = GL2ES2.GL_ALPHA; + numBytes = 1; + } + else + { + format = GL2ES2.GL_LUMINANCE; + numBytes = 1; + } + break; + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + assert false; + return; + } + + ByteBuffer buf = null; + if (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) + { + buf = ByteBuffer.wrap((byte[]) data); + } + else + { + buf = (ByteBuffer) data; + } + + // offset by the imageOffset + buf.position((tilew * imgYOffset + imgXOffset) * numBytes); + gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, GL2ES2.GL_UNSIGNED_BYTE, buf); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) + { + int format = 0; + int type = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + switch (imageFormat) + { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + format = GL2ES2.GL_RGBA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + format = GL2ES2.GL_BGRA; + type = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* + * This method only supports 3 and 4 components formats and INT types. + */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + assert false; + return; + } + + if (forceAlphaToOne) + { + new Throwable("forceAlphaToOne").printStackTrace(); + } + + IntBuffer buf = null; + if (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) + { + buf = IntBuffer.wrap((int[]) data); + } + else + { + buf = (IntBuffer) data; + } + + // offset by the imageOffset + buf.position(tilew * imgYOffset + imgXOffset); + gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, buf); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + assert false; + return; + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + + } + + private static void updateTextureFilterModes(Context ctx, int target, int minFilter, int magFilter) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // FIXME: unclear whether we really need to set up the enum values + // in the JoglContext as is done in the native code depending on + // extension availability; maybe this is the defined fallback + // behavior of the various Java3D modes + + // set texture min filter + switch (minFilter) + { + case Texture.FASTEST: + case Texture.BASE_LEVEL_POINT: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2ES2.GL_NEAREST); + break; + case Texture.BASE_LEVEL_LINEAR: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2ES2.GL_LINEAR); + break; + case Texture.MULTI_LEVEL_POINT: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2ES2.GL_NEAREST_MIPMAP_NEAREST); + break; + case Texture.NICEST: + case Texture.MULTI_LEVEL_LINEAR: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MIN_FILTER, GL2ES2.GL_LINEAR_MIPMAP_LINEAR); + break; + case Texture.FILTER4: + // We should never get here as we've disabled the FILTER4 feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MIN_FILTER, + // GL2ES2.GL_FILTER4_SGIS); + break; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + // set texture mag filter + switch (magFilter) + { + case Texture.FASTEST: + case Texture.BASE_LEVEL_POINT: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, GL2ES2.GL_NEAREST); + break; + case Texture.NICEST: + case Texture.BASE_LEVEL_LINEAR: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, GL2ES2.GL_LINEAR); + break; + /* case Texture.LINEAR_SHARPEN: + // We should never get here as we've disabled the TEXTURE_SHARPEN feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_SHARPEN_SGIS); + break; + case Texture.LINEAR_SHARPEN_RGB: + // We should never get here as we've disabled the TEXTURE_SHARPEN feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_SHARPEN_COLOR_SGIS); + break; + case Texture.LINEAR_SHARPEN_ALPHA: + // We should never get here as we've disabled the TEXTURE_SHARPEN feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_SHARPEN_ALPHA_SGIS); + break; + case Texture2D.LINEAR_DETAIL: + // We should never get here as we've disabled the TEXTURE_DETAIL feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_DETAIL_SGIS); + break; + case Texture2D.LINEAR_DETAIL_RGB: + // We should never get here as we've disabled the TEXTURE_DETAIL feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_DETAIL_COLOR_SGIS); + break; + case Texture2D.LINEAR_DETAIL_ALPHA: + // We should never get here as we've disabled the TEXTURE_DETAIL feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_LINEAR_DETAIL_ALPHA_SGIS); + break; + case Texture.FILTER4: + // We should never get here as we've disabled the FILTER4 feature + // gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_MAG_FILTER, + // GL2ES2.GL_FILTER4_SGIS); + break;*/ + } + + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + void updateTextureBoundary(Context ctx, int target, int boundaryModeS, int boundaryModeT, int boundaryModeR, float boundaryRed, + float boundaryGreen, float boundaryBlue, float boundaryAlpha) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // except the R gear at bottom and boundary color + // but I'm dropping 3dtexture support so no probs and who cares about boundary color + // CLAMP_TO_BOUNDARYand GL_CLAMP are gone so now just set as GL_CLAMP_TO_EDGE + // FIXME: GL_MIRRORED_REPEAT needs to be added + + // set texture wrap parameter + switch (boundaryModeS) + { + case Texture.WRAP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, GL2ES2.GL_REPEAT); + break; + case Texture.CLAMP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, GL2ES2.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, GL2ES2.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, GL2ES2.GL_CLAMP_TO_EDGE); + break; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + switch (boundaryModeT) + { + case Texture.WRAP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, GL2ES2.GL_REPEAT); + break; + case Texture.CLAMP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, GL2ES2.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, GL2ES2.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, GL2ES2.GL_CLAMP_TO_EDGE); + break; + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + // applies to Texture3D only + /* if (boundaryModeR != -1) + { + switch (boundaryModeR) + { + case Texture.WRAP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, GL2ES2.GL_REPEAT); + break; + + case Texture.CLAMP: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, GL2ES2.GL_CLAMP); + break; + case Texture.CLAMP_TO_EDGE: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, GL2ES2.GL_CLAMP_TO_EDGE); + break; + case Texture.CLAMP_TO_BOUNDARY: + gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, GL2ES2.GL_CLAMP_TO_BORDER); + break; + } + }*/ + + //http://stackoverflow.com/questions/913801/glteximage2d + // texture border no longer supported on ES2 + + /*if (boundaryModeS == Texture.CLAMP || boundaryModeT == Texture.CLAMP || boundaryModeR == Texture.CLAMP) + { + // set texture border color + float[] color = new float[4]; + color[0] = boundaryRed; + color[1] = boundaryGreen; + color[2] = boundaryBlue; + color[3] = boundaryAlpha; + gl.glTexParameterfv(target, GL2ES2.GL_TEXTURE_BORDER_COLOR, color, 0); + }*/ + + } + + /* private static final String getFilterName(int filter) + { + switch (filter) + { + case Texture.FASTEST: + return "Texture.FASTEST"; + case Texture.NICEST: + return "Texture.NICEST"; + case Texture.BASE_LEVEL_POINT: + return "Texture.BASE_LEVEL_POINT"; + case Texture.BASE_LEVEL_LINEAR: + return "Texture.BASE_LEVEL_LINEAR"; + case Texture.MULTI_LEVEL_POINT: + return "Texture.MULTI_LEVEL_POINT"; + case Texture.MULTI_LEVEL_LINEAR: + return "Texture.MULTI_LEVEL_LINEAR"; + case Texture.FILTER4: + return "Texture.FILTER4"; + case Texture.LINEAR_SHARPEN: + return "Texture.LINEAR_SHARPEN"; + case Texture.LINEAR_SHARPEN_RGB: + return "Texture.LINEAR_SHARPEN_RGB"; + case Texture.LINEAR_SHARPEN_ALPHA: + return "Texture.LINEAR_SHARPEN_ALPHA"; + case Texture2D.LINEAR_DETAIL: + return "Texture.LINEAR_DETAIL"; + case Texture2D.LINEAR_DETAIL_RGB: + return "Texture.LINEAR_DETAIL_RGB"; + case Texture2D.LINEAR_DETAIL_ALPHA: + return "Texture.LINEAR_DETAIL_ALPHA"; + default: + return "(unknown)"; + } + }*/ + + // mapping from java enum to gl enum + private static final int[] _gl_textureCubeMapFace = { GL2ES2.GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL2ES2.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL2ES2.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL2ES2.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL2ES2.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL2ES2.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, }; + + // The following three methods are used in multi-pass case + + // native method for setting blend color + @Override + // part of TUS updateNative, though not in fact used + void setBlendColor(Context ctx, float red, float green, float blue, float alpha) + { + if (VERBOSE) + System.err.println("JoglPipeline.setBlendColor()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setBlendColor++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + if (isExtensionAvailable.GL_ARB_imaging(gl)) + { + gl.glBlendColor(red, green, blue, alpha); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + } + + // native method for setting blend func + @Override + // part of TUS updateNative + void setBlendFunc(Context ctx, int srcBlendFunction, int dstBlendFunction) + { + if (VERBOSE) + System.err.println("JoglPipeline.setBlendFunc()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setBlendFunc++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + Jogl2es2Context joglesctx = ((Jogl2es2Context) ctx); + + if (!MINIMISE_NATIVE_CALLS_TRANSPARENCY || (joglesctx.gl_state.glEnableGL_BLEND != true + || joglesctx.gl_state.srcBlendFunction != srcBlendFunction || joglesctx.gl_state.dstBlendFunction != dstBlendFunction)) + { + gl.glEnable(GL2ES2.GL_BLEND); + gl.glBlendFunc(blendFunctionTable[srcBlendFunction], blendFunctionTable[dstBlendFunction]); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.glEnableGL_BLEND = true; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.srcBlendFunction = srcBlendFunction; + if (MINIMISE_NATIVE_CALLS_TRANSPARENCY) + joglesctx.gl_state.dstBlendFunction = dstBlendFunction; + } + } + + // native method for setting light enables + @Override + void setLightEnables(Context ctx, long enableMask, int maxLights) + { + if (VERBOSE) + System.err.println("JoglPipeline.setLightEnables()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setLightEnables++; + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + for (int i = 0; i < maxLights; i++) + { + joglesctx.enabledLights[i] = ((enableMask & (1 << i)) != 0); + } + } + + // native method for setting scene ambient + @Override + void setSceneAmbient(Context ctx, float red, float green, float blue) + { + if (VERBOSE) + System.err.println("JoglPipeline.setSceneAmbient()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setSceneAmbient++; + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + joglesctx.currentAmbientColor.x = red; + joglesctx.currentAmbientColor.y = green; + joglesctx.currentAmbientColor.z = blue; + joglesctx.currentAmbientColor.w = 1.0f; + + } + + // native method for disabling modelClip + @Override + // this is called as a reset + void disableModelClip(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.disableModelClip()"); + + /*GL2 gl = context(ctx).getGL().getGL2(); + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // not supported + + gl.glDisable(GL2ES2.GL_CLIP_PLANE0); + gl.glDisable(GL2ES2.GL_CLIP_PLANE1); + gl.glDisable(GL2ES2.GL_CLIP_PLANE2); + gl.glDisable(GL2ES2.GL_CLIP_PLANE3); + gl.glDisable(GL2ES2.GL_CLIP_PLANE4); + gl.glDisable(GL2ES2.GL_CLIP_PLANE5);*/ + } + + // native method for activating a particular texture unit + @Override + void activeTextureUnit(Context ctx, int texUnitIndex) + { + if (VERBOSE) + System.err.println("JoglPipeline.activeTextureUnit(texUnitIndex= " + texUnitIndex + ")"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.activeTextureUnit++; + + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesContext.gl2es2; + + if (texUnitIndex >= 0) + { + if (!MINIMISE_NATIVE_CALLS_TEXTURE || (joglesContext.gl_state.glActiveTexture != (texUnitIndex + GL2ES2.GL_TEXTURE0))) + { + gl.glActiveTexture(texUnitIndex + GL2ES2.GL_TEXTURE0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TEXTURE) + joglesContext.gl_state.glActiveTexture = (texUnitIndex + GL2ES2.GL_TEXTURE0); + } + } + } + + // native method for setting default texture + @Override + + void resetTextureNative(Context ctx, int texUnitIndex) + { + if (VERBOSE) + System.err.println("JoglPipeline.resetTextureNative(texUnitIndex=" + texUnitIndex + ")"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.resetTextureNative++; + + Jogl2es2Context joglesContext = (Jogl2es2Context) ctx; + GL2ES2 gl = joglesContext.gl2es2; + + if (texUnitIndex >= 0) + { + if (!MINIMISE_NATIVE_CALLS_TEXTURE || (joglesContext.gl_state.glActiveTexture != (texUnitIndex + GL2ES2.GL_TEXTURE0))) + { + gl.glActiveTexture(texUnitIndex + GL2ES2.GL_TEXTURE0); + // TODO: should I enable these? + // gl.glBindTexture(GL2ES2.GL_TEXTURE_2D, 0);//-1 is no texture , 0 is default + // gl.glBindTexture(GL2ES2.GL_TEXTURE_CUBE_MAP, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + if (MINIMISE_NATIVE_CALLS_TEXTURE) + joglesContext.gl_state.glActiveTexture = (texUnitIndex + GL2ES2.GL_TEXTURE0); + } + } + + /* + gl.glDisable(GL2.GL_TEXTURE_1D); + gl.glDisable(GL.GL_TEXTURE_2D); + gl.glDisable(GL2.GL_TEXTURE_3D); + gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); + */ + } + + // The native method for setting the ModelView matrix. + @Override + void setModelViewMatrix(Context ctx, double[] viewMatrix, double[] modelMatrix) + { + if (VERBOSE) + System.err.println("JoglPipeline.setModelViewMatrix(viewMatrix= " + lineString(viewMatrix) + " modelMatrix= " + + lineString(modelMatrix) + ")"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setModelViewMatrix++; + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + + //joglesctx.matrixUtil.deburnV.set(viewMatrix); + //joglesctx.matrixUtil.deburnV.transpose();// now done in ffp by call to native + joglesctx.currentViewMat.set(viewMatrix); + + //joglesctx.matrixUtil.deburnM.set(modelMatrix); + //joglesctx.matrixUtil.deburnM.transpose();// now done in ffp by call to native + joglesctx.currentModelMat.set(modelMatrix); + + //Moved up into setffp and only calc'ed if requested + /* + + joglesctx.currentModelViewMat.mul(joglesctx.matrixUtil.deburnV, joglesctx.matrixUtil.deburnM); + + + joglesctx.currentModelViewProjMat.mul(joglesctx.currentProjMat, joglesctx.currentModelViewMat); + + // use only the upper left as it is a 3x3 rotation matrix + JoglesMatrixUtil.transposeInvert(joglesctx.currentModelViewMat, joglesctx.currentNormalMat); + */ + + } + + // The native method for setting the Projection matrix. + @Override + void setProjectionMatrix(Context ctx, double[] projMatrix) + { + if (VERBOSE) + System.err.println("JoglPipeline.setProjectionMatrix()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setProjectionMatrix++; + + Jogl2es2Context joglesctx = (Jogl2es2Context) ctx; + + // Invert the Z value in clipping coordinates because OpenGL uses + // left-handed clipping coordinates, while Java3D defines right-handed + // coordinates everywhere. + projMatrix[8] *= -1.0; + projMatrix[9] *= -1.0; + projMatrix[10] *= -1.0; + projMatrix[11] *= -1.0; + + joglesctx.currentProjMat.set(projMatrix); + // joglesctx.currentProjMat.transpose(); // done in set ffp now + + // reverse it back in case others use it + projMatrix[8] *= -1.0; + projMatrix[9] *= -1.0; + projMatrix[10] *= -1.0; + projMatrix[11] *= -1.0; + + } + + // The native method for setting the Viewport. + @Override + void setViewport(Context ctx, int x, int y, int width, int height) + { + if (VERBOSE) + System.err.println("JoglPipeline.setViewport()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setViewport++; + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setViewportTime = System.nanoTime(); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + gl.glViewport(x, y, width, height); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + private static String lineString(double[] da) + { + String ret = "double["; + for (double d : da) + ret += " " + d; + return ret + "]"; + } + + private static String lineString(float[] fa) + { + String ret = "float["; + for (float f : fa) + ret += " " + f; + return ret + "]"; + } + + @Override + void freeTexture(Context ctx, int id) + { + if (VERBOSE) + System.err.println("JoglPipeline.freeTexture()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.freeTexture++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + if (id > 0) + { + int[] tmp = new int[1]; + tmp[0] = id; + gl.glDeleteTextures(1, tmp, 0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + else + { + System.err.println("tried to delete tex with texid <= 0"); + } + } + + @Override + int generateTexID(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.generateTexID()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.generateTexID++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + int[] tmp = new int[] { -1 }; + gl.glGenTextures(1, tmp, 0); + + if (tmp[0] < 1) + return -1; + + return tmp[0]; + } + + // Set glDepthMask. + @Override + void setDepthBufferWriteEnable(Context ctx, boolean mode) + { + if (VERBOSE) + System.err.println("JoglPipeline.setDepthBufferWriteEnable()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setDepthBufferWriteEnable++; + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + if (mode) + { + gl.glDepthMask(true); + } + else + { + gl.glDepthMask(false); + } + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + // Set internal render mode to one of FIELD_ALL, FIELD_LEFT or + // FIELD_RIGHT. Note that it is up to the caller to ensure that + // stereo is available before setting the mode to FIELD_LEFT or + // FIELD_RIGHT. The boolean doubleBuffer is TRUE for double buffered mode, FALSE + // for single buffering. + @Override + void setRenderMode(Context ctx, int mode, boolean doubleBuffer) + { + if (VERBOSE) + System.err.println("JoglPipeline.setRenderMode()"); + + // UGLY HACK the render mode is set to Canvas3D.FIELD_ALL after all + // geoms are drawn + // so I take the opportunity to unbind the vertex array + + GL2ES3 gl2es3 = ((Jogl2es2Context) ctx).gl2es3; + if (gl2es3 != null) + { + gl2es3.glBindVertexArray(0); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + //GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + //no no drawBuffer, possibly just skip it for now + // ES2 is much more complex with buffers https://www.khronos.org/registry/gles/extensions/EXT/EXT_draw_buffers.txt + + /* int drawBuf = 0; + if (doubleBuffer) + { + drawBuf = GL2ES2.GL_BACK; + switch (mode) + { + case Canvas3D.FIELD_LEFT: + drawBuf = GL2ES2.GL_BACK_LEFT; + break; + case Canvas3D.FIELD_RIGHT: + drawBuf = GL2ES2.GL_BACK_RIGHT; + break; + case Canvas3D.FIELD_ALL: + drawBuf = GL2ES2.GL_BACK; + break; + } + } + else + { + drawBuf = GL2ES2.GL_FRONT; + switch (mode) + { + case Canvas3D.FIELD_LEFT: + drawBuf = GL2ES2.GL_FRONT_LEFT; + break; + case Canvas3D.FIELD_RIGHT: + drawBuf = GL2ES2.GL_FRONT_RIGHT; + break; + case Canvas3D.FIELD_ALL: + drawBuf = GL2ES2.GL_FRONT; + break; + } + } + + gl.glDrawBuffer(drawBuf);*/ + } + + private static int getFunctionValue(int func) + { + switch (func) + { + case RenderingAttributes.ALWAYS: + func = GL2ES2.GL_ALWAYS; + break; + case RenderingAttributes.NEVER: + func = GL2ES2.GL_NEVER; + break; + case RenderingAttributes.EQUAL: + func = GL2ES2.GL_EQUAL; + break; + case RenderingAttributes.NOT_EQUAL: + func = GL2ES2.GL_NOTEQUAL; + break; + case RenderingAttributes.LESS: + func = GL2ES2.GL_LESS; + break; + case RenderingAttributes.LESS_OR_EQUAL: + func = GL2ES2.GL_LEQUAL; + break; + case RenderingAttributes.GREATER: + func = GL2ES2.GL_GREATER; + break; + case RenderingAttributes.GREATER_OR_EQUAL: + func = GL2ES2.GL_GEQUAL; + break; + } + + return func; + } + + private static int getStencilOpValue(int op) + { + switch (op) + { + case RenderingAttributes.STENCIL_KEEP: + op = GL2ES2.GL_KEEP; + break; + case RenderingAttributes.STENCIL_ZERO: + op = GL2ES2.GL_ZERO; + break; + case RenderingAttributes.STENCIL_REPLACE: + op = GL2ES2.GL_REPLACE; + break; + case RenderingAttributes.STENCIL_INCR: + op = GL2ES2.GL_INCR; + break; + case RenderingAttributes.STENCIL_DECR: + op = GL2ES2.GL_DECR; + break; + case RenderingAttributes.STENCIL_INVERT: + op = GL2ES2.GL_INVERT; + break; + } + + return op; + } + + // --------------------------------------------------------------------- + + // + // TransparencyAttributesRetained methods + // + + private static final int[] blendFunctionTable = new int[TransparencyAttributes.MAX_BLEND_FUNC_TABLE_SIZE]; + + static + { + blendFunctionTable[TransparencyAttributes.BLEND_ZERO] = GL2ES2.GL_ZERO; + blendFunctionTable[TransparencyAttributes.BLEND_ONE] = GL2ES2.GL_ONE; + blendFunctionTable[TransparencyAttributes.BLEND_SRC_ALPHA] = GL2ES2.GL_SRC_ALPHA; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA] = GL2ES2.GL_ONE_MINUS_SRC_ALPHA; + blendFunctionTable[TransparencyAttributes.BLEND_DST_COLOR] = GL2ES2.GL_DST_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_DST_COLOR] = GL2ES2.GL_ONE_MINUS_DST_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_SRC_COLOR] = GL2ES2.GL_SRC_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_ONE_MINUS_SRC_COLOR] = GL2ES2.GL_ONE_MINUS_SRC_COLOR; + blendFunctionTable[TransparencyAttributes.BLEND_CONSTANT_COLOR] = GL2ES2.GL_CONSTANT_COLOR; + } + + // ---------------------------------------------------------------------- + // Helper private functions for Canvas3D + // + // USED BY CONTEXT QUERIER BELOW which is used for create new context + private static boolean getPropertiesFromCurrentContext(JoglContext ctx, GL2ES2 gl) + { + // FIXME: this is a heavily abridged set of the stuff in Canvas3D.c; + // probably need to pull much more in + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS, tmp, 0); + ctx.setMaxTexCoordSets(tmp[0]); + if (VirtualUniverse.mc.transparentOffScreen) + { + ctx.setAlphaClearValue(0.0f); + } + else + { + ctx.setAlphaClearValue(1.0f); + } + /*if (gl.isExtensionAvailable("GL_ARB_vertex_shader")) + { + gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_COORDS_ARB, tmp, 0); + ctx.setMaxTexCoordSets(tmp[0]); + }*/ + return true; + } + + // Used by createNewContext above + private static int[] extractVersionInfo(String versionString) + { + // FIXME: use the second flash regex system to get the first number out + // examples + // OpenGL ES 3.0 [email protected] AU@ (GIT@I3fa967cfef) + // 4.5.0 NVIDIA 353.82 + // System.err.println("versionString: " + versionString); + if (versionString.startsWith("OpenGL ES ")) + versionString = versionString.substring("OpenGL ES ".length()); + StringTokenizer tok = new StringTokenizer(versionString, ". "); + int major = Integer.valueOf(tok.nextToken()).intValue(); + int minor = Integer.valueOf(tok.nextToken()).intValue(); + + // See if there's vendor-specific information which might + // imply a more recent OpenGL version + tok = new StringTokenizer(versionString, " "); + if (tok.hasMoreTokens()) + { + tok.nextToken(); + if (tok.hasMoreTokens()) + { + Pattern p = Pattern.compile("\\D*(\\d+)\\.(\\d+)\\.?(\\d*).*"); + Matcher m = p.matcher(tok.nextToken()); + if (m.matches()) + { + int altMajor = Integer.valueOf(m.group(1)).intValue(); + int altMinor = Integer.valueOf(m.group(2)).intValue(); + // Avoid possibly confusing situations by requiring + // major version to match + if (altMajor == major && altMinor > minor) + { + minor = altMinor; + } + } + } + } + return new int[] { major, minor }; + } + + // Used by createNewContext above + private static void checkTextureExtensions(Canvas3D cv, JoglContext ctx, GL2ES2 gl, boolean gl13) + { + if (gl13) + { + + // FIXME: setting this to cv.maxTexCoordSets = 8; and cutting the + // rest out doesn't work! + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_MULTI_TEXTURE; + cv.multiTexAccelerated = true; + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxTextureUnits = tmp[0]; + cv.maxTexCoordSets = cv.maxTextureUnits; + } + + //Combine function gone as shader not FFP + /*if (gl.isExtensionAvailable("GL_ARB_texture_env_combine")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE; + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE_SUBTRACT; + } + else if (gl.isExtensionAvailable("GL_EXT_texture_env_combine")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE; + } + + if (gl.isExtensionAvailable("GL_ARB_texture_env_dot3") || gl.isExtensionAvailable("GL_EXT_texture_env_dot3")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_COMBINE_DOT3; + }*/ + + if (gl13) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_CUBE_MAP; + } + + if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_ANISOTROPIC_FILTER; + float[] tmp = new float[1]; + gl.glGetFloatv(GL2ES2.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, tmp, 0); + cv.anisotropicDegreeMax = tmp[0]; + } + + if (!VirtualUniverse.mc.enforcePowerOfTwo && gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_NON_POWER_OF_TWO; + } + + //autoMipMapGeneration disabled + /*if (gl.isExtensionAvailable("GL_SGIS_generate_mipmap")) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION; + }*/ + + } + + // Used by createNewContext above + private static void checkGLSLShaderExtensions(Canvas3D cv, JoglContext ctx, GL2ES2 gl, boolean hasgl13) + { + + // Force shaders to be disabled, since no multitexture support + if (!hasgl13) + return; + + if ((gl.isExtensionAvailable("GL_ARB_shader_objects") // + && gl.isExtensionAvailable("GL_ARB_shading_language_100")) // + || gl.isExtensionAvailable("GL_AMD_program_binary_Z400")) + { + + // FIXME: this isn't complete and would need to set up the + // JoglContext for dispatch of various routines such as those + // related to vertex attributes + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxTextureImageUnits = tmp[0]; + gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxVertexTextureImageUnits = tmp[0]; + gl.glGetIntegerv(GL2ES2.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, tmp, 0); + cv.maxCombinedTextureImageUnits = tmp[0]; + int vertexAttrOffset = VirtualUniverse.mc.glslVertexAttrOffset; + ctx.setGLSLVertexAttrOffset(vertexAttrOffset); + gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_ATTRIBS, tmp, 0); + cv.maxVertexAttrs = tmp[0]; + // decr count to allow for reserved vertex attrs + cv.maxVertexAttrs -= vertexAttrOffset; + if (cv.maxVertexAttrs < 0) + { + cv.maxVertexAttrs = 0; + } + cv.shadingLanguageGLSL = true; + } + } + + // Used by createNewContext above + private static void setupCanvasProperties(Canvas3D cv, JoglContext ctx, GL2ES2 gl) + { + // Note: this includes relevant portions from both the + // NativePipeline's getPropertiesFromCurrentContext and setupCanvasProperties + + // Reset all fields + cv.multiTexAccelerated = false; + cv.maxTextureUnits = 1; + cv.maxTexCoordSets = 1; + cv.maxTextureImageUnits = 0; + cv.maxVertexTextureImageUnits = 0; + cv.maxCombinedTextureImageUnits = 0; + cv.maxVertexAttrs = 0; + cv.extensionsSupported = 0; + cv.textureExtendedFeatures = 0; + cv.textureColorTableSize = 0; + cv.anisotropicDegreeMax = 0; + cv.textureBoundaryWidthMax = 0; + cv.textureWidthMax = 0; + cv.textureHeightMax = 0; + cv.texture3DWidthMax = 0; + cv.texture3DHeightMax = 0; + cv.texture3DDepthMax = 0; + cv.shadingLanguageGLSL = false; + + // Now make queries and set up these fields + String glVersion = gl.glGetString(GL2ES2.GL_VERSION); + String glVendor = gl.glGetString(GL2ES2.GL_VENDOR); + String glRenderer = gl.glGetString(GL2ES2.GL_RENDERER); + cv.nativeGraphicsVersion = glVersion; + cv.nativeGraphicsVendor = glVendor; + cv.nativeGraphicsRenderer = glRenderer; + + // PJPJPJPJ just for debug + // System.out.println("***glVersion " +glVersion); + // System.out.println("***glVendor " +glVendor); + // System.out.println("***glRenderer " +glRenderer); + // System.out.println( ctx.getGLContext().toString()); + + // find out the version, major and minor version number + int[] versionNumbers = extractVersionInfo(glVersion); + int major = versionNumbers[0]; + int minor = versionNumbers[1]; + + /////////////////////////////////////////// + // setup the graphics context properties // + + // NOTE: Java 3D now requires OpenGL 1.3 for full functionality. + // For backwards compatibility with certain older graphics cards and + // drivers (e.g., the Linux DRI driver for older ATI cards), + // we will try to run on OpenGL 1.2 in an unsupported manner. However, + // we will not attempt to use OpenGL extensions for any features that + // are available in OpenGL 1.3, specifically multitexture, multisample, + // and cube map textures. + + if (major < 1 || (major == 1 && minor < 2)) + { + throw new IllegalRenderingStateException( + "Java 3D ERROR : OpenGL 1.2 or better is required (GL_VERSION=" + major + "." + minor + ")"); + } + + boolean gl20 = false; + boolean gl14 = false; + boolean gl13 = false; + + if (major == 1) + { + if (minor == 2) + { + System.err.println("JAVA 3D: OpenGL 1.2 detected; will run with reduced functionality"); + } + if (minor >= 3) + { + gl13 = true; + } + if (minor >= 4) + { + gl14 = true; + } + } + else + // major >= 2 + { + gl13 = true; + gl14 = true; + gl20 = true; + } + + if (gl20) + { + assert gl13; + assert gl14; + assert gl.isExtensionAvailable("GL_VERSION_2_0"); + } + + if (gl14) + { + assert gl13; + assert gl.isExtensionAvailable("GL_VERSION_1_4"); + } + + if (gl13) + { + assert gl.isExtensionAvailable("GL_VERSION_1_3"); + } + + // Set up properties for OpenGL 1.3 + // cv.textureExtendedFeatures |= Canvas3D.TEXTURE_3D; + + // Note that we don't query for GL_ARB_imaging here + + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_LOD_RANGE; + + /* + if (gl14) + { + cv.textureExtendedFeatures |= Canvas3D.TEXTURE_AUTO_MIPMAP_GENERATION; + }*/ + + // look for OpenGL 2.0 features + // Fix to Issue 455 : Need to disable NPOT textures for older cards that claim to support it. + // Some older cards (e.g., Nvidia fx500 and ATI 9800) claim to support OpenGL 2.0. + // This means that these cards have to support non-power-of-two (NPOT) texture, + // but their lack the necessary HW force the vendors the emulate this feature in software. + // The result is a ~100x slower down compare to power-of-two textures. + // Do not check for gl20 but instead check of GL_ARB_texture_non_power_of_two extension string + // if (gl20) { + // if(!VirtualUniverse.mc.enforcePowerOfTwo) { + // cv.textureExtendedFeatures |= Canvas3D.TEXTURE_NON_POWER_OF_TWO; + // } + // } + + // Setup GL_EXT_abgr + if (gl.isExtensionAvailable("GL_EXT_abgr")) + { + cv.extensionsSupported |= Canvas3D.EXT_ABGR; + } + + // GL_BGR is always supported + cv.extensionsSupported |= Canvas3D.EXT_BGR; + + // Setup multisample + // FIXME: this is not correct for the Windows platform yet + //FIXME: this might be tricky, if I screw around the accum calls turn on again + // ES2 has new enable/disable on GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE + // and GL2 and ES2 both have glSampleCoverage calls + // Renderer line 1158 is teh guy that goes for accum if this is not set + if (gl13) + { + cv.extensionsSupported |= Canvas3D.MULTISAMPLE; + ctx.setHasMultisample(true); + } + + if ((cv.extensionsSupported & Canvas3D.MULTISAMPLE) != 0 && !VirtualUniverse.mc.implicitAntialiasing) + { + //with a bit of luck ES2 will ignore this call and leave sampling on + //gl.glDisable(GL2ES2.GL_MULTISAMPLE); + } + + // Check texture extensions + checkTextureExtensions(cv, ctx, gl, gl13); + + // Check shader extensions + checkGLSLShaderExtensions(cv, ctx, gl, gl13); + + cv.textureBoundaryWidthMax = 1; + + int[] tmp = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_SIZE, tmp, 0); + cv.textureWidthMax = tmp[0]; + cv.textureHeightMax = tmp[0]; + + /*tmp[0] = -1; + gl.glGetIntegerv(GL2ES2.GL_MAX_3D_TEXTURE_SIZE, tmp, 0); + cv.texture3DWidthMax = tmp[0]; + cv.texture3DHeightMax = tmp[0]; + cv.texture3DDepthMax = tmp[0];*/ + } + + // Not needed generally as transpose can be called on the inteface with gl + private static void copyTranspose(double[] src, double[] dst) + { + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + dst[4] = src[1]; + dst[5] = src[5]; + dst[6] = src[9]; + dst[7] = src[13]; + dst[8] = src[2]; + dst[9] = src[6]; + dst[10] = src[10]; + dst[11] = src[14]; + dst[12] = src[3]; + dst[13] = src[7]; + dst[14] = src[11]; + dst[15] = src[15]; + } + + @Override + void clear(Context ctx, float r, float g, float b, boolean clearStencil) + { + if (VERBOSE) + System.err.println("JoglPipeline.clear()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.clear++; + + Jogl2es2Context jctx = (Jogl2es2Context) ctx; + GL2ES2 gl = jctx.gl2es2; + + // Mask of which buffers to clear, this always includes color & depth + int clearMask = GL2ES2.GL_DEPTH_BUFFER_BIT | GL2ES2.GL_COLOR_BUFFER_BIT | GL2ES2.GL_STENCIL_BUFFER_BIT; + + // NOTE stencil always cleared + + gl.glDepthMask(true); + gl.glClearColor(r, g, b, jctx.getAlphaClearValue()); + gl.glClear(clearMask); + if (DO_OUTPUT_ERRORS) + outputErrors(ctx); + } + + /** + * This native method makes sure that the rendering for this canvas gets + * done now. + */ + @Override + // render is it's own thread so finish stops nothing + void syncRender(Context ctx, boolean wait) + { + + if (VERBOSE) + System.err.println("JoglPipeline.syncRender()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.syncRenderTime = System.nanoTime(); + + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + + // clean up any buffers that need freeing + doClearBuffers(ctx); + + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).outputPerFrameData(); + + // also seems to be ok, just do it as well + if (!NEVER_RELEASE_CONTEXT) + { + // if (wait) + // gl.glFinish(); + // else + gl.glFlush(); + } + + } + + // The native method for swapBuffers - onscreen only + @Override + void swapBuffers(Canvas3D cv, Context ctx, Drawable drawable) + { + if (VERBOSE) + System.err.println("JoglPipeline.swapBuffers()"); + + GLDrawable draw = drawable(drawable); + draw.swapBuffers(); + + ((Jogl2es2Context) ctx).gl_state.clear(); + } + + private static void outputErrors(Context ctx) + { + if (DO_OUTPUT_ERRORS) + { + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + int err = gl.glGetError(); + if (err != GL2ES2.GL_NO_ERROR) + { + // GL_NO_ERROR = 0x0 + // GL_INVALID_ENUM = 0x500; 1280 + // GL_INVALID_VALUE = 0x501; 1281 + // GL_INVALID_OPERATION = 0x502; 1282 + // GL_INVALID_FRAMEBUFFER_OPERATION = 0x506; 1286 + // GL_OUT_OF_MEMORY= 0x505; 1285 + // GL_STACK_UNDERFLOW 503? + // GL_STACK_OVERFLOW 504? + + // check for no current shader program (likely a switch between + // scenes or something) + if (err == GL2ES2.GL_INVALID_OPERATION) + { + int[] res = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_CURRENT_PROGRAM, res, 0); + // 0 is no current program + if (res[0] == 0) + return; + } + + System.err.println("JoglesPipeline GL error reported " + err); + StackTraceElement[] st = new Throwable().getStackTrace(); + if (st.length > 1) + System.err.println("Occured in " + st[1]); + + // seems to produce heaps? + /*err = gl.glGetError(); + if (err != GL2ES2.GL_NO_ERROR) + { + System.err.println("woooh second error too! "+ err); + err = gl.glGetError(); + if (err != GL2ES2.GL_NO_ERROR) + { + System.err.println("woooh third error too! "+ err); + } + }*/ + } + } + } + + // The native method that sets this ctx to be the current one + @Override + boolean useCtx(Context ctx, Drawable drawable) + { + if (!NEVER_RELEASE_CONTEXT || !currently_current) + { + if (VERBOSE) + System.err.println("JoglPipeline.useCtx()**********************************"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.useCtx++; + + GLContext context = context(ctx); + + if (context.getGLDrawable() == null) + System.out.println("context.getGLDrawable() == null!"); + + currently_current = true; + int res = context.makeCurrent(); + return (res != GLContext.CONTEXT_NOT_CURRENT); + } + return true; + } + + public static boolean currently_current = false; + + // Optionally release the context. Returns true if the context was released. + @Override + boolean releaseCtx(Context ctx) + { + if (!NEVER_RELEASE_CONTEXT) + { + if (VERBOSE) + System.err.println("JoglPipeline.releaseCtx()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.releaseCtx++; + GLContext context = context(ctx); + + if (context.isCurrent()) + context.release(); + } + return true; + } + + // --------------------------------------------------------------------- + + // + // MasterControl methods + // + + // Maximum lights supported by the native API + @Override + int getMaximumLights() + { + if (VERBOSE) + System.err.println("JoglPipeline.getMaximumLights()"); + + // FIXME: this isn't quite what the NativePipeline returns but + // is probably close enough + return 8; + } + + // ----------------------- Below here are initialization methods + + // Number of milliseconds to wait for windows to pop up on screen + private static final int WAIT_TIME = 1000; + // Configurable constant just in case we want to change this later + private static final int MIN_FRAME_SIZE = 1; + + private GLProfile profile; + + private Object mainThreadContext; // Fix for Bug 983 + + static boolean isOffscreenLayerSurfaceEnabled(Canvas3D cv) + { + if (cv.drawable == null || cv.offScreen) + return false; + + JoglDrawable joglDrawble = (JoglDrawable) cv.drawable; + JAWTWindow jawtwindow = (JAWTWindow) joglDrawble.getNativeWindow(); + if (jawtwindow == null) + return false; + + return jawtwindow.isOffscreenLayerSurfaceEnabled(); + } + + static boolean hasFBObjectSizeChanged(JoglDrawable jdraw, int width, int height) + { + if (!(jdraw.getGLDrawable() instanceof GLFBODrawable)) + return false; + + FBObject fboBack = ((GLFBODrawable) jdraw.getGLDrawable()).getFBObject(GL.GL_BACK); + if (fboBack == null) + return false; + + return (width != fboBack.getWidth() || height != fboBack.getHeight()); + } + + // Mac/JRE 7; called from Renderer when resizing is detected + // Implementation follows the approach in + // jogamp.opengl.GLDrawableHelper.resizeOffscreenDrawable(..) + @Override + void resizeOffscreenLayer(Canvas3D cv, int cvWidth, int cvHeight) + { + if (!isOffscreenLayerSurfaceEnabled(cv)) + return; + + JoglDrawable joglDrawable = (JoglDrawable) cv.drawable; + if (!hasFBObjectSizeChanged(joglDrawable, cvWidth, cvHeight)) + return; + + int newWidth = Math.max(1, cvWidth); + int newHeight = Math.max(1, cvHeight); + + GLDrawable glDrawble = joglDrawable.getGLDrawable(); + GLContext glContext = context(cv.ctx); + + // Assuming glContext != null + + final NativeSurface surface = glDrawble.getNativeSurface(); + final ProxySurface proxySurface = (surface instanceof ProxySurface) ? (ProxySurface) surface : null; + + final int lockRes = surface.lockSurface(); + + try + { + // propagate new size - seems not relevant here + if (proxySurface != null) + { + final UpstreamSurfaceHook ush = proxySurface.getUpstreamSurfaceHook(); + if (ush instanceof UpstreamSurfaceHook.MutableSize) + { + ((UpstreamSurfaceHook.MutableSize) ush).setSurfaceSize(newWidth, newHeight); + } + } + /* + else if(DEBUG) { + // we have to assume surface contains the new size already, hence size check @ bottom + System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen surface n.a. ProxySurface, but " + +ns.getClass().getName()+": "+ns); } + */ + + GL2ES2 gl = glContext.getGL().getGL2ES2(); + + // FBO : should be the default case on Mac OS X + if (glDrawble instanceof GLFBODrawable) + { + + // Resize GLFBODrawable + // TODO msaa gets lost + // ((GLFBODrawable)glDrawble).resetSize(gl); + + // Alternative: resize GL_BACK FBObject directly, + // if multisampled the FBO sink (GL_FRONT) will be resized + // before the swap is executed + int numSamples = ((GLFBODrawable) glDrawble).getChosenGLCapabilities().getNumSamples(); + FBObject fboObjectBack = ((GLFBODrawable) glDrawble).getFBObject(GL.GL_BACK); + fboObjectBack.reset(gl, newWidth, newHeight, numSamples/* , false */); // false = don't reset + // SamplingSinkFBO + // immediately + fboObjectBack.bind(gl); + + // If double buffered without antialiasing the GL_FRONT FBObject + // will be resized by glDrawble after the next swap-call + } + // pbuffer - not tested because Mac OS X 10.7+ supports FBO + else + { + // Create new GLDrawable (pbuffer) and update the coresponding + // GLContext + + final GLContext currentContext = GLContext.getCurrent(); + final GLDrawableFactory factory = glDrawble.getFactory(); + + // Ensure to sync GL command stream + if (currentContext != glContext) + { + glContext.makeCurrent(); + } + gl.glFinish(); + glContext.release(); + + if (proxySurface != null) + { + proxySurface.enableUpstreamSurfaceHookLifecycle(false); + } + + try + { + glDrawble.setRealized(false); + // New GLDrawable + glDrawble = factory.createGLDrawable(surface); + glDrawble.setRealized(true); + + joglDrawable.setGLDrawable(glDrawble); + } + finally + { + if (proxySurface != null) + { + proxySurface.enableUpstreamSurfaceHookLifecycle(true); + } + } + + glContext.setGLDrawable(glDrawble, true); // re-association + + // make current last current context + if (currentContext != null) + { + currentContext.makeCurrent(); + } + } + } + finally + { + surface.unlockSurface(); + } + } + + // Fix for Bug 983 + private void checkAppContext() + { + if (mainThreadContext == null) + return; + + try + { + // Check by reflection that sun.awt.AppContext.getAppContext() + // doesn't return null + // (required by ImageIO.write() and other JMF internal calls) to + // apply workaround proposed at + // http://stackoverflow.com/questions/17223304/appcontext-is-null-from-rmi-thread-with-java-7-update-25 + final Class<?> appContextClass = Class.forName("sun.awt.AppContext"); + if (appContextClass.getMethod("getAppContext").invoke(null) == null) + { + final Field field = appContextClass.getDeclaredField("threadGroup2appContext"); + field.setAccessible(true); + final Map threadGroup2appContext = (Map) field.get(null); + final ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup(); + threadGroup2appContext.put(currentThreadGroup, mainThreadContext); + } + } + catch (Throwable ex) + { + // Let's consider app context is not necessary for the program + } + // Don't need mainThreadContext anymore + mainThreadContext = null; + } + + // This is the native method for creating the underlying graphics context. + @Override + Context createNewContext(Canvas3D cv, Drawable drawable, Context shareCtx, boolean isSharedCtx, boolean offScreen) + { + if (VERBOSE) + System.err.println("JoglPipeline.createNewContext()"); + + checkAppContext(); + GLDrawable glDrawable = null; + GLContext glContext = null; + + if (offScreen) + { + glDrawable = drawable(cv.drawable); // cv.drawable != null, set in + // 'createOffScreenBuffer' + glContext = glDrawable.createContext(context(shareCtx)); + } + else + { + // determined in 'getBestConfiguration' + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(cv.graphicsConfiguration); + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration) gcInf0.getPrivateData(); + + // JAWTWindow + JAWTWindow nativeWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(cv, awtConfig); + nativeWindow.lockSurface(); + try + { + glDrawable = GLDrawableFactory.getFactory(profile).createGLDrawable(nativeWindow); + glContext = glDrawable.createContext(context(shareCtx)); + } + finally + { + nativeWindow.unlockSurface(); + } + + cv.drawable = new JoglDrawable(glDrawable, nativeWindow); + } + + // assuming that this only gets called after addNotify has been called + glDrawable.setRealized(true); + + // Apparently we are supposed to make the context current at this point + // and set up a bunch of properties + + // Work around for some low end graphics driver bug, such as Intel + // Chipset. + // Issue 324 : Lockup J3D program and throw exception using JOGL + // renderer + boolean failed = false; + int failCount = 0; + int MAX_FAIL_COUNT = 5; + do + { + failed = false; + int res = glContext.makeCurrent(); + if (res == GLContext.CONTEXT_NOT_CURRENT) + { + // System.err.println("makeCurrent fail : " + failCount); + failed = true; + ++failCount; + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + } + } + } + while (failed && (failCount < MAX_FAIL_COUNT)); + + if (failCount == MAX_FAIL_COUNT) + { + throw new IllegalRenderingStateException("Unable to make new context current after " + failCount + "tries"); + } + + GL2ES2 gl = glContext.getGL().getGL2ES2(); + + Jogl2es2Context ctx = new Jogl2es2Context(glContext); + + try + { + if (!getPropertiesFromCurrentContext(ctx, gl)) + { + throw new IllegalRenderingStateException("Unable to fetch properties from current OpenGL context"); + } + + if (!isSharedCtx) + { + // Set up fields in Canvas3D + setupCanvasProperties(cv, ctx, gl); + } + + // Enable rescale normal + // gl.glEnable(GL2.GL_RESCALE_NORMAL); + + // gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE); + gl.glDepthFunc(GL.GL_LEQUAL); + // gl.glEnable(GL2.GL_COLOR_MATERIAL); + + /* + * OpenGL specs: glReadBuffer specifies a color buffer as the source + * for subsequent glReadPixels. This source mode is initially + * GL_FRONT in single-buffered and GL_BACK in double-buffered + * configurations. + * + * We leave this mode unchanged in on-screen rendering and adjust it + * in off-screen rendering. See below. + */ + // gl.glReadBuffer(GL_FRONT); // off window, default for + // single-buffered non-stereo window + + // Issue 417: JOGL: Mip-mapped NPOT textures rendered incorrectly + // J3D images are aligned to 1 byte + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); + + // Workaround for issue 400: Enable separate specular by default + // gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, + // GL2.GL_SEPARATE_SPECULAR_COLOR); + + // Mac OS X / JRE 7 : onscreen rendering = offscreen rendering + // bind FBO + if (!offScreen && glDrawable instanceof GLFBODrawable) + { + GLFBODrawable fboDrawable = (GLFBODrawable) glDrawable; + // bind GLFBODrawable's drawing FBObject + // GL_BACK returns the correct FBOObject for single/double + // buffering, incl. multisampling + fboDrawable.getFBObject(GL.GL_BACK).bind(gl); + } + + // FBO or pbuffer + if (offScreen) + { + + // Final caps + GLCapabilitiesImmutable chosenCaps = glDrawable.getChosenGLCapabilities(); + + // FBO + if (glDrawable instanceof GLFBODrawable) + { + GLFBODrawable fboDrawable = (GLFBODrawable) glDrawable; + // bind GLFBODrawable's drawing FBObject + // GL_BACK returns the correct FBOObject for single/double + // buffering, incl. multisampling + fboDrawable.getFBObject(GL.GL_BACK).bind(gl); + } + // pbuffer + else + { + // Double buffering: read from back buffer, as we don't swap + // Even this setting is identical to the initially mode it + // is set explicitly + /* + * if (chosenCaps.getDoubleBuffered()) { + * gl.glReadBuffer(GL.GL_BACK); } else { + * gl.glReadBuffer(GL.GL_FRONT); } + */ + } + } + } + finally + { + glContext.release(); + } + + return ctx; + } + + @Override + void createQueryContext(Canvas3D cv, Drawable drawable, boolean offScreen, int width, int height) + { + if (VERBOSE) + System.err.println("JoglPipeline.createQueryContext()"); + + // Assumes createQueryContext is never called for a drawable != null + + if (offScreen) + { + + Drawable offDrawable = createOffScreenBuffer(cv, null, width, height); + + GLDrawable glDrawable = drawable(offDrawable); + + glDrawable.setRealized(true); + + GLContext glContext = glDrawable.createContext(null); + glContext.makeCurrent(); + + Jogl2es2Context ctx = new Jogl2es2Context(glContext); + + GL2ES2 gl = glContext.getGL().getGL2ES2(); + + // get current context properties + getPropertiesFromCurrentContext(ctx, gl); + // Set up fields in Canvas3D + setupCanvasProperties(cv, ctx, gl); + + // Done ! + + glContext.release(); + glContext.destroy(); + glDrawable.setRealized(false); + } + else + { + + // TODO can't find an implementation which avoids the use of + // QueryCanvas + // JOGL requires a visible Frame for an onscreen context + + Frame f = new Frame(); + f.setUndecorated(true); + f.setLayout(new BorderLayout()); + + ContextQuerier querier = new ContextQuerier(cv); + + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration) Canvas3D.graphicsConfigTable.get(cv.graphicsConfiguration) + .getPrivateData(); + + QueryCanvas canvas = new QueryCanvas(awtConfig, querier); + + f.add(canvas, BorderLayout.CENTER); + f.setSize(MIN_FRAME_SIZE, MIN_FRAME_SIZE); + f.setVisible(true); + canvas.doQuery(); + // Attempt to wait for the frame to become visible, but don't block + // the EDT + if (!EventQueue.isDispatchThread()) + { + synchronized (querier) + { + if (!querier.done()) + { + try + { + querier.wait(WAIT_TIME); + } + catch (InterruptedException e) + { + } + } + } + } + + disposeOnEDT(f); + } + } + + // This is the native for creating an offscreen buffer + @Override + Drawable createOffScreenBuffer(Canvas3D cv, Context ctx, int width, int height) + { + if (VERBOSE) + System.err.println("JoglPipeline.createOffScreenBuffer()"); + + // ctx unused, doesn't exist yet + + // Offscreen Canvas3D's JoglGraphicsConfiguration + JoglGraphicsConfiguration jgc = (JoglGraphicsConfiguration) cv.graphicsConfiguration; + + // Retrieve the offscreen Canvas3D's GraphicsConfigInfo + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(jgc); + + // Offscreen Canvas3D's graphics configuration, determined in + // 'getBestConfiguration' + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration) gcInf0.getPrivateData(); + + // TODO Offscreen Canvas3D's graphics devise, determined in + // 'getBestConfiguration' + // AbstractGraphicsDevice device = awtConfig.getScreen().getDevice(); // + // throws exception + // Alternative: default graphics device + AbstractGraphicsDevice device = GLDrawableFactory.getDesktopFactory().getDefaultDevice(); + + // Offscreen Canvas3D's capabilites, determined in + // 'getBestConfiguration' + GLCapabilities canvasCaps = (GLCapabilities) awtConfig.getChosenCapabilities(); + + // For further investigations : the user's GraphicsConfigTemplate3D (not + // used yet) + GraphicsConfigTemplate3D gct3D = gcInf0.getGraphicsConfigTemplate3D(); + + // Assuming that the offscreen drawable will/can support the chosen + // GLCapabilities + // of the offscreen Canvas3D + + final GLCapabilities offCaps = new GLCapabilities(profile); + offCaps.copyFrom(canvasCaps); + + // double bufffering only if scene antialiasing is required/preferred + // and supported + if (offCaps.getSampleBuffers() == false) + { + offCaps.setDoubleBuffered(false); + offCaps.setNumSamples(0); + } + + // Never stereo + offCaps.setStereo(false); + + // Set preferred offscreen drawable : framebuffer object (FBO) or + // pbuffer + offCaps.setFBO(true); // switches to pbuffer if FBO is not supported + // caps.setPBuffer(true); + + // !! a 'null' capability chooser; JOGL doesn't call a chooser for + // offscreen drawable + + // If FBO : 'offDrawable' is of type com.jogamp.opengl.GLFBODrawable + GLDrawable offDrawable = GLDrawableFactory.getFactory(profile).createOffscreenDrawable(device, offCaps, null, width, height); + + // !! these chosen caps are not final as long as the corresponding + // context is made current + // System.out.println("createOffScreenBuffer chosenCaps = " + + // offDrawable.getChosenGLCapabilities()); + + return new JoglDrawable(offDrawable, null); + } + + // 'destroyContext' is called first if context exists + @Override + void destroyOffScreenBuffer(Canvas3D cv, Context ctx, Drawable drawable) + { + if (VERBOSE) + System.err.println("JoglPipeline.destroyOffScreenBuffer()"); + + // it is done in 'destroyContext' + } + + // This is the native for reading the image from the offscreen buffer + @Override + void readOffScreenBuffer(Canvas3D cv, Context ctx, int format, int dataType, Object data, int width, int height) + { + if (VERBOSE) + System.err.println("JoglPipeline.readOffScreenBuffer()"); + + GLDrawable glDrawable = ((JoglDrawable) cv.drawable).getGLDrawable(); + GLCapabilitiesImmutable chosenCaps = glDrawable.getChosenGLCapabilities(); + GLFBODrawable fboDrawable = null; + + GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + + // If FBO + if (chosenCaps.isFBO()) + { + + fboDrawable = (GLFBODrawable) glDrawable; + + if (chosenCaps.getDoubleBuffered()) + { + // swap = resolve multisampling or flip back/front FBO + fboDrawable.swapBuffers(); + // unbind texture render target, we read from FBO + gl.glBindTexture(GL.GL_TEXTURE_2D, 0); + } + + // bind FBO for reading pixel data + // GL_FRONT = SamplingSinkFBO if double buffered and multisampled + // GL_FRONT if double buffered ( = GL_BAck before swap was called) + // GL_FRONT = GL_BACK if single buffered (single FBO) + + fboDrawable.getFBObject(GL.GL_FRONT).bind(gl); + } + // else pbuffer + + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, width); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + + int type = 0; + + if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_BYTE_BUFFER)) + { + + switch (format) + { + // GL_BGR + case ImageComponentRetained.TYPE_BYTE_BGR: + type = GL2.GL_BGR; + break; + case ImageComponentRetained.TYPE_BYTE_RGB: + type = GL.GL_RGB; + break; + // GL_ABGR_EXT + case ImageComponentRetained.TYPE_BYTE_ABGR: + if (gl.isExtensionAvailable("GL_EXT_abgr")) + { // If false, + // should never + // come here! + type = GL2.GL_ABGR_EXT; + } + else + { + assert false; + return; + } + break; + case ImageComponentRetained.TYPE_BYTE_RGBA: + type = GL.GL_RGBA; + break; + + /* + * This method only supports 3 and 4 components formats and BYTE + * types. + */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_INT_BGR: + case ImageComponentRetained.TYPE_INT_RGB: + case ImageComponentRetained.TYPE_INT_ARGB: + default: + throw new AssertionError("illegal format " + format); + } + + gl.glReadPixels(0, 0, width, height, type, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap((byte[]) data)); + + } + else if ((dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_ARRAY) + || (dataType == ImageComponentRetained.IMAGE_DATA_TYPE_INT_BUFFER)) + { + + int intType = GL2.GL_UNSIGNED_INT_8_8_8_8; + boolean forceAlphaToOne = false; + + switch (format) + { + /* GL_BGR */ + case ImageComponentRetained.TYPE_INT_BGR: /* Assume XBGR format */ + type = GL.GL_RGBA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + forceAlphaToOne = true; + break; + case ImageComponentRetained.TYPE_INT_RGB: /* Assume XRGB format */ + forceAlphaToOne = true; + /* Fall through to next case */ + case ImageComponentRetained.TYPE_INT_ARGB: + type = GL2.GL_BGRA; + intType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + break; + /* + * This method only supports 3 and 4 components formats and BYTE + * types. + */ + case ImageComponentRetained.TYPE_BYTE_LA: + case ImageComponentRetained.TYPE_BYTE_GRAY: + case ImageComponentRetained.TYPE_USHORT_GRAY: + case ImageComponentRetained.TYPE_BYTE_BGR: + case ImageComponentRetained.TYPE_BYTE_RGB: + case ImageComponentRetained.TYPE_BYTE_RGBA: + case ImageComponentRetained.TYPE_BYTE_ABGR: + default: + throw new AssertionError("illegal format " + format); + } + + /* Force Alpha to 1.0 if needed */ + // if (forceAlphaToOne) { + // gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 0.0f); + // gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 1.0f); + // } + + gl.glReadPixels(0, 0, width, height, type, intType, IntBuffer.wrap((int[]) data)); + + /* Restore Alpha scale and bias */ + // if (forceAlphaToOne) { + // gl.glPixelTransferf(GL2.GL_ALPHA_SCALE, 1.0f); + // gl.glPixelTransferf(GL2.GL_ALPHA_BIAS, 0.0f); + // } + } + else + { + throw new AssertionError("illegal image data type " + dataType); + } + + // If FBO + if (chosenCaps.isFBO()) + { + // bind FBO for drawing + fboDrawable.getFBObject(GL.GL_BACK).bind(gl); + } + } + + // Setup the full scene antialising in D3D and ogl when GL_ARB_multisamle + // supported + @Override + // looks like one time call in renderer.doWork + void setFullSceneAntialiasing(Context ctx, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.setFullSceneAntialiasing()"); + if (OUTPUT_PER_FRAME_STATS) + ((Jogl2es2Context) ctx).perFrameStats.setFullSceneAntialiasing++; + + JoglContext joglctx = (JoglContext) ctx; + GL2ES2 gl = ((Jogl2es2Context) ctx).gl2es2; + // PERF:GL2ES2 gl = context(ctx).getGL().getGL2ES2(); + // not supported in ES2, possibly just part of context generally + // http://stackoverflow.com/questions/27035893/antialiasing-in-opengl-es-2-0 + // FIXME: This is working under GL2ES2 but will need to change I think + // https://github.com/adrian110288/gdc2011-android-opengl/blob/master/src/com/example/gdc11/GDC11Activity.java + + if (joglctx.getHasMultisample() && !VirtualUniverse.mc.implicitAntialiasing) + { + if (enable) + { + System.out.println("I just set MULTISAMPLE just then"); + gl.glEnable(GL2ES2.GL_MULTISAMPLE); + } + else + { + gl.glDisable(GL2ES2.GL_MULTISAMPLE); + } + } + } + + // Native method to update separate specular color control + // looks like a one time call at the start of renderer.doWork + @Override + void updateSeparateSpecularColorEnable(Context ctx, boolean enable) + { + if (VERBOSE) + System.err.println("JoglPipeline.updateSeparateSpecularColorEnable()"); + + /* + * GL2 gl = context(ctx).getGL().getGL2(); //GL2ES2 gl = + * context(ctx).getGL().getGL2ES2(); // bound to be not supported as + * definately shader work now + * + * if (enable) { gl.glLightModeli(GL2ES2.GL_LIGHT_MODEL_COLOR_CONTROL, + * GL2ES2.GL_SEPARATE_SPECULAR_COLOR); } else { + * gl.glLightModeli(GL2ES2.GL_LIGHT_MODEL_COLOR_CONTROL, + * GL2ES2.GL_SINGLE_COLOR); } + */ + } + + // --------------------------------------------------------------------- + + // + // Canvas3D methods - native wrappers + // + + @Override + void destroyContext(Drawable drawable, Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.destroyContext()"); + + JoglDrawable joglDrawable = (JoglDrawable) drawable; + GLContext context = context(ctx); + + // possibly bug in marshmallow + // google belwo and see its a bug on marshmallow + // 04-19 00:33:36.941 18278-18322/com.ingenieur.ese.eseandroid + // E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb8dc6060 + // 04-19 00:33:37.182 18278-18278/com.ingenieur.ese.eseandroid + // D/JogAmp.NEWT: onStop.0 + + // after this a restart adn a swapBuffers call + // gl_window.getDelegatedDrawable().swapBuffers(); + // gets a + // com.jogamp.opengl.GLException: Error swapping buffers, eglError + // 0x300d, jogamp.opengl.egl.EGLDrawable[realized true, + + if (joglDrawable != null) + { + if (GLContext.getCurrent() == context) + { + context.release(); + } + context.destroy(); + + // assuming this is the right point at which to make this call + joglDrawable.getGLDrawable().setRealized(false); + + joglDrawable.destroyNativeWindow(); + } + } + + // This is the native method for getting the number of lights the underlying + // native library can support. + @Override + int getNumCtxLights(Context ctx) + { + if (VERBOSE) + System.err.println("JoglPipeline.getNumCtxLights()"); + + /* + * GL2ES2 gl = context(ctx).getGL().getGL2ES2(); int[] res = new int[1]; + * gl.glGetIntegerv(GL2ES2.GL_MAX_LIGHTS, res, 0); return res[0]; + */ + + // lights are now me! this is not called anyway + return 8; + } + + // True under Solaris, + // False under windows when display mode <= 8 bit + @Override + // probably pointless? + boolean validGraphicsMode() + { + if (VERBOSE) + System.err.println("JoglPipeline.validGraphicsMode()"); + + // FIXME: believe this should do exactly what the native code + // used to, but not 100% sure (also in theory should only run + // this code on the Windows platform? What about Mac OS X?) + /* + * DisplayMode currentMode = + * GraphicsEnvironment.getLocalGraphicsEnvironment(). + * getDefaultScreenDevice().getDisplayMode(); // Note: on X11 platforms, + * a bit depth < 0 simply indicates that // multiple visuals are + * supported on the current display mode + * + * if (VERBOSE) System.err.println(" Returning " + + * (currentMode.getBitDepth() < 0 || currentMode.getBitDepth() > 8)); + * + * return (currentMode.getBitDepth() < 0 || currentMode.getBitDepth() > + * 8); + */ + + return true; + } + + // Native method for eye lighting + @Override + void ctxUpdateEyeLightingEnable(Context ctx, boolean localEyeLightingEnable) + { + if (VERBOSE) + System.err.println("JoglPipeline.ctxUpdateEyeLightingEnable()"); + + // lighting is entirely in shaders now so this should affect nothing? + + /* + * GL2 gl = context(ctx).getGL().getGL2(); //GL2ES2 gl = + * context(ctx).getGL().getGL2ES2(); + * + * if (localEyeLightingEnable) { + * gl.glLightModeli(GL2ES2.GL_LIGHT_MODEL_LOCAL_VIEWER, GL2ES2.GL_TRUE); + * } else { gl.glLightModeli(GL2ES2.GL_LIGHT_MODEL_LOCAL_VIEWER, + * GL2ES2.GL_FALSE); } + */ + } + // --------------------------------------------------------------------- + + // + // DrawingSurfaceObject methods + // + + // Method to construct a new DrawingSurfaceObject + @Override + DrawingSurfaceObject createDrawingSurfaceObject(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.createDrawingSurfaceObject()"); + return new JoglDrawingSurfaceObject(cv); + } + + // Method to free the drawing surface object + @Override + // NOOP + void freeDrawingSurface(Canvas3D cv, DrawingSurfaceObject drawingSurfaceObject) + { + if (VERBOSE) + System.err.println("JoglPipeline.freeDrawingSurface()"); + // This method is a no-op + } + + // Method to free the native drawing surface object + @Override + // NOOP + void freeDrawingSurfaceNative(Object o) + { + if (VERBOSE) + System.err.println("JoglPipeline.freeDrawingSurfaceNative()"); + // This method is a no-op + } + + // ---------------------------------------------------------------------- + // Context-related routines + // + + // Helper used everywhere + // USED heaps + private static GLContext context(Context ctx) + { + if (ctx == null) + return null; + return ((JoglContext) ctx).getGLContext(); + } + + // Helper used everywhere + // USED a small amount + private static GLDrawable drawable(Drawable drawable) + { + if (drawable == null) + return null; + return ((JoglDrawable) drawable).getGLDrawable(); + } + + // ---------------------------------------------------------------------- + // General helper routines + // + + private static ThreadLocal<FloatBuffer> nioVertexTemp = new ThreadLocal<FloatBuffer>(); + private static ThreadLocal<DoubleBuffer> nioVertexDoubleTemp = new ThreadLocal<DoubleBuffer>(); + private static ThreadLocal<FloatBuffer> nioColorTemp = new ThreadLocal<FloatBuffer>(); + private static ThreadLocal<ByteBuffer> nioColorByteTemp = new ThreadLocal<ByteBuffer>(); + private static ThreadLocal<FloatBuffer> nioNormalTemp = new ThreadLocal<FloatBuffer>(); + private static ThreadLocal<FloatBuffer[]> nioTexCoordSetTemp = new ThreadLocal<FloatBuffer[]>(); + private static ThreadLocal<FloatBuffer[]> nioVertexAttrSetTemp = new ThreadLocal<FloatBuffer[]>(); + + // I think these are not used often as nio buffers sort it out + // but they are used a bit mind you + private static FloatBuffer getVertexArrayBuffer(float[] vertexArray) + { + return getVertexArrayBuffer(vertexArray, true); + } + + private static FloatBuffer getVertexArrayBuffer(float[] vertexArray, boolean copyData) + { + return getNIOBuffer(vertexArray, nioVertexTemp, copyData); + } + + private static DoubleBuffer getVertexArrayBuffer(double[] vertexArray) + { + return getVertexArrayBuffer(vertexArray, true); + } + + private static DoubleBuffer getVertexArrayBuffer(double[] vertexArray, boolean copyData) + { + return getNIOBuffer(vertexArray, nioVertexDoubleTemp, true); + } + + private static FloatBuffer getColorArrayBuffer(float[] colorArray) + { + return getColorArrayBuffer(colorArray, true); + } + + private static FloatBuffer getColorArrayBuffer(float[] colorArray, boolean copyData) + { + return getNIOBuffer(colorArray, nioColorTemp, true); + } + + private static ByteBuffer getColorArrayBuffer(byte[] colorArray) + { + return getColorArrayBuffer(colorArray, true); + } + + private static ByteBuffer getColorArrayBuffer(byte[] colorArray, boolean copyData) + { + return getNIOBuffer(colorArray, nioColorByteTemp, true); + } + + private static FloatBuffer getNormalArrayBuffer(float[] normalArray) + { + return getNormalArrayBuffer(normalArray, true); + } + + private static FloatBuffer getNormalArrayBuffer(float[] normalArray, boolean copyData) + { + return getNIOBuffer(normalArray, nioNormalTemp, true); + } + + private static FloatBuffer[] getTexCoordSetBuffer(Object[] texCoordSet) + { + return getNIOBuffer(texCoordSet, nioTexCoordSetTemp); + } + + private static FloatBuffer[] getVertexAttrSetBuffer(Object[] vertexAttrSet) + { + return getNIOBuffer(vertexAttrSet, nioVertexAttrSetTemp); + } + + private static FloatBuffer getNIOBuffer(float[] array, ThreadLocal<FloatBuffer> threadLocal, boolean copyData) + { + if (array == null) + { + return null; + } + FloatBuffer buf = threadLocal.get(); + if (buf == null) + { + buf = Buffers.newDirectFloatBuffer(array.length); + threadLocal.set(buf); + } + else + { + buf.rewind(); + if (buf.remaining() < array.length) + { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectFloatBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) + { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static DoubleBuffer getNIOBuffer(double[] array, ThreadLocal<DoubleBuffer> threadLocal, boolean copyData) + { + if (array == null) + { + return null; + } + DoubleBuffer buf = threadLocal.get(); + if (buf == null) + { + buf = Buffers.newDirectDoubleBuffer(array.length); + threadLocal.set(buf); + } + else + { + buf.rewind(); + if (buf.remaining() < array.length) + { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectDoubleBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) + { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static ByteBuffer getNIOBuffer(byte[] array, ThreadLocal<ByteBuffer> threadLocal, boolean copyData) + { + if (array == null) + { + return null; + } + ByteBuffer buf = threadLocal.get(); + if (buf == null) + { + buf = Buffers.newDirectByteBuffer(array.length); + threadLocal.set(buf); + } + else + { + buf.rewind(); + if (buf.remaining() < array.length) + { + int newSize = Math.max(2 * buf.remaining(), array.length); + buf = Buffers.newDirectByteBuffer(newSize); + threadLocal.set(buf); + } + } + if (copyData) + { + buf.put(array); + buf.rewind(); + } + return buf; + } + + private static FloatBuffer[] getNIOBuffer(Object[] array, ThreadLocal<FloatBuffer[]> threadLocal) + { + if (array == null) + { + return null; + } + FloatBuffer[] bufs = threadLocal.get(); + + // First resize array of FloatBuffers + if (bufs == null) + { + bufs = new FloatBuffer[array.length]; + threadLocal.set(bufs); + } + else if (bufs.length < array.length) + { + FloatBuffer[] newBufs = new FloatBuffer[array.length]; + System.arraycopy(bufs, 0, newBufs, 0, bufs.length); + bufs = newBufs; + threadLocal.set(bufs); + } + + // Now go down array of arrays, converting each into a direct + // FloatBuffer + for (int i = 0; i < array.length; i++) + { + float[] cur = (float[]) array[i]; + FloatBuffer buf = bufs[i]; + if (buf == null) + { + buf = Buffers.newDirectFloatBuffer(cur.length); + bufs[i] = buf; + } + else + { + buf.rewind(); + if (buf.remaining() < cur.length) + { + int newSize = Math.max(2 * buf.remaining(), cur.length); + buf = Buffers.newDirectFloatBuffer(newSize); + bufs[i] = buf; + } + } + buf.put(cur); + buf.rewind(); + } + + return bufs; + } + + // PJ: requesting caps is expensive, caps are unlikely to change during live + // times, so we use a variable that is lazy initialized for each + private isExtensionAvailable isExtensionAvailable = new isExtensionAvailable(); + + private class isExtensionAvailable + { + private int GL_EXT_abgr = 0; + + private boolean GL_EXT_abgr(GL2ES2 gl) + { + if (GL_EXT_abgr == 0) + GL_EXT_abgr = gl.isExtensionAvailable("GL_EXT_abgr") ? 1 : -1; + + return GL_EXT_abgr == 1; + } + + private int GL_ARB_imaging = 0; + + private boolean GL_ARB_imaging(GL2ES2 gl) + { + if (GL_ARB_imaging == 0) + GL_ARB_imaging = gl.isExtensionAvailable("GL_ARB_imaging") ? 1 : -1; + + return GL_ARB_imaging == 1; + } + + } + + // Methods to get actual capabilities from Canvas3D + @Override + boolean hasDoubleBuffer(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.hasDoubleBuffer()"); + if (VERBOSE) + System.err.println(" Returning " + caps(cv).getDoubleBuffered()); + return caps(cv).getDoubleBuffered(); + } + + @Override + boolean hasStereo(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.hasStereo()"); + if (VERBOSE) + System.err.println(" Returning " + caps(cv).getStereo()); + return caps(cv).getStereo(); + } + + @Override + int getStencilSize(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.getStencilSize()"); + if (VERBOSE) + System.err.println(" Returning " + caps(cv).getStencilBits()); + return caps(cv).getStencilBits(); + } + + @Override + boolean hasSceneAntialiasingMultisample(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.hasSceneAntialiasingMultisample()"); + if (VERBOSE) + System.err.println(" Returning " + caps(cv).getSampleBuffers()); + + return caps(cv).getSampleBuffers(); + } + + @Override + boolean hasSceneAntialiasingAccum(Canvas3D cv) + { + if (VERBOSE) + System.err.println("JoglPipeline.hasSceneAntialiasingAccum()"); + // Accum style antialiasing is gone + return false; + /* + * GLCapabilities caps = caps(cv); if (VERBOSE) System.err .println( + * " Returning " + (caps.getAccumRedBits() > 0 && + * caps.getAccumGreenBits() > 0 && caps.getAccumBlueBits() > 0)); return + * (caps.getAccumRedBits() > 0 && caps.getAccumGreenBits() > 0 && + * caps.getAccumBlueBits() > 0); + */ + + } + + // Used to get caps for the canvas3d + private static GLCapabilities caps(Canvas3D ctx) + { + if (ctx.drawable != null) + { + // latest state for on- and offscreen drawables + return (GLCapabilities) drawable(ctx.drawable).getChosenGLCapabilities(); + } + else + { + // state at the time of 'getBestConfiguration' + return ((JoglGraphicsConfiguration) ctx.graphicsConfiguration).getGLCapabilities(); + } + } + + // AWT AWT AWT AWT AWT AWT AWT AWT AWT + // --------------------------------------------------------------------- + + // Determine whether specified graphics config is supported by pipeline + @Override + boolean isGraphicsConfigSupported(GraphicsConfigTemplate3D gct, GraphicsConfiguration gc) + { + if (VERBOSE) + System.err.println("JoglPipeline.isGraphicsConfigSupported()"); + + // FIXME: it looks like this method is implemented incorrectly + // in the existing NativePipeline in both the Windows and X11 + // ports. According to the semantics of the javadoc, it looks + // like this method is supposed to figure out the OpenGL + // capabilities which would be requested by the passed + // GraphicsConfiguration object were it to be used, and see + // whether it is possible to create a context with them. + // Instead, on both platforms, the implementations basically set + // up a query based on the contents of the + // GraphicsConfigTemplate3D object, using the + // GraphicsConfiguration object only to figure out on which + // GraphicsDevice and screen we're making the request, and see + // whether it's possible to choose an OpenGL pixel format based + // on that information. This makes this method less useful and + // we can probably just safely return true here uniformly + // without breaking anything. + return true; + } + // + // Canvas3D / GraphicsConfigTemplate3D methods - logic dealing with + // native graphics configuration or drawing surface + // + + // Return a graphics config based on the one passed in. Note that we can + // assert that the input config is non-null and was created from a + // GraphicsConfigTemplate3D. + // This method must return a valid GraphicsConfig, or else it must throw + // an exception if one cannot be returned. + @Override + // during Canvas3D init + GraphicsConfiguration getGraphicsConfig(GraphicsConfiguration gconfig) + { + + if (VERBOSE) + System.err.println("JoglPipeline.getGraphicsConfig()"); + + GraphicsConfigInfo gcInf0 = Canvas3D.graphicsConfigTable.get(gconfig); + AWTGraphicsConfiguration awtConfig = (AWTGraphicsConfiguration) gcInf0.getPrivateData(); + + return awtConfig.getAWTGraphicsConfiguration(); + } + + private enum DisabledCaps + { + STEREO, AA, DOUBLE_BUFFER, + } + + // Get best graphics config from pipeline + @Override + GraphicsConfiguration getBestConfiguration(GraphicsConfigTemplate3D gct, GraphicsConfiguration[] gc) + { + if (VERBOSE) + System.err.println("JoglPipeline.getBestConfiguration()"); + + // Create a GLCapabilities based on the GraphicsConfigTemplate3D + final GLCapabilities caps = new GLCapabilities(profile); + + caps.setDoubleBuffered(gct.getDoubleBuffer() != GraphicsConfigTemplate.UNNECESSARY); + + caps.setStereo(gct.getStereo() != GraphicsConfigTemplate.UNNECESSARY); + + // Scene antialiasing only if double buffering + if (gct.getSceneAntialiasing() != GraphicsConfigTemplate.UNNECESSARY && gct.getDoubleBuffer() != GraphicsConfigTemplate.UNNECESSARY) + { + caps.setSampleBuffers(true); + caps.setNumSamples(2); + } + else + { + caps.setSampleBuffers(false); + caps.setNumSamples(0); + } + + caps.setDepthBits(gct.getDepthSize()); + caps.setStencilBits(gct.getStencilSize()); + + caps.setRedBits(Math.max(5, gct.getRedSize())); + caps.setGreenBits(Math.max(5, gct.getGreenSize())); + caps.setBlueBits(Math.max(5, gct.getBlueSize())); + + // Issue 399: Request alpha buffer if transparentOffScreen is set + if (VirtualUniverse.mc.transparentOffScreen) + { + caps.setAlphaBits(1); + } + + // Add PREFERRED capabilities in order of least to highest priority and + // we will try disabling them + ArrayList<DisabledCaps> capsToDisable = new ArrayList<DisabledCaps>(); + + if (gct.getStereo() == GraphicsConfigTemplate.PREFERRED) + { + capsToDisable.add(DisabledCaps.STEREO); + } + + if (gct.getSceneAntialiasing() == GraphicsConfigTemplate.PREFERRED) + { + capsToDisable.add(DisabledCaps.AA); + } + + // if AA is required, so is double buffering. + if (gct.getSceneAntialiasing() != GraphicsConfigTemplate.REQUIRED && gct.getDoubleBuffer() == GraphicsConfigTemplate.PREFERRED) + { + capsToDisable.add(DisabledCaps.DOUBLE_BUFFER); + } + + // Pick an arbitrary graphics device. + GraphicsDevice device = gc[0].getDevice(); + AbstractGraphicsScreen screen = (device != null) ? AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT) + : AWTGraphicsScreen.createDefault(); + + // Create a Frame and dummy GLCanvas to perform eager pixel format + // selection + + // Note that we loop in similar fashion to the NativePipeline's + // native code in the situation where we need to disable certain + // capabilities which aren't required + boolean tryAgain = true; + CapabilitiesCapturer capturer = null; + AWTGraphicsConfiguration awtConfig = null; + while (tryAgain) + { + Frame f = new Frame(); + f.setUndecorated(true); + f.setLayout(new BorderLayout()); + capturer = new CapabilitiesCapturer(); + try + { + awtConfig = createAwtGraphicsConfiguration(caps, capturer, screen); + QueryCanvas canvas = new QueryCanvas(awtConfig, capturer); + f.add(canvas, BorderLayout.CENTER); + f.setSize(MIN_FRAME_SIZE, MIN_FRAME_SIZE); + f.setVisible(true); + canvas.doQuery(); + if (DEBUG_CONFIG) + { + System.err.println("Waiting for CapabilitiesCapturer"); + } + // Try to wait for result without blocking EDT + if (!EventQueue.isDispatchThread()) + { + synchronized (capturer) + { + if (!capturer.done()) + { + try + { + capturer.wait(WAIT_TIME); + } + catch (InterruptedException e) + { + } + } + } + } + disposeOnEDT(f); + tryAgain = false; + } + catch (GLException e) + { + // Failure to select a pixel format; try switching off one + // of the only-preferred capabilities + if (capsToDisable.size() == 0) + { + tryAgain = false; + } + else + { + switch (capsToDisable.remove(0)) + { + case STEREO: + caps.setStereo(false); + break; + case AA: + caps.setSampleBuffers(false); + break; + case DOUBLE_BUFFER: + caps.setDoubleBuffered(false); + break; + } + awtConfig = null; + } + } + } + int chosenIndex = capturer.getChosenIndex(); + GLCapabilities chosenCaps = null; + if (chosenIndex < 0) + { + if (DEBUG_CONFIG) + { + System.err.println("CapabilitiesCapturer returned invalid index"); + } + // It's possible some platforms or implementations might not + // support the GLCapabilitiesChooser mechanism; feed in the + // same GLCapabilities later which we gave to the selector + chosenCaps = caps; + } + else + { + if (DEBUG_CONFIG) + { + System.err.println("CapabilitiesCapturer returned index=" + chosenIndex); + } + chosenCaps = capturer.getCapabilities(); + } + + // FIXME chosenIndex isn't used anymore, used -1 instead of finding it. + JoglGraphicsConfiguration config = new JoglGraphicsConfiguration(chosenCaps, chosenIndex, device); + + // FIXME: because of the fact that JoglGraphicsConfiguration + // doesn't override hashCode() or equals(), we will basically be + // creating a new one each time getBestConfiguration() is + // called; in theory, we should probably map the same + // GLCapabilities on the same GraphicsDevice to the same + // JoglGraphicsConfiguration object + + // Cache the GraphicsTemplate3D + GraphicsConfigInfo gcInf0 = new GraphicsConfigInfo(gct); + gcInf0.setPrivateData(awtConfig); + + synchronized (Canvas3D.graphicsConfigTable) + { + Canvas3D.graphicsConfigTable.put(config, gcInf0); + } + + return config; + } + + private boolean checkedForGetScreenMethod = false; + private Method getScreenMethod = null; + + @Override + // Screen3D class calls during init and that init is only called in the init + // of Canvas3D + // Notice this is using reflection on the GraphicsDevice! + int getScreen(final GraphicsDevice graphicsDevice) + { + if (VERBOSE) + System.err.println("JoglPipeline.getScreen()"); + + if (!checkedForGetScreenMethod) + { + // All of the Sun GraphicsDevice implementations have a method + // int getScreen(); + // which we want to call reflectively if it's available. + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() + { + try + { + getScreenMethod = graphicsDevice.getClass().getDeclaredMethod("getScreen", new Class[] {}); + getScreenMethod.setAccessible(true); + } + catch (Exception e) + { + } + checkedForGetScreenMethod = true; + return null; + } + }); + } + + if (getScreenMethod != null) + { + try + { + return ((Integer) getScreenMethod.invoke(graphicsDevice, (Object[]) null)).intValue(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + return 0; + } + + // getBestConfiguration ONLY below here + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + // Non pipeline interface too + + // ---------------------------------------------------------------------- + // Helper classes and methods to support query context functionality + // and pixel format selection + // Used by Query Canvas apabilitiesCapturer and therefore only get best + // configuration + private interface ExtendedCapabilitiesChooser extends GLCapabilitiesChooser + { + public void init(GLContext context); + } + + // Canvas subclass to help with various query operations such as the + // "query context" mechanism and pixel format selection. + // Must defeat and simplify the single-threading behavior of JOGL's + // GLCanvas in order to be able to set up a temporary pixel format + // and OpenGL context. Apparently simply turning off the + // single-threaded mode isn't enough to do this. + + // Used by get best configuration + private final class QueryCanvas extends Canvas + { + + private GLDrawable glDrawable; + private ExtendedCapabilitiesChooser chooser; + private boolean alreadyRan; + + private AWTGraphicsConfiguration awtConfig = null; + private JAWTWindow nativeWindow = null; + + private QueryCanvas(AWTGraphicsConfiguration awtConfig, ExtendedCapabilitiesChooser chooser) + { + // The platform-specific GLDrawableFactory will only provide a + // non-null GraphicsConfiguration on platforms where this is + // necessary (currently only X11, as Windows allows the pixel + // format of the window to be set later and Mac OS X seems to + // handle this very differently than all other platforms). On + // other platforms this method returns null; it is the case (at + // least in the Sun AWT implementation) that this will result in + // equivalent behavior to calling the no-arg super() constructor + // for Canvas. + super(awtConfig.getAWTGraphicsConfiguration()); + + this.awtConfig = awtConfig; + this.chooser = chooser; + } + + @Override + public void addNotify() + { + super.addNotify(); + + nativeWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + nativeWindow.lockSurface(); + try + { + glDrawable = GLDrawableFactory.getFactory(profile).createGLDrawable(nativeWindow); + } + finally + { + nativeWindow.unlockSurface(); + } + + glDrawable.setRealized(true); + } + + // It seems that at least on Mac OS X we need to do the OpenGL + // context-related work outside of the addNotify call because the + // Canvas hasn't been resized to a non-zero size by that point + private void doQuery() + { + if (alreadyRan) + return; + GLContext context = glDrawable.createContext(null); + int res = context.makeCurrent(); + if (res != GLContext.CONTEXT_NOT_CURRENT) + { + try + { + chooser.init(context); + } + finally + { + context.release(); + } + } + context.destroy(); + alreadyRan = true; + + glDrawable.setRealized(false); + nativeWindow.destroy(); + } + } + + // Used by get best configuration + private static AWTGraphicsConfiguration createAwtGraphicsConfiguration(GLCapabilities capabilities, CapabilitiesChooser chooser, + AbstractGraphicsScreen screen) + { + GraphicsConfigurationFactory factory = GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilities.class); + AWTGraphicsConfiguration awtGraphicsConfiguration = (AWTGraphicsConfiguration) factory.chooseGraphicsConfiguration(capabilities, + capabilities, chooser, screen, VisualIDHolder.VID_UNDEFINED); + return awtGraphicsConfiguration; + } + + // Used in conjunction with IndexCapabilitiesChooser in pixel format + // selection -- see getBestConfiguration + + // Used by getBestConfiguration + private static class CapabilitiesCapturer extends DefaultGLCapabilitiesChooser implements ExtendedCapabilitiesChooser + { + private boolean done; + private GLCapabilities capabilities; + private int chosenIndex = -1; + + public boolean done() + { + return done; + } + + public GLCapabilities getCapabilities() + { + return capabilities; + } + + public int getChosenIndex() + { + return chosenIndex; + } + + public int chooseCapabilities(GLCapabilities desired, GLCapabilities[] available, int windowSystemRecommendedChoice) + { + int res = super.chooseCapabilities(desired, Arrays.asList(available), windowSystemRecommendedChoice); + capabilities = available[res]; + chosenIndex = res; + markDone(); + return res; + } + + @Override + public void init(GLContext context) + { + // Avoid hanging things up for several seconds + kick(); + } + + private void markDone() + { + synchronized (this) + { + done = true; + notifyAll(); + } + } + + private void kick() + { + synchronized (this) + { + notifyAll(); + } + } + } + + // Used to support the query context mechanism -- needs to be more + // than just a GLCapabilitiesChooser + + // ONLY used by createQuerycontext above, hence unused + // What possibly invoked via some sort of crazy reflect, do not delete + // can't seem to get it invoked now? + private final class ContextQuerier extends DefaultGLCapabilitiesChooser implements ExtendedCapabilitiesChooser + { + private Canvas3D canvas; + private boolean done; + + public ContextQuerier(Canvas3D canvas) + { + this.canvas = canvas; + } + + public boolean done() + { + return done; + } + + @Override + public void init(GLContext context) + { + // This is basically a temporary, NOTE not JoglesContext either + JoglContext jctx = new JoglContext(context); + GL2ES2 gl = context.getGL().getGL2ES2(); + // Set up various properties + if (getPropertiesFromCurrentContext(jctx, gl)) + { + setupCanvasProperties(canvas, jctx, gl); + } + markDone(); + } + + private void markDone() + { + synchronized (this) + { + done = true; + notifyAll(); + } + } + } + + // used by getBestConfiguration above + private static void disposeOnEDT(final Frame f) + { + Runnable r = new Runnable() { + @Override + public void run() + { + f.setVisible(false); + f.dispose(); + } + }; + if (!EventQueue.isDispatchThread()) + { + EventQueue.invokeLater(r); + } + else + { + r.run(); + } + } + +} diff --git a/src/main/java/org/jogamp/java3d/MasterControl.java b/src/main/java/org/jogamp/java3d/MasterControl.java index 5d1002f..e806caa 100644 --- a/src/main/java/org/jogamp/java3d/MasterControl.java +++ b/src/main/java/org/jogamp/java3d/MasterControl.java @@ -826,6 +826,8 @@ private static String getProperty(final String prop) { // Use default pipeline } else if (rendStr.equals("jogl")) { pipelineType = Pipeline.Type.JOGL; + } else if (rendStr.equals("jogl2es2")) { + pipelineType = Pipeline.Type.JOGL2ES2; } else if (rendStr.equals("noop")) { pipelineType = Pipeline.Type.NOOP; } else { diff --git a/src/main/java/org/jogamp/java3d/Pipeline.java b/src/main/java/org/jogamp/java3d/Pipeline.java index 57567f3..7f6f248 100644 --- a/src/main/java/org/jogamp/java3d/Pipeline.java +++ b/src/main/java/org/jogamp/java3d/Pipeline.java @@ -36,10 +36,12 @@ import java.nio.FloatBuffer; * pipeline methods are defined here. */ abstract class Pipeline { - // Supported rendering pipelines + // Supported rendering pipelines enum Type { // Java rendering pipeline using Java Bindings for OpenGL JOGL, + + JOGL2ES2, // No-op rendering pipeline NOOP, @@ -72,6 +74,8 @@ public Pipeline run() { switch (pipeType) { case JOGL: return (Pipeline)Class.forName("org.jogamp.java3d.JoglPipeline").newInstance(); + case JOGL2ES2: + return (Pipeline)Class.forName("org.jogamp.java3d.Jogl2es2Pipeline").newInstance(); case NOOP: return (Pipeline)Class.forName("org.jogamp.java3d.NoopPipeline").newInstance(); } @@ -129,6 +133,8 @@ public Pipeline run() { switch (pipelineType) { case JOGL: return "JOGL"; + case JOGL2ES2: + return "JOGLES"; case NOOP: return "NOOP"; default: @@ -144,6 +150,8 @@ public Pipeline run() { switch (pipelineType) { case JOGL: return "OpenGL"; + case JOGL2ES2: + return "OpenGLES"; case NOOP: return "None"; default: diff --git a/src/main/java/org/jogamp/java3d/SceneGraphObject.java b/src/main/java/org/jogamp/java3d/SceneGraphObject.java index 8755fb3..9a4b341 100644 --- a/src/main/java/org/jogamp/java3d/SceneGraphObject.java +++ b/src/main/java/org/jogamp/java3d/SceneGraphObject.java @@ -72,7 +72,7 @@ public abstract class SceneGraphObject extends Object { SceneGraphObjectRetained retained; // This object's capability bits - private long capabilityBits = 0L; + long capabilityBits = 0L; // This object's capabilityIsFrequent bits private long capabilityIsFrequentBits = ~0L; |