io7m
io7m-jcanephora 0.51.0
io7m-jcanephora 0.51.0 Documentation

- 1. Package Information
  - 1.1. Orientation
  - 1.2. Requirements
  - 1.3. Installation
  - 1.4. Platform Specific Issues
  - 1.5. License
- 2. Design/Rationale
  - 2.1. Problems with OpenGL
  - 2.2. Goals
  - 2.3. API Design
- 3. jcanephora Core Interfaces
  - 3.1. Overview
  - 3.2. JCGLContextType
  - 3.3. JCGLContextType
  - 3.4. JCGLArrayBuffersType
  - 3.5. JCGLArrayObjectsType
  - 3.6. JCGLBlendingType
  - 3.7. JCGLClearType
  - 3.8. JCGLColorBufferMaskingType
  - 3.9. JCGLDepthBuffersType
  - 3.10. JCGLDrawType
  - 3.11. JCGLFramebuffersType
  - 3.12. JCGLIndexBuffersType
  - 3.13. JCGLPolygonModesType
  - 3.14. JCGLScissorType
  - 3.15. JCGLShadersType
  - 3.16. JCGLStencilBuffersType
  - 3.17. JCGLTexturesType
  - 3.18. JCGLTimersType
  - 3.19. JCGLViewportsType
- 4. API Reference
  - 4.1. Javadoc

1
Package Information

- 1.1. Orientation
  - 1.1.1. Overview
  - 1.1.2. Correctness
  - 1.1.3. Simplicity
  - 1.1.4. Compatibility
  - 1.1.5. Performance
- 1.2. Requirements
- 1.3. Installation
  - 1.3.1. Source compilation
  - 1.3.2. Maven
- 1.4. Platform Specific Issues
- 1.5. License

1.1
Orientation
1.1.1
Overview
1
The jcanephora package implements a set of abstractions over OpenGL with the intention of providing an API that enforces program correctness statically. The OpenGL API is extremely error prone, but thankfully many of the problems can be alleviated with the simple application of static types. Where the Java type system is too weak to give static guarantees, dynamic checks are used to prevent silent failure.
1.1.2
Correctness
1
Most functions in OpenGL return integers (type int). The glCreateShader() function, for example, returns an int regardless of whether the function is creating a fragment shader or a vertex shader. Obviously, returning a different type depending on whether a fragment shader or a vertex shader was requested would require a dependent type system. As Java clearly does not have dependent types, the jcanephora package provides a shaderCompileVertex() function that returns a value of type JCGLVertexShaderType and a shaderCompileFragment() function that returns a value of type JCGLFragmentShaderType, eliminating at compile-time the possibility of confusing the two. The package takes a similar approach to static safety throughout.
2
All implementations of the jcanephora API are run through a large battery of tests to ensure consistent semantics.
1.1.3
Simplicity
1
The jcanephora package attempts to provide abstractions that are as minimal as possible. It should be possible for any experienced OpenGL programmer to use the package without having to learn new concepts or fight a complicated class hierarchy. The idea is to provide static type safety in as simple a manner as possible.
1.1.4
Compatibility
1
The jcanephora exposes interfaces that represent the OpenGL 3.3 core API.
1.1.5
Performance
1
OpenGL programs often have high performance requirements. The jcanephora API abstractions attempt to be as cheap as possible. Operations that allocate memory or perform potentially expensive operations are clearly marked. Emphasis is placed on writing code paths that generate little in the way of garbage to be collected, reducing GC pressure and improving realtime performance. Many potentially expensive queries (such as a request for the available number of texture units) are requested eagerly and cached, in order to reduce the number of OpenGL calls required at "render time". The package tracks OpenGL state locally to avoid redundant OpenGL calls.
1.2
Requirements
1
The Maven POM files are the canonical source for the project's dependencies:
1.2.1. POM files

- io7m-jcanephora-core-0.51.0.pom
- io7m-jcanephora-jogl-0.51.0.pom

2
Beyond the dependencies given in the POM files, the jcanephora package requires a a GPU supporting OpenGL 3.3.
1.3
Installation
1.3.1
Source compilation
1
The project can be compiled and installed with Maven:
2

    $ mvn -C clean install

1.3.2
Maven
1
Regular releases are made to the Central Repository, so it's possible to use the io7m-jcanephora package in your projects by simply adding some dependencies to your Maven project.
2
The package is split into multiple sub-packages. The jcanephora-core package provides the types, interfaces, and implementations comprising the core of jcanephora. This package is required to use jcanephora.
3

    <dependency>
      <groupId>com.io7m.jcanephora</groupId>
      <artifactId>io7m-jcanephora-core</artifactId>
      <version>0.51.0</version>
    </dependency>

4
To use the JOGL backend for jcanephora, include the jcanephora-jogl package:
5

    <dependency>
      <groupId>com.io7m.jcanephora</groupId>
      <artifactId>io7m-jcanephora-jogl</artifactId>
      <version>0.51.0</version>
    </dependency>

6
All io7m.com packages use Semantic Versioning [0] , which implies that it is always safe to use version ranges with an exclusive upper bound equal to the next major version - the API of the package will not change in a backwards-incompatible manner before the next major version.
1.4
Platform Specific Issues
1
There are currently no known platform-specific issues.
1.5
License
1
All files distributed with the io7m-jcanephora package are placed under the following license:

    Copyright © 2015 <code@io7m.com> http://io7m.com

    Permission to use, copy, modify, and/or distribute this software for any
    purpose with or without fee is hereby granted, provided that the above
    copyright notice and this permission notice appear in all copies.

    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

2
Design/Rationale

- 2.1. Problems with OpenGL
  - 2.1.1. Overview
  - 2.1.2. Types
  - 2.1.3. Confusion
  - 2.1.4. Silent failure
- 2.2. Goals
  - 2.2.1. Overview
  - 2.2.2. Portability
  - 2.2.3. Type safety
  - 2.2.4. Linearity
  - 2.2.5. Conspicuity of errors
- 2.3. API Design
  - 2.3.1. Interfaces as capabilities
  - 2.3.2. Types

2.1
Problems with OpenGL
2.1.1
Overview
1
The jcanephora package takes the general view that OpenGL as an API is a ridiculously error-prone and poorly defined mess [1]. The package attempts to address most if not all of the problems listed in this section.
2.1.2
Types
1
The OpenGL API itself is weakly typed even for a C language API, simply exposing absolutely everything as a float, int, or untyped pointer. Entire classes of problems that should be prevented at compile time are moved to run-time, increasing development time, developer frustration, and testing burden.
2.1.3
Confusion
1
Rather than add or remove function entry points, the OpenGL designers decided to endlessly extend existing functions with yet more combinations of barely-typed parameters. Actually using the OpenGL API correctly is a challenge even for those that are familiar with the full API specification. Out of the hundreds of thousands of combinations of parameters that can be passed to any given API function, only a handful are actually valid.
2.1.4
Silent failure
1
OpenGL programs frequently fail, and when they do, they usually do so silently. Programmers are forced to call glGetError after every API call if they wish to avoid this (and actually detect the error at the correct function call).
2.2
Goals
2.2.1
Overview
1
Given the problems with OpenGL, the jcanephora package has the following goals (in no particular order):
2.2.2
Portability
1
Programs using the jcanephora APIs must run on as many different OpenGL implementations as possible. Code written using the API should be version-branch-free, and use only the features common to all available OpenGL implementations.
2.2.3
Type safety
1
Representing every object as an integer is unacceptable. Any conceptually distinct objects must be made type-incompatible, eliminating entire classes of bugs typical to OpenGL programs at compile time.
2
Only the correct ways to use the API are exposed. Misuse of the API is simply made impossible.
2.2.4
Linearity
1
Branching, version-specific code must be eliminated as far as is possible. Code of this type is fragile, difficult to test, and usually incorrect.
2.2.5
Conspicuity of errors
1
Silent failure of programs is unacceptable. All errors that cannot be prevented statically must be loudly and immediately signalled at runtime.
2.3
API Design
2.3.1
Interfaces as capabilities
1
The package is written with one particular concept, throughout: Types represent capabilities. If the developer has a value of a given type, then the developer can perform all of the operations associated with that type.
2
This is in direct contrast to the OpenGL API, where the developer always has full access to all API functions, and individual functions (or combinations of parameters to functions) may not work based on what the implementation supports. The emphasis is on isolating the correct operations and statically preventing incorrect operations from occurring.
2.3.2
Types
1
The jcanephora package wraps all conceptually distinct objects in new types and provides sane and unsurprising functions to construct them. This prevents large classes of bugs from occurring, at compile time. It also allows the interfaces to be self-documenting. Consider the following C function prototype:
2.3.2.1. Weakly typed C function

    void _ (GLsizei, GLuint *)

2
What does the function do? It could generate a set of textures (glGenTextures), or it could generate a set of new buffer names (glGenBuffers). Without carefully reading the documentation, the required parameters to the function are certainly not obvious from the type alone. Assuming the developer has read the documentation and worked out that the correct way to call the function is with the following sequence:
2.3.2.2. Weakly typed C calls

    GLuint texture;

    glGenTextures(1, &texture);
    checkGLErrors();

3
The developer now has a value of type GLuint. Given that many functions in the OpenGL API also take values of GLuint, and those values have nothing to do with textures, that's a huge number of potential bugs that cannot be prevented by the compiler.
4
The developer now wants to allocate a new RGBA texture. The types of the glTexImage2D and glTexParameteri functions are, quite frankly, insane. Almost no developer manages to use these functions successfully without trial and error and, naturally, all errors are runtime errors. Eventually, through some painful experimentation, the developer might stumble onto the following sequence of calls:
2.3.2.3. Allocating a texture

    GLuint texture;
    void *data = makeTextureData();

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

5
The types of the parameters to all of the functions mentioned allow the developer to pass in practically anything. Only a very small subset of the possible parameters are actually valid. An even smaller subset of the possible parameters actually work across different versions of OpenGL!
6
Now consider the following jcanephora function:
2.3.2.4. Texture allocation?

    public JCGLTexture2DType _(
        JCGLTextureUnitType _,
        long _,
        long _,
        JCGLTextureFormat _,
        JCGLTextureWrapS _,
        JCGLTextureWrapT _,
        JCGLTextureFilterMinification _,
        JCGLTextureFilterMagnification _)
        throws JCGLException;

7
Even without the names of the function and the parameters, the type above make it quite clear that the function returns a JCGLTexture2DType, which is somehow derived from the arguments to the function. It's a reasonable assumption to make that the function allocates a new texture.
8
The wrapping modes on the S and T axis will presumably be derived from the parameters of type JCGLTextureWrapS and JCGLTextureWrapT respectively. The minification and magnification filters will presumably be derived from the parameters of type JCGLTextureFilterMinification and JCGLTextureFilterMagnification, respectively. The programmer is statically prevented from mixing up the two axes, or the two filter domains. The JCGLTextureFormat parameter is obviously intended to specify the format of the texture. Any parameter familiar with OpenGL texturing will be aware that any operations involving textures requires access to a texture unit, and the JCGLTextureUnitType parameter above presumably specifies the unit that will be used.
9
The two parameters of type long presumably correspond to the width and height of the resulting texture, but the types are not descriptive enough to say for sure. Unfortunately, Java lacks a way to make existing arithmetic types incompatible without sacrificing efficiency [2]. In this case, the two parameters do indeed correspond to the width and height, in that order, and the jcanephora package uses this order consistently throughout.
10
The extremely descriptive type given for the function, and the presence of declared (but unchecked) exceptions are direct contributions towards the goals of type safety and conspicuity of errors. It is very difficult for the programmer to use this function incorrectly: the type system guarantees correct usage.
3
jcanephora Core Interfaces

- 3.1. Overview
- 3.2. JCGLContextType
  - 3.2.1. Description
  - 3.2.2. Usage Example
- 3.3. JCGLContextType
  - 3.3.1. Description
  - 3.3.2. Currency
  - 3.3.3. Sharing
- 3.4. JCGLArrayBuffersType
  - 3.4.1. Description
  - 3.4.2. Usage examples
  - 3.4.3. State
- 3.5. JCGLArrayObjectsType
  - 3.5.1. Description
  - 3.5.2. Usage Examples
  - 3.5.3. State
- 3.6. JCGLBlendingType
  - 3.6.1. Description
  - 3.6.2. Usage examples
  - 3.6.3. State
- 3.7. JCGLClearType
  - 3.7.1. Description
  - 3.7.2. Usage example
- 3.8. JCGLColorBufferMaskingType
  - 3.8.1. Description
  - 3.8.2. Usage example
  - 3.8.3. State
- 3.9. JCGLDepthBuffersType
  - 3.9.1. Description
  - 3.9.2. Usage examples
  - 3.9.3. State
- 3.10. JCGLDrawType
  - 3.10.1. Description
  - 3.10.2. Usage examples
- 3.11. JCGLFramebuffersType
  - 3.11.1. Description
  - 3.11.2. Usage examples
  - 3.11.3. Feedback
  - 3.11.4. State
- 3.12. JCGLIndexBuffersType
  - 3.12.1. Description
  - 3.12.2. Usage examples
  - 3.12.3. State
- 3.13. JCGLPolygonModesType
  - 3.13.1. Description
  - 3.13.2. Usage example
  - 3.13.3. State
- 3.14. JCGLScissorType
  - 3.14.1. Description
- 3.15. JCGLShadersType
  - 3.15.1. Description
  - 3.15.2. Usage examples
  - 3.15.3. State
- 3.16. JCGLStencilBuffersType
  - 3.16.1. Description
  - 3.16.2. Usage examples
- 3.17. JCGLTexturesType
  - 3.17.1. Description
  - 3.17.2. Usage examples
  - 3.17.3. Cube map coordinate systems
- 3.18. JCGLTimersType
  - 3.18.1. Description
  - 3.18.2. Usage examples
- 3.19. JCGLViewportsType
  - 3.19.1. Description
  - 3.19.2. Usage example
  - 3.19.3. State

3.1
Overview
1
The jcanephora package is separated into a number of "core" interfaces, through which all higher-level abstractions/interfaces work.
3.2
JCGLContextType

- 3.2.1. Description
- 3.2.2. Usage Example

3.2.1
Description
1
The JCGLImplementationType interface represents an OpenGL implementation. Specific implementations, such as the JOGL implementation, provide subtypes of this interface that allow for the creation of Contexts. It is not a goal of the package to wrap all of the complicated context creation functionality exposed by packages such as JOGL - users are expected to initialize and configure OpenGL contexts and then pass those contexts to jcanephora for use.
3.2.2
Usage Example
1
An example that creates a pair of contexts using the JOGL implementation:
3.2.2.1. JOGL Implementation Example

    GLContext gc_main;
    GLContext gc_background;

    JCGLImplementationJOGLType i = JCGLImplementationJOGL.getInstance();
    JCGLContextType c_main = i.newContextFrom(gc_main, "main");
    JCGLContextType c_back = i.newContextFrom(gc_background, "background-resources");

3.3
JCGLContextType

- 3.3.1. Description
- 3.3.2. Currency
- 3.3.3. Sharing

3.3.1
Description
1
The JCGLContextType interface represents a single OpenGL context.
3.3.2
Currency
1
To accept commands, an OpenGL context must be made current. For a given thread t, at most one context can be current on t at any given time.
3.3.3
Sharing
1
An OpenGL context may be shared. Briefly, this means that if a context c is shared with a context d, then certain types of objects allocated on c are visible to d, and vice versa. This is often used in rendering engines to enable textures and other large resources to be loaded on a separate background thread without interrupting rendering. The jcanephora API knows types of objects may be shared across contexts, and raises errors at run-time if the user attempts to use a non-shared object on the wrong context.
3.4
JCGLArrayBuffersType

- 3.4.1. Description
- 3.4.2. Usage examples
- 3.4.3. State

3.4.1
Description
1
The JCGLArrayBuffersType interface provides functions for allocating, updating, and using array buffers. It provides a typed interface to the glGenBuffers and glBufferData functions.
2
Array buffers are the basic storage type for vertex data used during rendering. The jcanephora API uses the same model as the OpenGL 3.3 API: Buffers must be bound before use, and at most one buffer of each type can be bound at any one time. The API currently exposes the following buffer types:
3.4.1.1. Buffer Types

- Array buffers
- Index buffers

3.4.2
Usage examples
1
To allocate a 128 byte array buffer, indicating to OpenGL that the contents of the buffer will be specified once and then used repeatedly for rendering:
3.4.2.1. Allocation

    JCGLArrayBuffersType g;

    JCGLArrayBufferType a = g.arrayBufferAllocate(128, JCGLUsageHint.USAGE_STATIC_DRAW);

2
The buffer will remain bound after allocation. To update the contents of the buffer, an instance of JCGLBufferUpdateType must be created:
3.4.2.2. Updates

    JCGLArrayBuffersType g;
    JCGLArrayBufferType a;

    final JCGLBufferUpdateType<JCGLArrayBufferType> u =
      JCGLBufferUpdates.newUpdateReplacingAll(this.array_buffer);
    final ByteBuffer b = u.getData();

    for (int index = 0; index < 128; ++index) {
      b.put(index, (byte) index);
    }

    g.arrayBufferUpdate(u);
    g.arrayBufferUnbind();

3.4.3
State
1
OpenGL specifies that at most one array buffer can be bound on a context at any given time. Many API functions operate on the currently bound buffer and the jcanephora API avoids changing the current binding behind the programmer's back.
3.5
JCGLArrayObjectsType

- 3.5.1. Description
- 3.5.2. Usage Examples
- 3.5.3. State

3.5.1
Description
1
The JCGLArrayObjectsType interface provides functions for allocating, updating, and using array objects.
3.5.2
Usage Examples
1
An array object is a container for an index buffer binding and a set of numbered vertex array attributes. Essentially, the programmer can state that numbered vertex array attributes will be supplied with data from assigned array buffers, with each attribute knowing how to extract values from the otherwise untyped buffer. An array attribute will read count values of type t from an array buffer, starting at offset o in bytes, and stepping forward stride bytes for each vertex. This more or less encodes the notion that an array buffer is an array of record [3] values, with each array attribute reading from a specific record field for each element.
3.5.2.1. Array Buffers As Arrays Of Records
2
As a simple example, consider an array containing three vertices, with each vertex containing a single three-element floating point vector:
3.5.2.2. Positions

    JCGLArrayBuffersType g_ab;
    JCGLArrayBufferType ab;

    final int vertex_size = 3 * 4;
    ab = g_ab.arrayBufferAllocate(vertex_size * 3L, JCGLUsageHint.USAGE_STATIC_DRAW);

    final JCGLBufferUpdateType<JCGLArrayBufferType> u = JCGLBufferUpdates.newUpdateReplacingAll(ab);
    final FloatBuffer d = u.getData().asFloatBuffer();

    d.put(0, -0.5f);
    d.put(1, 0.5f);
    d.put(2, -0.5f);

    d.put(3, -0.5f);
    d.put(4, -0.5f);
    d.put(5, -0.5f);

    d.put(6, 0.5f);
    d.put(7, -0.5f);
    d.put(8, -0.5f);

    g_ab.arrayBufferUpdate(u);
    g_ab.arrayBufferUnbind();

3
Then, assuming that vertex positions should be supplied to a shading language program via vertex attribute 0:
3.5.2.3. Attribute

    JCGLArrayObjectsType aob;

    final JCGLArrayObjectBuilderType aob = g_ao.arrayObjectNewBuilder();
    aob.setAttributeFloatingPoint(
      0,
      ab,
      3,
      JCGLScalarType.TYPE_FLOAT,
      vertex_size,
      0L,
      false);

    JCGLArrayObjectType ao = g_ao.arrayObjectAllocate(aob);

3.5.3
State
1
OpenGL specifies that exactly one array object will be bound on a context at any given time. OpenGL provides a default array object that is bound when no user-created array object exists. Many API functions operate on the currently bound array object and the jcanephora API avoids changing the current binding behind the programmer's back.
3.6
JCGLBlendingType

- 3.6.1. Description
- 3.6.2. Usage examples
- 3.6.3. State

3.6.1
Description
1
The JCGLBlendingType interface provides functions for configuring framebuffer color blending.
3.6.2
Usage examples
1
To enable blending, using the fairly typical color = (destination * source_alpha) + (source * (1.0 - source_alpha)) blending equation:
3.6.2.1. Blending

    JCGLBlendingType g;

    g.blendingEnable(
      BlendFunction.BLEND_SOURCE_ALPHA,
      BlendFunction.BLEND_ONE_MINUS_SOURCE_ALPHA);

3.6.3
State
1
The current blending state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
3.7
JCGLClearType

- 3.7.1. Description
- 3.7.2. Usage example

3.7.1
Description
1
The JCGLClearType interface provides functions for clearing the contents of framebuffers.
3.7.2
Usage example
1
The following code creates a clearing specification that will clear the current framebuffer to a dark grey color, and the current depth buffer to 1.0:
3.7.2.1. Blending

    JCGLClearType g;

    final JCGLClearSpecification.Builder cb = JCGLClearSpecification.builder();
    cb.setColorBufferClear(new VectorI4F(0.1f, 0.1f, 0.1f, 1.0f));
    cb.setDepthBufferClear(1.0f);
    JCGLClearSpecification c = cb.build();

    g.clear(c);

2
The JCGLClearSpecification value can be re-used indefinitely. The API also provides optional strict checking which can, for example, raise an exception if the programmer attempts to clear the depth or stencil buffer without the current framebuffer actually having one [4].
3.8
JCGLColorBufferMaskingType

- 3.8.1. Description
- 3.8.2. Usage example
- 3.8.3. State

3.8.1
Description
1
The JCGLColorBufferMaskingType interface provides functions for masking writes to specific color channels of the framebuffer.
3.8.2
Usage example
1
The following code prevents subsequent rendering operations from rendering to the red channel of any framebuffer:
3.8.2.1. Masking

    JCGLColorBufferMaskingType g;

    g.colorBufferMask(false, true, true, true);

3.8.3
State
1
The current masking state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
3.9
JCGLDepthBuffersType

- 3.9.1. Description
- 3.9.2. Usage examples
- 3.9.3. State

3.9.1
Description
1
The JCGLDepthBuffersType interface provides functions for controlling depth testing, writing, and clamping of depth values.
3.9.2
Usage examples
1
To clear the current depth buffer to a value typical of the far clipping plane:
3.9.2.1. Depth buffer clear

    JCGLDepthBuffersType g;

    g.depthBufferClear(1.0f);

2
To discard all writes to the depth buffer (note that this will also prevent clearing of the depth buffer):
3.9.2.2. Depth buffer mask

    JCGLDepthBuffersType g;

    g.depthBufferWriteDisable();

3
To enable depth testing and set the depth function such that only depth values less than the current value in the depth buffer will be written:
3.9.2.3. Depth buffer enable

    JCGLDepthBuffersType g;

    g.depthBufferTestEnable(DepthFunction.DEPTH_LESS_THAN);

4
Most of the functions raise exceptions if attempting to perform operations on a framebuffer that does not have a depth buffer.
3.9.3
State
1
The current depth-testing and depth-writing state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
3.10
JCGLDrawType

- 3.10.1. Description
- 3.10.2. Usage examples

3.10.1
Description
1
The JCGLDrawType interface provides functions for initiating rendering operations.
3.10.2
Usage examples
1
Assuming that an array object is currently bound and has an attached index buffer, drawing an object is trivial:
3.10.2.1. Drawing

    JCGLDrawType g;

    g.drawElements(Primitives.PRIMITIVE_TRIANGLES);

2
Similar functions exist for rendering without index buffers.
3.11
JCGLFramebuffersType

- 3.11.1. Description
- 3.11.2. Usage examples
- 3.11.3. Feedback
- 3.11.4. State

3.11.1
Description
1
The JCGLFramebuffersType interface provides functions for creating and controlling framebuffers.
3.11.2
Usage examples
1
A framebuffer in OpenGL is an opaque structure with one or more attachments. Currently, attachments can only be textures. Textures are useful when the result of rendering to a framebuffer is going to be read back at some point, or perhaps used to texture other objects. For example, some 3D engines implement so-called "procedural textures" by rendering a generated pattern to a texture and then applying that texture to objects in the scene. Attachments are attached at specific points to any given framebuffer: Framebuffers can have a depth attachment, a stencil attachment, a depth+stencil attachment, and an implementation-defined maximum number of color attachments.
2
When rendering to a framebuffer, a shading program will write values to one or moredraw buffers, and each draw buffer is mapped to a color attachments. Multiple draw buffers cannot be mapped to the same color attachment.
3
In jcanephora, framebuffers are created via mutable builders which are then passed to the API functions, resulting in immutable framebuffers [5].
3.11.2.1. Framebuffer builder

    JCGLFramebuffersType g;
    JCGLFramebufferBuilderType b = g.framebufferNewBuilder();

4
To attach allocated textures/buffers to a framebuffer, the API requires the programmer to first obtain a list of the available attachment points:
3.11.2.2. Framebuffer color attachment points

    JCGLFramebuffersType g;

    List<JCGLFramebufferColorAttachmentPointType> points = g.framebufferGetColorAttachmentPoints();

5
Similarly, the API requires the programmer to obtain a list of the available draw buffers:
3.11.2.3. Framebuffer draw buffers

    JCGLFramebuffersType g;

    List<JCGLFramebufferDrawBufferType> buffers = g.framebufferGetDrawBuffers();

6
The programmer must pass values of type JCGLFramebufferColorAttachmentPointType to various attachment functions and is therefore statically prevented from trying to use more color attachments than the OpenGL implementation allows.
7
After allocating a color and depth/stencil texture, the textures must be attached at the relevant attachment points and draw buffers mapped to the color attachments.
3.11.2.4. Framebuffer attachments

    JCGLFramebuffersType g;
    JCGLTexturesType gt;
    JCGLTextureUnitType u;

    List<FramebufferColorAttachmentPointType> points = g.framebufferGetColorAttachmentPoints();
    List<JCGLFramebufferDrawBufferType> buffers = g.framebufferGetDrawBuffers();

    JCGLTexture2DType d = gt.texture2DAllocate(u, 128, 128, JCGLTextureFormat.TEXTURE_FORMAT_DEPTH_16_2BPP, ...);
    JCGLTexture2DType c = gt.texture2DAllocate(u, 128, 128, JCGLTextureFormat.TEXTURE_FORMAT_RGBA_8_4BPP, ...);
    gt.textureUnitUnbind(u);

    JCGLFramebufferBuilderType b = g.framebufferNewBuilder();
    b.attachColorTexture2DAt(points.get(0), buffers.get(0), c);
    b.attachDepthTexture2D(d);

8
All unspecified draw buffers are mapped to GL_NONE (meaning that any data written to the other draw buffers will go nowhere). The builders also raise exceptions if the programmer attempts to, for example, specify a non-color-renderable format for a color attachment.
9
To actually allocate a framebuffer, the mutable builder is passed to an allocation function:
3.11.2.5. Framebuffer attachment/buffer mappings

    JCGLFramebuffersType g;
    JCGLFramebufferBuilderType b;

    FramebufferType f = g.framebufferAllocate(b);

10
The function validates the framebuffer configuration upon allocation, raising an exception if the framebuffer is not usable on the current OpenGL implementation.
11
To render to the framebuffer, the framebuffer must be bound:
3.11.2.6. Framebuffer binding

    JCGLFramebuffersType g;
    JCGLFramebufferType f;

    g.framebufferDrawBind(f);
    {
      // Perform rendering.
    }
    g.framebufferDrawUnbind(f);

3.11.3
Feedback
1
The OpenGL specification explicitly states that feedback is illegal and results in undefined behaviour. Feedback is defined as occurring when a texture t is both attached to the current draw framebuffer and is also bound to a texture unit and being sampled. This would result in a feedback loop where data written to the framebuffer would be written to a texture currently being sampled.
2
The jcanephora attempts to detect and prevent feedback by raising exceptions:
3.11.3.1. Feedback

1. When a texture is bound, the current framebuffer (if any) is checked to see if the texture is attached to the framebuffer.
2. When a draw framebuffer is bound, a check is made to see if any of the attached textures are currently bound.

3.11.4
State
1
OpenGL specifies that at most one framebuffer can be bound on a context at any given time. Many API functions operate on the currently bound framebuffer and the jcanephora API avoids changing the current binding behind the programmer's back.
3.12
JCGLIndexBuffersType

- 3.12.1. Description
- 3.12.2. Usage examples
- 3.12.3. State

3.12.1
Description
1
The JCGLIndexBuffersType interface provides functions for allocating, updating, and using index buffers. It provides a typed interface to the glGenBuffers and glBufferData functions.
2
Index buffers are used to store indices for indexed rendering as provided by the OpenGL glDrawElements family of functions.
3.12.2
Usage examples
1
Usage of index buffers is similar to array buffers with the main difference being that index buffers know the type of their indices.
3.12.2.1. Allocation and Updates

    JCGLIndexBuffersType g;

    JCGLIndexBufferType a = g.indexBufferAllocate(3, JCGLUnsignedType.TYPE_UNSIGNED_INT, JCGLUsageHint.USAGE_STATIC_DRAW);

    final JCGLBufferUpdateType<JCGLIndexBufferType> u =
      JCGLBufferUpdates.newUpdateReplacingAll(this.index_buffer);
    final IntBuffer i = u.getData().asIntBuffer();

    i.put(0, 0);
    i.put(1, 1);
    i.put(2, 2);

    g.indexBufferUpdate(u);
    g.indexBufferUnbind();

3.12.3
State
1
OpenGL specifies that at most one index buffer can be bound to the current array object at any given time. OpenGL provides a default array object that is used when attempting to bind an index buffer, if no user-created array object is bound.
3.13
JCGLPolygonModesType

- 3.13.1. Description
- 3.13.2. Usage example
- 3.13.3. State

3.13.1
Description
1
The JCGLPolygonModesType interface provides functions for controlling the rendering style of individual polygons.
3.13.2
Usage example
1
The following code sets a wireframe rendering style:
3.13.2.1. Wireframe

    JCGLPolygonModesType g;

    g.polygonSetMode(JCGLPolygonMode.POLYGON_LINES);

3.13.3
State
1
The current polygon rendering style state is global to a given context; when enabled, it is enabled for all subsequent rendering operations on that context until explicitly modified or disabled.
3.14
JCGLScissorType

- 3.14.1. Description

3.14.1
Description
1
The JCGLScissorType interface provides functions for controlling the OpenGL scissor test.
3.15
JCGLShadersType

- 3.15.1. Description
- 3.15.2. Usage examples
- 3.15.3. State

3.15.1
Description
1
The JCGLShadersType interface provides functions for compiling and using shading language programs.
3.15.2
Usage examples
1
To compile vertex, geometry, and fragment shaders, simply pass in lists of strings, with each string terminated with CRLF or LF characters:
3.15.2.1. Compiling shaders

    JCGLShadersType g;

    List<String> v_source = new ArrayList<String>();
    v_source.add("#version 330\n");
    v_source.add("in vec4 position;\n");
    v_source.add("void\n");
    v_source.add("main (void)\n");
    v_source.add("{\n");
    v_source.add("  gl_Position = position;\n");
    v_source.add("}\n");
    JCGLVertexShaderType vs = g.shaderCompileVertex("example-v", v_source);

    List<String> g_source = new ArrayList<String>();
    g_source.add("#version 330 core\n");
    g_source.add("layout(points) in;\n");
    g_source.add("layout(points, max_vertices = 1) out;\n");
    g_source.add("void\n");
    g_source.add("main ()\n");
    g_source.add("{\n");
    g_source.add("  gl_Position = gl_in[0].gl_Position;\n");
    g_source.add("  EmitVertex();\n");
    g_source.add("  EndPrimitive();\n");
    g_source.add("}\n");
    JCGLGeometryShaderType gs = g.shaderCompileGeometry("example-g", g_source);

    List<String> f_source = new ArrayList<String>();
    f_source.add("#version 330\n");
    f_source.add("uniform vec4 color;\n");
    f_source.add("out vec4 color0;\n");
    f_source.add("void\n");
    f_source.add("main (void)\n");
    f_source.add("{\n");
    f_source.add("  color0 = color;\n");
    f_source.add("}\n");
    JCGLFragmentShaderType fs = g.shaderCompileFragment("example-f", f_source);

2
To combine (attach and link) the shaders into a working program:
3.15.2.2. Producing a program

    JCGLShadersType g;

    JCGLProgramShaderType p = g.shaderLinkProgram("program", vs, Optional.of(gs), fs);

3
The program functions interrogate the compiled program when it is created and produce a set of mappings from names to active uniforms (parameters). A read-only view of these can be accessed via the program reference, and assigned values via the provided functions:
3.15.2.3. Program uniforms

    JCGLShadersType g;

    Map<String, JCGLProgramUniformType> uniforms = p.getUniforms();

    JCGLProgramUniformType p = uniforms.get("color");
    assert p != null;
    g.programActivate(p);
    p.programUniformPutVector4f(p, new VectorI4F(1.0f, 0.0f, 0.0f, 1.0f));

3.15.3
State
1
OpenGL specifies that at most one shading program can be active on a context at any given time. Many API functions operate on the currently active program and the jcanephora API avoids changing the current binding behind the programmer's back.
3.16
JCGLStencilBuffersType

- 3.16.1. Description
- 3.16.2. Usage examples

3.16.1
Description
1
The JCGLStencilBuffersType interface provides functions for controlling the stencil buffer.
3.16.2
Usage examples
1
To clear the current stencil buffer:
3.16.2.1. Stencil buffer clear

    JCGLStencilBuffersType g;

    g.stencilBufferClear(0);

2
See the referenced JavaDoc for the full range of functions. Most of the functions will raise exceptions if the current framebuffer configuration does not include a stencil buffer; this is considered to be behaviour preferable to silently producing incorrect results.
3.17
JCGLTexturesType

- 3.17.1. Description
- 3.17.2. Usage examples
- 3.17.3. Cube map coordinate systems

3.17.1
Description
1
The JCGLTexturesType interface provides functions for allocating, updating, and using textures.
3.17.2
Usage examples
1
The JCGLTexturesType interfaces expose functions that will allocate textures of formats supported by the current implementation. The interfaces only expose textures that are required to be supported by OpenGL 3.3 core.
2
In order to use a texture in OpenGL, the texture must be bound to a texture unit. There are a limited number of texture units available [6], and so the API requires the user to obtain access to a list of the current units:
3.17.2.1. Texture units

    JCGLTexturesType g;
    List<JCGLTextureUnitType> u = g.textureGetUnits();

3
A texture unit may have at most one texture bound to it at any one time. This is in slight contrast to the way that the OpenGL API normally works: The OpenGL API specifies that a given texture unit may have multiple textures bound to a given texture unit as long as all of the textures are of a different type (two-dimensional, cube map, etc). However, it then states that it is not legal to pass that texture unit to multiple different uniform parameters in a shading program. There is, therefore, very little practical utility to binding multiple textures to a single texture unit, and exposing such behaviour in the API complicates tracking of state and makes programs slightly more difficult to reason about. The jcanephora API therefore imposes a simple restriction: One texture per texture unit.
4
To allocate a 256 * 256 RGBA texture, 8 bits per channel (four bytes per pixel), with nearest-neighbour filtering and repeat wrapping around both axes:
3.17.2.2. Texture allocation

    JCGLTexturesType g;
    JCGLTextureUnitType u;

    JCGLTexture2DType t =
      g.texture2DAllocate(
        u,
        256,
        256,
        JCGLTextureFormat.TEXTURE_FORMAT_RGBA_8_4BPP,
        JCGLTextureWrapS.TEXTURE_WRAP_REPEAT,
        JCGLTextureWrapT.TEXTURE_WRAP_REPEAT,
        JCGLTextureFilterMinification.TEXTURE_FILTER_NEAREST,
        JCGLTextureFilterMagnification.TEXTURE_FILTER_NEAREST);

5
Once allocated, the texture will remain bound to the texture unit u until explicitly unbound.
6
The jcanephora exposes both two-dimensional and cube map textures. See the relevant superinterfaces.
3.17.3
Cube map coordinate systems
1
Note that in the OpenGL specification, cube textures arbitrarily use a left-handed coordinate system as opposed to OpenGL's right-handed system. This is a frequent source of errors in programs. See the following fragment shader:
3.17.3.1. Cube map shader

    in vec3 f_normal;   // Eye-space surface normal
    in vec3 f_position; // Eye-space surface position

    uniform mat4        m_view_inverse; // Inverse view matrix, eye-space -> world-space.
    uniform samplerCube t_reflection;

    out vec4 frag_color;

    void
    main (void)
    {
      vec3 n = normalize (f_normal);
      vec3 v = normalize (f_position);
      vec3 r = reflect (v, n);
      vec3 u = m_view_inverse * r;

      frag_color = texture (t_reflection, u);
    }

2
The program calculates a reflection vector r from the eye-space incident vector f_position and eye-space surface normal f_normal. It transforms r back into world-space, giving u, and then uses u to index into the cube texture. This would be textbook-correct, if OpenGL didn't arbitrarily use a left-handed coordinate system for cube textures! The coordinates u are obviously specified in a right-handed coordinate system. The practical results of this are that all of the cube texture faces will be displayed upside down, and the familiar concept of the negative Z axis meaning "forwards" will be reversed.
3
The jcanephora package contains support for using right-handed cube maps in OpenGL, consistent with the right-handed coordinate system used in most OpenGL programs. In order to use right-handed cube maps, the following steps are required:
3.17.3.2. Cube map addressing

1. Cube map textures are supplied to the OpenGL implementation using the "RH" variants of the texture loading and update functions. Faces are specified in a manner consistent with OpenGL's coordinate system. Informally, the "forward" face of the cube map is the negative Z face, the "right" face of the cube map is the positive X face, and so on.
2. In shaders, the coordinates passed to the texture function (or textureCube function, on old OpenGL implementations) should be multiplied with a matrix that effectively negates the Y and Z components.

4
The earlier example shader becomes:
3.17.3.3. Cube map shader

    in vec3 f_normal;   // Eye-space surface normal
    in vec3 f_position; // Eye-space surface position

    uniform mat4        m_view_inverse; // Inverse view matrix, eye-space -> world-space.
    uniform samplerCube t_reflection;

    out vec4 frag_color;

    void
    main (void)
    {
      const mat3 m_t = mat3 (
        1.0,  0.0,  0.0,
        0.0, -1.0,  0.0,
        0.0,  0.0, -1.0
      );

      vec3 n = normalize (f_normal);
      vec3 v = normalize (f_position);
      vec3 r = reflect (v, n);
      vec3 u = m_view_inverse * r;
      vec3 w = m_t * u;

      frag_color = texture (t_reflection, w);
    }

5
This approach means that both the loading and generation of cube textures is consistent with that of 2D textures and the rest of OpenGL, and the complexity of addressing the textures is limited to one function call in shaders. Renderers that produce cube maps do not need to use unusual projection matrices to "flip" the coordinate system when rendering, and everything works according to the principles of least astonishment!
6
The correct solution to the problem would have been for the cube map coordinate system to have been in a consistent right-handed coordinate space to begin with.
3.18
JCGLTimersType

- 3.18.1. Description
- 3.18.2. Usage examples

3.18.1
Description
1
The JCGLTimersType interface provides functions for allocating and evaluating timer queries.
3.18.2
Usage examples
1
The JCGLTimersType interfaces expose functions that allocate and evaluate timer queries. A timer query is, at the most basic level, a way of having the GPU record the current time in a manner that can be retrieved by the CPU later on.
2
To allocate a new timer query:
3.18.2.1. Allocation

    JCGLTimersType g;
    JCGLTimerType t = g.timerQueryAllocate();

3
A timer query must be asynchronously started and stopped, and can then later be queried. For example, to measure the time it takes to execute a series of OpenGL commands:
3.18.2.2. Timing

    JCGLDrawType gd;
    JCGLTimersType g;
    JCGLTimerType t;

    g.timerQueryBegin(t);
    gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
    gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
    gd.drawElements(JCGLPrimitives.PRIMITIVE_TRIANGLES);
    g.timerQueryFinish(t);

4
The timerQueryBegin/timerQueryFinish commands execute asynchronously, recording the current time on the GPU as the GPU reaches each command in the queue. The application can then ask the GPU if the timer commands have finished executing, and return the recorded time values if they have:
3.18.2.3. Retrieval

    JCGLDrawType gd;
    JCGLTimersType g;
    JCGLTimerType t;

    if (g.timerQueryResultIsReady(t)) {
      long elapsed = g.timerQueryResultGet(t);
      // The time to execute the three draw commands is time_end - time_start nanoseconds
    }

5
At most one timer query can be executing at any one time; calls to timerQueryBegin/timerQueryFinish cannot be nested.
6
Calling timerQueryResultGet implies synchronization between the CPU and GPU and should therefore be called after all other rendering operations for the frame have completed. As timer queries are most often used to implement OpenGL profiling, this is not usually an onerous restriction. Generally, applications will allocate many timers for their rendering pipelines, update timers during rendering of a frame, and then query all timers at the end of the frame to measure the time taken by each part of the pipeline.
3.19
JCGLViewportsType

- 3.19.1. Description
- 3.19.2. Usage example
- 3.19.3. State

3.19.1
Description
1
The JCGLViewportsType interface provides functions for controlling the current viewport.
3.19.2
Usage example
1
The following code configures a 640x480 viewport:
3.19.2.1. Viewport

    JCGLViewportsType g;

    AreaInclusiveUnsignedLType area =
      AreaInclusiveUnsignedL.of(
        new UnsignedRangeInclusiveL(0, 639),
        new UnsignedRangeInclusiveL(0, 479)
      );

    g.viewportSet(area);

3.19.3
State
1
The current viewport state is global to a given context; when modified, it is modified for all subsequent rendering operations on that context until modified again.
4
API Reference

- 4.1. Javadoc

4.1
Javadoc
1
API documentation for the package is provided via the included Javadoc.

[0]
http://semver.org
[1]
Whilst, annoyingly, being the only viable cross-platform and vendor neutral graphics API available.
[2]
New types will be new classes, resulting in all integers being replaced with pointers to integers.
[3]
Or struct, in C terminology.
[4]
This is usually the result of the programmer forgetting to attach a depth/stencil attachment on framebuffer creation, and can lead to difficult-to-trace bugs.
[5]
That is, the framebuffer attachments cannot be reconfigured once the framebuffer is created. This matches the typical use patterns with regards to framebuffers and is also a design choice taken from practical experience: On most OpenGL implementations, reconfiguring a framebuffer is a rather expensive operation.
[6]
OpenGL 3.3 guarantees that at least 16 will be available in any implementation.