jake2.gwt.client.WebGLAdapter.java Source code

Java tutorial

Introduction

Here is the source code for jake2.gwt.client.WebGLAdapter.java

Source

/*
Copyright (C) 2010 Copyright 2010 Google Inc.
    
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
See the GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package jake2.gwt.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayInteger;
import com.google.gwt.corp.gfx.client.canvas.CanvasElement;
import com.google.gwt.corp.webgl.client.ByteBufferWrapper;
import com.google.gwt.corp.webgl.client.HasWebGLArray;
import com.google.gwt.corp.webgl.client.WebGL;
import com.google.gwt.corp.webgl.client.WebGLArray;
import com.google.gwt.corp.webgl.client.WebGLByteArray;
import com.google.gwt.corp.webgl.client.WebGLFloatArray;
import com.google.gwt.corp.webgl.client.WebGLIntArray;
import com.google.gwt.corp.webgl.client.WebGLShortArray;
import com.google.gwt.corp.webgl.client.WebGLUnsignedByteArray;
import com.google.gwt.corp.webgl.client.WebGLUnsignedShortArray;
import com.google.gwt.corp.webgl.client.WebGL.Texture;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Window;

import jake2.render.DisplayMode;
import jake2.render.GLAdapter;
import jake2.render.gl.AbstractGL20Adapter;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;

/**
 * Partial mapping of lwjgl to WebGL.
 * 
 * @author Stefan Haustein
 */
public class WebGLAdapter extends AbstractGL20Adapter {

    static final int SMALL_BUF_COUNT = 4;

    int uMvpMatrix;
    int uSampler0;
    int uSampler1;
    int uTexEnv0;
    int uTexEnv1;
    int uEnableTexture0;
    int uEnableTexture1;

    JsArray<WebGL.Buffer> staticBuffers = (JsArray<WebGL.Buffer>) JavaScriptObject.createArray();

    class BufferData {
        WebGLArray<?> toBind;
        WebGL.Buffer buffer;
        int byteStride;
        int size;
        int type;
        int byteSize;
        boolean normalize;
    }

    private BufferData[] bufferData = new BufferData[SMALL_BUF_COUNT];

    public final WebGL gl;

    int logCount = 0;

    public void log(String msg) {
        if (logCount >= 1000) {
            return;
        }
        log_((logCount++) + ": " + msg);
    };

    private native void log_(String msg) /*-{
                                         console.log(msg);
                                         }-*/;

    FloatBuffer colorBuffer;
    private CanvasElement canvas;

    JsArray<WebGL.Texture> textures = (JsArray<Texture>) JsArray.createArray();
    JsArrayInteger textureFormats = (JsArrayInteger) JsArray.createArray();

    private int clientActiveTexture = 0;
    private int activeTexture = 0;
    private int[] boundTextureId = new int[2];
    private int[] texEnvMode = new int[2];
    private JsArrayInteger textureFormat = (JsArrayInteger) JavaScriptObject.createArray();
    private WebGL.Buffer elementBuffer;

    public WebGLAdapter(CanvasElement canvas) {
        super(canvas.getWidth(), canvas.getHeight());

        this.canvas = canvas;
        gl = WebGL.getContext(canvas, JavaScriptObject.createObject());

        if (gl == null) {
            throw new UnsupportedOperationException("WebGL N/A");
        }

        initShader();
        checkError("initShader");

        elementBuffer = gl.glCreateBuffer();
        checkError("createBuffer f. elements");

        for (int i = 0; i < bufferData.length; i++) {
            BufferData bd = new BufferData();
            bd.buffer = gl.glCreateBuffer();
            checkError("createBuffer" + i);
            bufferData[i] = bd;
        }

    }

    //  private native JavaScriptObject noPremultipliedAlpha() /*-{
    //   return {premultipliedAlpha: false};
    //  }-*/;

    private WebGL.Shader loadShader(int shaderType, String shaderSource) {
        // Create the shader object
        WebGL.Shader shader = gl.glCreateShader(shaderType);
        if (shader == null) {
            throw new RuntimeException();
        }
        // Load the shader source
        gl.glShaderSource(shader, shaderSource);

        // Compile the shader
        gl.glCompileShader(shader);

        // Check the compile status
        boolean compiled = gl.glGetShaderParameterb(shader, WebGL.GL_COMPILE_STATUS);
        if (!compiled) {
            // Something went wrong during compilation; get the error
            throw new RuntimeException("Shader compile error: " + gl.glGetShaderInfoLog(shader));
        }
        return shader;
    }

    public static native String getUserAgent() /*-{
                                               return navigator.userAgent.toLowerCase();
                                               }-*/;

    private void initShader() {
        String vertexShaderSource = "attribute vec4 a_position;\n" + "attribute vec4 a_color;\n"
                + "attribute vec2 a_texCoord0; \n" + "attribute vec2 a_texCoord1; \n"
                + "uniform mat4 u_mvpMatrix; \n" + "varying vec4 v_color; \n" + "varying vec2 v_texCoord0; \n"
                + "varying vec2 v_texCoord1; \n" + "void main() {\n" + "  gl_Position = u_mvpMatrix * a_position;\n"
                + "  v_color = a_color;        \n" + "  v_texCoord0 = a_texCoord0;  \n"
                + "  v_texCoord1 = a_texCoord1;  \n" + "}\n";

        String fragmentShaderSource = "uniform sampler2D s_texture0;  \n" + "uniform sampler2D s_texture1;  \n"
                + "uniform int s_texEnv0;  \n" + "uniform int s_texEnv1;  \n" + "uniform int u_enable_texture_0; \n"
                + "uniform int u_enable_texture_1; \n" + "varying vec4 v_color; \n"
                + "varying vec2 v_texCoord0;      \n" + "varying vec2 v_texCoord1;" + "vec4 finalColor;      \n"
                + "void main() {                 \n" + "finalColor = v_color;"
                + "  if (u_enable_texture_0 == 1) { \n" + "    vec4 texel = texture2D(s_texture0, v_texCoord0); \n"
                + "    if(s_texEnv0 == 1) { " + "      finalColor = finalColor * texel;"
                + "    } else if (s_texEnv0 == 2) {"
                + "      finalColor = vec4(texel.r, texel.g, texel.b, finalColor.a);" + "    } else {"
                + "      finalColor = texel;" + "    }" + "}" + " if (u_enable_texture_1 == 1) { \n"
                + "      vec4 texel = texture2D(s_texture1, v_texCoord1); \n" + "    if(s_texEnv1 == 1) { "
                + "      finalColor = finalColor * texel;" + "    } else if (s_texEnv1 == 2) {"
                + "      finalColor = vec4(texel.r, texel.g, texel.b, finalColor.a);" + "    } else {"
                + "      finalColor = texel;" + "    }" + "  } \n" +
                // simple alpha check
                " if (finalColor.a == 0.0) {\n" + "   discard;\n" + " }\n" + " gl_FragColor = finalColor; \n"
                + "}\n";
        //    }

        // create our shaders
        WebGL.Shader vertexShader = loadShader(WebGL.GL_VERTEX_SHADER, vertexShaderSource);
        WebGL.Shader fragmentShader = loadShader(WebGL.GL_FRAGMENT_SHADER, fragmentShaderSource);

        if (vertexShader == null || fragmentShader == null) {
            log("Shader error");
            throw new RuntimeException("shader error");
        }

        // Create the program object
        WebGL.Program programObject = gl.glCreateProgram();
        if (programObject == null || gl.glGetError() != WebGL.GL_NO_ERROR) {
            log("Program errror");
            throw new RuntimeException("program error");
        }
        // Attach our two shaders to the program

        gl.glAttachShader(programObject, vertexShader);
        gl.glAttachShader(programObject, fragmentShader);

        // Bind "vPosition" to attribute 0
        gl.glBindAttribLocation(programObject, GLAdapter.ARRAY_POSITION, "a_position");
        gl.glBindAttribLocation(programObject, GLAdapter.ARRAY_COLOR, "a_color");
        gl.glBindAttribLocation(programObject, GLAdapter.ARRAY_TEXCOORD_0, "a_texCoord0");
        gl.glBindAttribLocation(programObject, GLAdapter.ARRAY_TEXCOORD_1, "a_texCoord1");

        // Link the program
        gl.glLinkProgram(programObject);

        //TODO(haustein) get position, color from the linker, too
        uMvpMatrix = gl.glGetUniformLocation(programObject, "u_mvpMatrix");
        uSampler0 = gl.glGetUniformLocation(programObject, "s_texture0");
        uSampler1 = gl.glGetUniformLocation(programObject, "s_texture1");
        uTexEnv0 = gl.glGetUniformLocation(programObject, "s_texEnv0");
        uTexEnv1 = gl.glGetUniformLocation(programObject, "s_texEnv1");

        uEnableTexture0 = gl.glGetUniformLocation(programObject, "u_enable_texture_0");
        uEnableTexture1 = gl.glGetUniformLocation(programObject, "u_enable_texture_1");

        // // Check the link status
        boolean linked = gl.glGetProgramParameterb(programObject, WebGL.GL_LINK_STATUS);
        if (!linked) {
            throw new RuntimeException("linker Error: " + gl.glGetProgramInfoLog(programObject));
        }

        gl.glUseProgram(programObject);

        gl.glUniform1i(uSampler0, 0);
        gl.glUniform1i(uSampler1, 1);
        gl.glActiveTexture(GL_TEXTURE0);
    }

    public String webGLFloatArrayToString(WebGLFloatArray fa) {
        StringBuilder sb = new StringBuilder();
        sb.append("len: " + fa.getLength());
        sb.append("data: ");
        for (int i = 0; i < Math.min(fa.getLength(), 10); i++) {
            sb.append(fa.get(i) + ",");
        }
        return sb.toString();
    }

    public String webGLIntArrayToString(WebGLIntArray fa) {
        StringBuilder sb = new StringBuilder();
        sb.append("len: " + fa.getLength());
        sb.append("data: ");
        for (int i = 0; i < Math.min(fa.getLength(), 10); i++) {
            sb.append(fa.get(i) + ",");
        }
        return sb.toString();
    }

    public String webGLUnsignedShortArrayToString(WebGLUnsignedShortArray fa) {
        StringBuilder sb = new StringBuilder();
        sb.append("len: " + fa.getLength());
        sb.append("data: ");
        for (int i = 0; i < Math.min(fa.getLength(), 10); i++) {
            sb.append(fa.get(i) + ",");
        }
        return sb.toString();
    }

    @Override
    public void glActiveTexture(int texture) {
        gl.glActiveTexture(texture);
        activeTexture = texture - GL_TEXTURE0;
        checkError("glActiveTexture");
    }

    @Override
    public void glAlphaFunc(int i, float j) {
        // TODO Auto-generated method stub
    }

    @Override
    public void glClientActiveTexture(int texture) {
        clientActiveTexture = texture - GL_TEXTURE0;
    }

    @Override
    public void glColorPointer(int size, int stride, FloatBuffer colorArrayBuf) {
        glColorPointer(size, WebGL.GL_FLOAT, stride, colorArrayBuf);
    }

    @Override
    public void glColorPointer(int size, boolean unsigned, int stride, ByteBuffer colorAsByteBuffer) {
        glColorPointer(size, unsigned ? WebGL.GL_UNSIGNED_BYTE : WebGL.GL_BYTE, stride, colorAsByteBuffer);
    }

    private final void glColorPointer(int size, int type, int stride, Buffer buf) {
        glVertexAttribPointer(GLAdapter.ARRAY_COLOR, size, type, true, stride, buf);
        checkError("glColorPointer");
    }

    @Override
    public void glDeleteTextures(IntBuffer texnumBuffer) {
        for (int i = 0; i < texnumBuffer.remaining(); i++) {
            int tid = texnumBuffer.get(texnumBuffer.position() + i);
            gl.glDeleteTexture(textures.get(tid));
            textures.set(tid, null);
            checkError("glDeleteTexture");
        }
    }

    @Override
    public void glDepthFunc(int func) {
        gl.glDepthFunc(func);
        checkError("glDepthFunc");
    }

    @Override
    public void glDepthMask(boolean b) {
        gl.glDepthMask(b);
        checkError("glDepthMask");
    }

    @Override
    public void glDepthRange(float gldepthmin, float gldepthmax) {
        gl.glDepthRange(gldepthmin, gldepthmax);
        checkError("glDepthRange");

    }

    @Override
    public void glDrawBuffer(int buf) {
        // specify which color buffers are to be drawn into

    }

    @Override
    public void glDrawElements(int mode, ShortBuffer srcIndexBuf) {
        prepareDraw();

        gl.glBindBuffer(WebGL.GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
        checkError("bindBuffer(el)");
        gl.glBufferData(WebGL.GL_ELEMENT_ARRAY_BUFFER, getWebGLArray(srcIndexBuf, WebGL.GL_UNSIGNED_SHORT),
                WebGL.GL_DYNAMIC_DRAW);
        checkError("bufferData(el)");

        int count = srcIndexBuf.remaining();
        gl.glDrawElements(mode, count, WebGL.GL_UNSIGNED_SHORT, 0);
        checkError("drawElements");
    }

    @Override
    public void glFinish() {
        gl.glFinish();
    }

    @Override
    public String glGetString(int id) {
        return gl.glGetString(id);
    }

    //  @Override
    //  public final void glHint(int h, int i) {
    //    gl.glHint(h, i);
    //  }

    @Override
    public void glPixelStorei(int i, int j) {
        gl.glPixelStorei(i, j);
    }

    @Override
    public void glPointParameterf(int id, float value) {
        // TODO Auto-generated method stub

    }

    @Override
    public void glPointSize(float value) {
        // TODO Auto-generated method stub

    }

    @Override
    public void glPolygonMode(int i, int j) {
        // TODO Auto-generated method stub

    }

    @Override
    public void glReadPixels(int x, int y, int width, int height, int glBgr, int glUnsignedByte, ByteBuffer image) {
        // TODO Auto-generated method stub

    }

    @Override
    public void glTexCoordPointer(int size, int byteStride, FloatBuffer buf) {
        glVertexAttribPointer(GLAdapter.ARRAY_TEXCOORD_0 + clientActiveTexture, size, GL_FLOAT, false, byteStride,
                buf);
        checkError("texCoordPointer");
    }

    @Override
    public void glTexEnvi(int target, int pid, int value) {
        texEnvMode[activeTexture] = value;
    }

    @Override
    public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border,
            int format, int type, ByteBuffer pixels) {

        textureFormat.set(boundTextureId[activeTexture], internalformat);
        WebGLArray<?> array = getWebGLArray(pixels, type);
        gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, array);
        checkError("glTexImage2D");
    }

    @Override
    public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border,
            int format, int type, IntBuffer pixels) {

        textureFormat.set(boundTextureId[activeTexture], internalformat);
        WebGLArray<?> array = getWebGLArray(pixels, type);
        gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, array);
        checkError("glTexImage2D");
    }

    @Override
    public void glTexParameteri(int glTexture2d, int glTextureMinFilter, int glFilterMin) {
        gl.glTexParameteri(glTexture2d, glTextureMinFilter, glFilterMin);
        checkError("glTexParameteri");
    }

    @Override
    public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format,
            int type, ByteBuffer pixels) {
        WebGLArray<?> array = getWebGLArray(pixels, type);
        gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, array);
        checkError("glTexSubImage2D");
    }

    @Override
    public void glTexSubImage2D(int target, int level, int xoffset, int yoffset, int width, int height, int format,
            int type, IntBuffer pixels) {
        WebGLArray<?> array = getWebGLArray(pixels, type);
        gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, array);
        checkError("glTexSubImage2D");
    }

    @Override
    public void glVertexPointer(int size, int byteStride, FloatBuffer buf) {
        glVertexAttribPointer(GLAdapter.ARRAY_POSITION, size, GL_FLOAT, false, byteStride, buf);
        checkError("glVertexPointer");

    }

    @Override
    public void setDisplayMode(DisplayMode displayMode) {
        canvas.setWidth(displayMode.width);
        canvas.setHeight(displayMode.height);
    }

    public DisplayMode[] getAvailableDisplayModes() {
        return new DisplayMode[] { getDisplayMode(),
                new DisplayMode(Window.getClientWidth(), Window.getClientHeight(), 32, 60) };
    }

    @Override
    public void shutdow() {
        // TODO Auto-generated method stub

    }

    @Override
    public void swapBuffers() {
        // TODO Auto-generated method stub

    }

    @Override
    public final void glBindTexture(int target, int textureId) {
        Texture texture = textures.get(textureId);
        if (texture == null) {
            texture = gl.glCreateTexture();
            textures.set(textureId, texture);
        }

        //    log ("binding texture " + texture + " id " + textureId + " for activeTexture: " + (activeTexture-GL_TEXTURE0));
        gl.glBindTexture(target, texture);
        checkError("glBindTexture");

        boundTextureId[activeTexture] = textureId;

        // glColor3f((float)Math.random(), (float)Math.random(), (float)Math.random());
    }

    @Override
    public final void glBlendFunc(int a, int b) {
        gl.glBlendFunc(a, b);
        checkError("glBlendFunc");

    }

    @Override
    public final void glClear(int mask) {
        gl.glClear(mask);
        checkError("glClear");

    }

    @Override
    public final void glColor4f(float red, float green, float blue, float alpha) {
        gl.glVertexAttrib4f(GLAdapter.ARRAY_COLOR, red, green, blue, alpha);
        checkError("glColor4f");
    }

    public void glTexImage2d(int target, int level, Element image) {
        //      log("setting texImage2d; image: " + image.getSrc());

        gl.glTexImage2D(target, level, image);
        checkError("texImage2D");
    }

    @Override
    public final void glEnable(int i) {
        if (i == GL_TEXTURE_2D) {
            switch (activeTexture) {
            case 0:
                gl.glUniform1i(uEnableTexture0, 1);
                break;
            case 1:
                gl.glUniform1i(uEnableTexture1, 1);
                break;
            default:
                throw new RuntimeException();
            }
        }
        gl.glEnable(i);
        checkError("glEnable");
    }

    @Override
    public final int glGetError() {
        return gl.glGetError();
    }

    @Override
    public final void glClearColor(float f, float g, float h, float i) {
        gl.glClearColor(f, g, h, i);
        checkError("glClearColor");
    }

    @Override
    public void glDrawArrays(int mode, int first, int count) {
        prepareDraw();
        //    log("drawArrays mode:" + mode + " first:" + first + " count:" +count);
        gl.glDrawArrays(mode, first, count);
        checkError("drawArrays");
    }

    public void checkError(String string) {
        //     int err = gl.glGetError();
        //   if (err != GL_NO_ERROR) {
        //      log("GL_ERROR in " + string + "(): " + err);
        //      //throw new RuntimeException("GL_ERROR in " + string + "(): " + err);
        //   }
    }

    public void updatTCBuffer(FloatBuffer buf, int offset, int count) {
        BufferData bd = bufferData[GLAdapter.ARRAY_TEXCOORD_0];
        gl.glBindBuffer(WebGL.GL_ARRAY_BUFFER, bd.buffer);

        int pos = buf.position();
        int limit = buf.limit();

        buf.position(pos + offset);
        buf.limit(pos + offset + count);

        WebGLArray<?> data = getWebGLArray(buf, GL_FLOAT);

        gl.glBufferSubData(WebGL.GL_ARRAY_BUFFER, offset * 4, data);

        buf.position(pos);
        buf.limit(limit);
    }

    private void prepareDraw() {
        if (updateMvpMatrix()) {
            gl.glUniformMatrix4fv(uMvpMatrix, false, WebGLFloatArray.create(mvpMatrix));
            checkError("prepareDraw");
        }

        gl.glUniform1i(uTexEnv0, getTextureMode(0));
        gl.glUniform1i(uTexEnv1, getTextureMode(1));

        //    StringBuilder sizes = new StringBuilder();

        for (int i = 0; i < SMALL_BUF_COUNT; i++) {
            BufferData bd = bufferData[i];
            if (bd.toBind != null) {
                gl.glBindBuffer(WebGL.GL_ARRAY_BUFFER, bd.buffer);
                checkError("bindBuffer" + i);

                //               int len = bd.toBind.getByteLength();
                //               if (len < bd.byteSize) {
                //                  gl.glBufferSubData(WebGL.GL_ARRAY_BUFFER, 0, bd.toBind);
                //               } else {
                //                  bd.byteSize = len;
                gl.glBufferData(WebGL.GL_ARRAY_BUFFER, bd.toBind, WebGL.GL_STREAM_DRAW);
                //               }
                checkError("bufferData" + i);

                gl.glVertexAttribPointer(i, bd.size, bd.type, bd.normalize, bd.byteStride, 0);
                checkError("vertexAttribPointer");

                bd.toBind = null;
            }
        }

        //    log ("prepDraw: " + sizes);
    }

    private int getTextureMode(int i) {
        return texEnvMode[i] == GL_REPLACE ? 0 : (textureFormats.get(boundTextureId[i]) == 3 ? 2 : 1);
    }

    @Override
    public final void glScissor(int i, int j, int width, int height) {
        gl.glScissor(i, j, width, height);
        checkError("glScissor");
    }

    @Override
    public void glTexParameterf(int target, int pname, float param) {
        gl.glTexParameterf(target, pname, param);
        checkError("glTexParameterf");
    }

    @Override
    public final void glEnableClientState(int i) {
        switch (i) {
        case GL_COLOR_ARRAY:
            gl.glEnableVertexAttribArray(GLAdapter.ARRAY_COLOR);
            checkError("enableClientState colorArr");
            break;
        case GL_VERTEX_ARRAY:
            gl.glEnableVertexAttribArray(GLAdapter.ARRAY_POSITION);
            checkError("enableClientState vertexArrr");
            break;
        case GL_TEXTURE_COORD_ARRAY:
            switch (clientActiveTexture) {
            case 0:
                gl.glEnableVertexAttribArray(GLAdapter.ARRAY_TEXCOORD_0);
                checkError("enableClientState texCoord0");
                break;
            case 1:
                gl.glEnableVertexAttribArray(GLAdapter.ARRAY_TEXCOORD_1);
                checkError("enableClientState texCoord1");
                break;
            default:
                throw new RuntimeException();
            }
            break;
        default:
            log("unsupported / unrecogized client state " + i);
        }
    }

    @Override
    public final void glDisableClientState(int i) {
        switch (i) {
        case GL_COLOR_ARRAY:
            gl.glDisableVertexAttribArray(GLAdapter.ARRAY_COLOR);
            break;
        case GL_VERTEX_ARRAY:
            gl.glDisableVertexAttribArray(GLAdapter.ARRAY_POSITION);
            break;
        case GL_TEXTURE_COORD_ARRAY:
            switch (clientActiveTexture) {
            case 0:
                gl.glDisableVertexAttribArray(GLAdapter.ARRAY_TEXCOORD_0);
                break;
            case 1:
                gl.glDisableVertexAttribArray(GLAdapter.ARRAY_TEXCOORD_1);
                break;
            default:
                throw new RuntimeException();
            }
            break;
        default:
            log("unsupported / unrecogized client state");
        }
        checkError("DisableClientState");
    }

    @Override
    public final void glDisable(int i) {
        if (i == GL_TEXTURE_2D) {
            switch (activeTexture) {
            case 0:
                gl.glUniform1i(uEnableTexture0, 0);
                break;
            case 1:
                gl.glUniform1i(uEnableTexture1, 0);
                break;
            default:
                throw new RuntimeException();
            }
        }
        gl.glDisable(i);
        checkError("glDisable");
    }

    @Override
    public final void glCullFace(int c) {
        gl.glCullFace(c);
        checkError("glCullFace");
    }

    @Override
    public final void glShadeModel(int s) {
    }

    @Override
    public final void glViewport(int x, int y, int w, int h) {
        super.glViewport(x, y, w, h);
        gl.glViewport(x, y, w, h);
        checkError("glViewport");
    }

    public void glVertexAttribPointer(int arrayId, int size, int type, boolean normalize, int byteStride,
            Buffer nioBuffer) {
        BufferData bd = bufferData[arrayId];
        bd.byteStride = byteStride;
        bd.size = size;
        bd.normalize = normalize;
        bd.type = type;
        WebGLArray<?> webGLArray = getWebGLArray(nioBuffer, type);
        bd.toBind = webGLArray;
    }

    public void glVertexAttribPointer(int arrayId, int size, int type, boolean normalize, int byteStride,
            int offset, Buffer nioBuffer, int staticDrawId) {
        WebGL.Buffer buffer = staticBuffers.get(staticDrawId);
        if (buffer == null) {
            buffer = gl.glCreateBuffer();
            staticBuffers.set(staticDrawId, buffer);
            gl.glBindBuffer(WebGL.GL_ARRAY_BUFFER, buffer);
            WebGLArray<?> webGLArray = getWebGLArray(nioBuffer, type);
            gl.glBufferData(WebGL.GL_ARRAY_BUFFER, webGLArray, WebGL.GL_STATIC_DRAW);
            checkError("bufferData");
            log("static buffer created; id: " + staticDrawId + " remaining: " + nioBuffer.remaining());
        }
        gl.glBindBuffer(WebGL.GL_ARRAY_BUFFER, buffer);
        gl.glVertexAttribPointer(arrayId, size, type, normalize, byteStride, offset);
        bufferData[arrayId].toBind = null;
        checkError("vertexAttribPointer");
    }

    private WebGLArray<?> getWebGLArray(Buffer buffer, int type) {

        int elementSize;
        HasWebGLArray arrayHolder;

        if (!(buffer instanceof HasWebGLArray)) {
            if (type != GL_BYTE && type != GL_UNSIGNED_BYTE) {
                log("buffer byte order problem");
                throw new RuntimeException("Buffer byte order problem");
            }
            if (buffer instanceof IntBuffer) {
                elementSize = 4;
            } else {
                throw new RuntimeException("NYI");
            }
            arrayHolder = (HasWebGLArray) ((ByteBufferWrapper) buffer).getByteBuffer();
        } else {
            arrayHolder = (HasWebGLArray) buffer;
            elementSize = arrayHolder.getElementSize();
        }

        WebGLArray<?> webGLArray = arrayHolder.getWebGLArray();
        int remainingBytes = buffer.remaining() * elementSize;

        int byteOffset = webGLArray.getByteOffset() + buffer.position() * elementSize;

        switch (type) {
        case WebGL.GL_FLOAT:
            return WebGLFloatArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes / 4);

        case WebGL.GL_UNSIGNED_BYTE:
            return WebGLUnsignedByteArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes);

        case WebGL.GL_UNSIGNED_SHORT:
            return WebGLUnsignedShortArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes / 2);

        case WebGL.GL_INT:
            return WebGLIntArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes / 4);

        case WebGL.GL_SHORT:
            return WebGLShortArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes / 2);

        case WebGL.GL_BYTE:
            return WebGLByteArray.create(webGLArray.getBuffer(), byteOffset, remainingBytes);
        }

        throw new IllegalArgumentException();
    }

    public void glGenerateMipmap(int t) {
        gl.glGenerateMipmap(t);
        checkError("genMipmap");
    }

}