private void bindWebGL() {
    // Adds the Surface3D to the document.
    Surface3D surface = new Surface3D(500, 500);
    RootPanel.get().add(surface);
    final GL2 gl = surface.getGL();
    if (gl == null) {
      Window.alert("No WebGL context found. Exiting.");
      return;
    }

    // Sets up the GL context.
    gl.clearColor(0.0f, 0f, 0f, 1f);
    gl.clearDepth(1);
    gl.viewport(0, 0, surface.getWidth(), surface.getHeight());

    gl.enable(EnableCap.DEPTH_TEST);
    gl.depthFunc(DepthFunction.LEQUAL);
    gl.clear(ClearBufferMask.COLOR_BUFFER_BIT, ClearBufferMask.DEPTH_BUFFER_BIT);

    // Creates a lambertian shader.
    LambertianShader shader = new LambertianShader();
    try {
      shader.init(gl);
    } catch (ShaderException e) {
      Window.alert("Error loading the shader.");
      return;
    }

    // Binds the shader.
    shader.bind();

    // Creates a sphere.
    StaticMesh mesh = new StaticMesh(gl, PrimitiveFactory.makeSphere(30, 30));
    mesh.setPositionIndex(shader.getAttributePosition());
    mesh.setNormalIndex(shader.getAttributeNormal());

    shader.setLightPosition(0, 5, 5);
    shader.setDiffuseColor(1, 0, 0, 1);

    // Sets up the model view matrix.
    MatrixStack.MODELVIEW.push();
    MatrixStack.MODELVIEW.translate(0, 0, -5);
    shader.setModelViewMatrix(MatrixStack.MODELVIEW.get());
    MatrixStack.MODELVIEW.pop();

    // Sets up a basic camera for projection.
    MatrixStack.PROJECTION.pushIdentity();
    MatrixStack.PROJECTION.perspective(45, 1, .1f, 100);
    shader.setProjectionMatrix(MatrixStack.PROJECTION.get());
    MatrixStack.PROJECTION.pop();

    // Draws the mesh.
    mesh.draw();

    mesh.dispose();
    shader.dispose();
  }
  /** Create and initialize the WebGL context. */
  public final boolean initWebGLContext() {
    if (gl == null) {
      gl =
          GL2ContextHelper.getGL2(
              getWidget(),
              new WebGLContextAttributes() {
                {
                  setStencilEnable(true);
                }
              });

      if (gl == null) {
        return false;
      }
    }

    float[] cc = getState().clearColor;
    gl.clearColor(cc[0], cc[1], cc[2], cc[3]);
    gl.clearDepth(1);

    gl.enable(EnableCap.DEPTH_TEST);
    gl.depthFunc(DepthFunction.LEQUAL);
    gl.clear(ClearBufferMask.COLOR_BUFFER_BIT, ClearBufferMask.DEPTH_BUFFER_BIT);

    try {
      shader =
          new AbstractShader() {
            @Override
            protected void initImpl() throws ShaderException {
              initProgram(getState().vertexShaderSource, getState().fragmentShaderSource);
            }
          };

      shader.init(gl);
      shader.bind();
    } catch (ShaderException e) {
      Window.alert(e.getMessage());
      return false;
    }

    vertexPositionAttribute = shader.getAttributeLocation("aVertexPosition");
    gl.enableVertexAttribArray(vertexPositionAttribute);

    vertexColorAttribute = shader.getAttributeLocation("aVertexColor");
    gl.enableVertexAttribArray(vertexColorAttribute);

    PROJECTION.pushIdentity();
    PROJECTION.perspective(getState().fov, 1, getState().minDist, getState().maxDist);
    gl.uniformMatrix(shader.getUniformLocation("uPMatrix"), PROJECTION.get());
    PROJECTION.pop();

    buildBuffers(getState().shapes);

    return true;
  }
  public void drawShapes() {
    gl.clear(ClearBufferMask.COLOR_BUFFER_BIT, ClearBufferMask.DEPTH_BUFFER_BIT);

    MODELVIEW.push();
    float[] gt = getState().translation;
    if (gt != null) {
      MODELVIEW.translate(gt[0], gt[1], gt[2]);
    }

    for (int i = 0; i < vertices.length; ++i) {
      WebGLState s = getState();

      MODELVIEW.push();
      MODELVIEW.translate(s.shapes[i].tx, s.shapes[i].ty, s.shapes[i].tz);

      MODELVIEW.rotateX(s.shapes[i].rx);
      MODELVIEW.rotateY(s.shapes[i].ry);
      MODELVIEW.rotateZ(s.shapes[i].rz);

      setMatrixUniforms();
      MODELVIEW.pop();

      gl.bindBuffer(BufferTarget.ARRAY_BUFFER, vertices[i]);
      gl.vertexAttribPointer(vertexPositionAttribute, 3, DataType.FLOAT, false, 0, 0);

      gl.bindBuffer(BufferTarget.ARRAY_BUFFER, colors[i]);
      gl.vertexAttribPointer(vertexColorAttribute, 4, DataType.FLOAT, false, 0, 0);

      if (indices[i] != null) {
        gl.bindBuffer(BufferTarget.ELEMENT_ARRAY_BUFFER, indices[i]);
        gl.drawElements(BeginMode.TRIANGLES, 36, DrawElementsType.UNSIGNED_SHORT, 0);
      } else {
        gl.drawArrays(BeginMode.TRIANGLE_STRIP, 0, 3);
      }
    }

    MODELVIEW.pop();
  }