/*
* 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 com.googlecode.gwtquake.client;
import static com.google.gwt.webgl.client.WebGLRenderingContext.ARRAY_BUFFER;
import static com.google.gwt.webgl.client.WebGLRenderingContext.BYTE;
import static com.google.gwt.webgl.client.WebGLRenderingContext.COMPILE_STATUS;
import static com.google.gwt.webgl.client.WebGLRenderingContext.DYNAMIC_DRAW;
import static com.google.gwt.webgl.client.WebGLRenderingContext.ELEMENT_ARRAY_BUFFER;
import static com.google.gwt.webgl.client.WebGLRenderingContext.FLOAT;
import static com.google.gwt.webgl.client.WebGLRenderingContext.FRAGMENT_SHADER;
import static com.google.gwt.webgl.client.WebGLRenderingContext.INT;
import static com.google.gwt.webgl.client.WebGLRenderingContext.LINK_STATUS;
import static com.google.gwt.webgl.client.WebGLRenderingContext.NO_ERROR;
import static com.google.gwt.webgl.client.WebGLRenderingContext.SHORT;
import static com.google.gwt.webgl.client.WebGLRenderingContext.STATIC_DRAW;
import static com.google.gwt.webgl.client.WebGLRenderingContext.STREAM_DRAW;
import static com.google.gwt.webgl.client.WebGLRenderingContext.UNSIGNED_BYTE;
import static com.google.gwt.webgl.client.WebGLRenderingContext.UNSIGNED_SHORT;
import static com.google.gwt.webgl.client.WebGLRenderingContext.VERTEX_SHADER;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
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.dom.client.ImageElement;
import com.google.gwt.html5.client.CanvasElement;
import com.googlecode.gwtgl.array.ArrayBufferView;
import com.googlecode.gwtgl.array.Float32Array;
import com.googlecode.gwtgl.array.Int16Array;
import com.googlecode.gwtgl.array.Int32Array;
import com.googlecode.gwtgl.array.Int8Array;
import com.googlecode.gwtgl.array.Uint16Array;
import com.googlecode.gwtgl.array.Uint8Array;
import com.google.gwt.user.client.Window;
import com.googlecode.gwtgl.binding.WebGLBuffer;
import com.googlecode.gwtgl.binding.WebGLProgram;
import com.google.gwt.webgl.client.WebGLRenderingContext;
import com.googlecode.gwtgl.binding.WebGLShader;
import com.googlecode.gwtgl.binding.WebGLTexture;
import com.googlecode.gwtgl.binding.WebGLUniformLocation;
import com.googlecode.gwtquake.shared.render.DisplayMode;
import com.googlecode.gwtquake.shared.render.Gl1Context;
/**
* Partial mapping of GL1.x to WebGL.
*
* @author Stefan Haustein
*/
@SuppressWarnings("unchecked")
public class WebGLGl1Contect extends Gl1Context {
static final int SMALL_BUF_COUNT = 4;
WebGLUniformLocation uMvpMatrix;
WebGLUniformLocation uSampler0;
WebGLUniformLocation uSampler1;
WebGLUniformLocation uTexEnv0;
WebGLUniformLocation uTexEnv1;
WebGLUniformLocation uEnableTexture0;
WebGLUniformLocation uEnableTexture1;
JsArray<WebGLBuffer> staticBuffers = (JsArray<WebGLBuffer>) JavaScriptObject.createArray();
class BufferData {
ArrayBufferView toBind;
WebGLBuffer buffer;
int byteStride;
int size;
int type;
int byteSize;
boolean normalize;
}
private BufferData[] bufferData = new BufferData[SMALL_BUF_COUNT];
public final WebGLRenderingContext gl;
int logCount = 0;
public void log(String msg) {
if (logCount >= 1000) {
return;
}
System.out.println((logCount++) + ": " + msg);
};
FloatBuffer colorBuffer;
private CanvasElement canvas;
JsArray<WebGLTexture> textures = (JsArray<WebGLTexture>) 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 WebGLBuffer elementBuffer;
public WebGLGl1Contect(CanvasElement canvas) {
this.canvas = canvas;
gl = canvas.getContextWebGL();
if (gl == null) {
throw new UnsupportedOperationException("WebGL N/A");
}
initShader();
checkError("initShader");
elementBuffer = gl.createBuffer();
checkError("createBuffer f. elements");
for (int i = 0; i < bufferData.length; i++) {
BufferData bd = new BufferData();
bd.buffer = gl.createBuffer();
checkError("createBuffer" + i);
bufferData[i] = bd;
}
}
private WebGLShader loadShader(int shaderType, String shaderSource) {
// Create the shader object
WebGLShader shader = gl.createShader(shaderType);
if (shader == null) {
throw new RuntimeException();
}
// Load the shader source
gl.shaderSource(shader, shaderSource);
// Compile the shader
gl.compileShader(shader);
// Check the compile status
boolean compiled = gl.getShaderParameterb(shader, COMPILE_STATUS);
if (!compiled) {
// Something went wrong during compilation; get the error
throw new RuntimeException("Shader compile error: "
+ gl.getShaderInfoLog(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 = "#ifdef GL_ES\n"
+ "precision mediump float;\n" + "#endif\n"
+ "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" +
"float gamma = 1.5;\n" +
"float igamma = 1.0 / gamma;\n" +
"gl_FragColor = vec4(pow(finalColor.r, igamma), pow(finalColor.g, igamma), pow(finalColor.b, igamma), finalColor.a);" +
"}\n";
// create our shaders
WebGLShader vertexShader = loadShader(VERTEX_SHADER, vertexShaderSource);
WebGLShader fragmentShader = loadShader(FRAGMENT_SHADER,
fragmentShaderSource);
if (vertexShader == null || fragmentShader == null) {
log("Shader error");
throw new RuntimeException("shader error");
}
// Create the program object
WebGLProgram programObject = gl.createProgram();
if (programObject == null || gl.getError() != NO_ERROR) {
log("Program errror");
throw new RuntimeException("program error");
}
// Attach our two shaders to the program
gl.attachShader(programObject, vertexShader);
gl.attachShader(programObject, fragmentShader);
// Bind "vPosition" to attribute 0
gl.bindAttribLocation(programObject, Gl1Context.ARRAY_POSITION, "a_position");
gl.bindAttribLocation(programObject, Gl1Context.ARRAY_COLOR, "a_color");
gl.bindAttribLocation(programObject, Gl1Context.ARRAY_TEXCOORD_0,
"a_texCoord0");
gl.bindAttribLocation(programObject, Gl1Context.ARRAY_TEXCOORD_1,
"a_texCoord1");
// Link the program
gl.linkProgram(programObject);
// TODO(haustein) get position, color from the linker, too
uMvpMatrix = gl.getUniformLocation(programObject, "u_mvpMatrix");
uSampler0 = gl.getUniformLocation(programObject, "s_texture0");
uSampler1 = gl.getUniformLocation(programObject, "s_texture1");
uTexEnv0 = gl.getUniformLocation(programObject, "s_texEnv0");
uTexEnv1 = gl.getUniformLocation(programObject, "s_texEnv1");
uEnableTexture0 = gl.getUniformLocation(programObject, "u_enable_texture_0");
uEnableTexture1 = gl.getUniformLocation(programObject, "u_enable_texture_1");
// // Check the link status
boolean linked = gl.getProgramParameterb(programObject, LINK_STATUS);
if (!linked) {
throw new RuntimeException("linker Error: "
+ gl.getProgramInfoLog(programObject));
}
gl.useProgram(programObject);
gl.uniform1i(uSampler0, 0);
gl.uniform1i(uSampler1, 1);
gl.activeTexture(GL_TEXTURE0);
}
public String webGLFloatArrayToString(Float32Array 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(Int32Array 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(Uint16Array 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.activeTexture(texture);
activeTexture = texture - GL_TEXTURE0;
checkError("glActiveTexture");
}
@Override
public void glAlphaFunc(int i, float j) {
// TODO: Remove this. Alpha text/func are unsupported in ES.
}
@Override
public void glClientActiveTexture(int texture) {
clientActiveTexture = texture - GL_TEXTURE0;
}
@Override
public void glColorPointer(int size, int stride, FloatBuffer colorArrayBuf) {
glColorPointer(size, FLOAT, stride, colorArrayBuf);
}
@Override
public void glColorPointer(int size, boolean unsigned, int stride,
ByteBuffer colorAsByteBuffer) {
glColorPointer(size, unsigned ? UNSIGNED_BYTE : BYTE, stride,
colorAsByteBuffer);
}
private final void glColorPointer(int size, int type, int stride, Buffer buf) {
glVertexAttribPointer(Gl1Context.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.deleteTexture(textures.get(tid));
textures.set(tid, null);
checkError("glDeleteTexture");
}
}
@Override
public void glDepthFunc(int func) {
gl.depthFunc(func);
checkError("glDepthFunc");
}
@Override
public void glDepthMask(boolean b) {
gl.depthMask(b);
checkError("glDepthMask");
}
@Override
public void glDepthRange(float gldepthmin, float gldepthmax) {
gl.depthRange(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.bindBuffer(ELEMENT_ARRAY_BUFFER, elementBuffer);
checkError("bindBuffer(el)");
gl.bufferData(ELEMENT_ARRAY_BUFFER, getTypedArray(srcIndexBuf,
UNSIGNED_SHORT), DYNAMIC_DRAW);
checkError("bufferData(el)");
int count = srcIndexBuf.remaining();
gl.drawElements(mode, count, UNSIGNED_SHORT, 0);
checkError("drawElements");
}
@Override
public void glFinish() {
gl.finish();
}
@Override
public String glGetString(int id) {
// TODO: Where is getParameter()?
// String s = gl.getParameter(id);
//return s == null ? "" : s;
return "glGetString not implemented";
}
@Override
public void glPixelStorei(int i, int j) {
gl.pixelStorei(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(Gl1Context.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);
ArrayBufferView array = getTypedArray(pixels, type);
gl.texImage2D(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);
ArrayBufferView array = getTypedArray(pixels, type);
gl.texImage2D(target, level, internalformat, width, height, border,
format, type, array);
checkError("glTexImage2D");
}
@Override
public void glTexParameteri(int glTexture2d, int glTextureMinFilter,
int glFilterMin) {
gl.texParameteri(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) {
ArrayBufferView array = getTypedArray(pixels, type);
gl.texSubImage2D(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) {
ArrayBufferView array = getTypedArray(pixels, type);
gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format,
type, array);
checkError("glTexSubImage2D");
}
@Override
public void glVertexPointer(int size, int byteStride, FloatBuffer buf) {
glVertexAttribPointer(Gl1Context.ARRAY_POSITION, size, GL_FLOAT, false,
byteStride, buf);
checkError("glVertexPointer");
}
@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) {
WebGLTexture texture = textures.get(textureId);
if (texture == null) {
texture = gl.createTexture();
textures.set(textureId, texture);
}
// log ("binding texture " + texture + " id " + textureId +
// " for activeTexture: " + (activeTexture-GL_TEXTURE0));
gl.bindTexture(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.blendFunc(a, b);
checkError("glBlendFunc");
}
@Override
public final void glClear(int mask) {
gl.clear(mask);
checkError("glClear");
}
@Override
public final void glColor4f(float red, float green, float blue, float alpha) {
gl.vertexAttrib4f(Gl1Context.ARRAY_COLOR, red, green, blue, alpha);
checkError("glColor4f");
}
public void glTexImage2d(int target, int level, int internalformat,
int format, int type, ImageElement image) {
// log("setting texImage2d; image: " + image.getSrc());
gl.texImage2D(target, level, internalformat, format, type, image);
checkError("texImage2D");
}
public void glTexImage2d(int target, int level, int internalformat,
int format, int type, CanvasElement image) {
// log("setting texImage2d; image: " + image.getSrc());
gl.texImage2D(target, level, internalformat, format, type, image);
checkError("texImage2D");
}
@Override
public final void glEnable(int i) {
// In ES, you don't enable/disable TEXTURE_2D. We use it this call to
// enable one of the two active textures supported by the shader.
if (i == GL_TEXTURE_2D) {
switch (activeTexture) {
case 0:
gl.uniform1i(uEnableTexture0, 1);
break;
case 1:
gl.uniform1i(uEnableTexture1, 1);
break;
default:
throw new RuntimeException();
}
return;
}
gl.enable(i);
checkError("glEnable");
}
@Override
public final int glGetError() {
return gl.getError();
}
@Override
public final void glClearColor(float f, float g, float h, float i) {
gl.clearColor(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.drawArrays(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[Gl1Context.ARRAY_TEXCOORD_0];
gl.bindBuffer(ARRAY_BUFFER, bd.buffer);
int pos = buf.position();
int limit = buf.limit();
buf.position(pos + offset);
buf.limit(pos + offset + count);
ArrayBufferView data = getTypedArray(buf, GL_FLOAT);
gl.bufferSubData(ARRAY_BUFFER, offset * 4, data);
buf.position(pos);
buf.limit(limit);
}
private void prepareDraw() {
if (updateMvpMatrix()) {
gl.uniformMatrix4fv(uMvpMatrix, false, Float32Array.create(mvpMatrix));
checkError("prepareDraw");
}
gl.uniform1i(uTexEnv0, getTextureMode(0));
gl.uniform1i(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.bindBuffer(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.bufferData(ARRAY_BUFFER, bd.toBind, STREAM_DRAW);
// }
checkError("bufferData" + i);
gl.vertexAttribPointer(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.scissor(i, j, width, height);
checkError("glScissor");
}
@Override
public void glTexParameterf(int target, int pname, float param) {
gl.texParameterf(target, pname, param);
checkError("glTexParameterf");
}
@Override
public final void glEnableClientState(int i) {
switch (i) {
case GL_COLOR_ARRAY:
gl.enableVertexAttribArray(Gl1Context.ARRAY_COLOR);
checkError("enableClientState colorArr");
break;
case GL_VERTEX_ARRAY:
gl.enableVertexAttribArray(Gl1Context.ARRAY_POSITION);
checkError("enableClientState vertexArrr");
break;
case GL_TEXTURE_COORD_ARRAY:
switch (clientActiveTexture) {
case 0:
gl.enableVertexAttribArray(Gl1Context.ARRAY_TEXCOORD_0);
checkError("enableClientState texCoord0");
break;
case 1:
gl.enableVertexAttribArray(Gl1Context.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.disableVertexAttribArray(Gl1Context.ARRAY_COLOR);
break;
case GL_VERTEX_ARRAY:
gl.disableVertexAttribArray(Gl1Context.ARRAY_POSITION);
break;
case GL_TEXTURE_COORD_ARRAY:
switch (clientActiveTexture) {
case 0:
gl.disableVertexAttribArray(Gl1Context.ARRAY_TEXCOORD_0);
break;
case 1:
gl.disableVertexAttribArray(Gl1Context.ARRAY_TEXCOORD_1);
break;
default:
throw new RuntimeException();
}
break;
default:
log("unsupported / unrecogized client state");
}
checkError("DisableClientState");
}
@Override
public final void glDisable(int i) {
// In ES, you don't enable/disable TEXTURE_2D. We use it this call to
// disable one of the two active textures supported by the shader.
if (i == GL_TEXTURE_2D) {
switch (activeTexture) {
case 0:
gl.uniform1i(uEnableTexture0, 0);
break;
case 1:
gl.uniform1i(uEnableTexture1, 0);
break;
default:
throw new RuntimeException();
}
return;
}
gl.disable(i);
checkError("glDisable");
}
@Override
public final void glCullFace(int c) {
gl.cullFace(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.viewport(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;
ArrayBufferView webGLArray = getTypedArray(nioBuffer, type);
bd.toBind = webGLArray;
}
public void glVertexAttribPointer(int arrayId, int size, int type,
boolean normalize, int byteStride, int offset, Buffer nioBuffer,
int staticDrawId) {
WebGLBuffer buffer = staticBuffers.get(staticDrawId);
if (buffer == null) {
buffer = gl.createBuffer();
staticBuffers.set(staticDrawId, buffer);
gl.bindBuffer(ARRAY_BUFFER, buffer);
ArrayBufferView webGLArray = getTypedArray(nioBuffer, type);
gl.bufferData(ARRAY_BUFFER, webGLArray, STATIC_DRAW);
checkError("bufferData");
log("static buffer created; id: " + staticDrawId + " remaining: "
+ nioBuffer.remaining());
}
gl.bindBuffer(ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(arrayId, size, type, normalize, byteStride, offset);
bufferData[arrayId].toBind = null;
checkError("vertexAttribPointer");
}
private ArrayBufferView getTypedArray(Buffer buffer, int type) {
int elementSize;
HasArrayBufferView arrayHolder;
if (!(buffer instanceof HasArrayBufferView)) {
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 = (HasArrayBufferView) ((ByteBufferWrapper) buffer).getByteBuffer();
} else {
arrayHolder = (HasArrayBufferView) buffer;
elementSize = arrayHolder.getElementSize();
}
ArrayBufferView webGLArray = arrayHolder.getTypedArray();
int remainingBytes = buffer.remaining() * elementSize;
int byteOffset = webGLArray.getByteOffset() + buffer.position()
* elementSize;
switch (type) {
case FLOAT:
return Float32Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes / 4);
case UNSIGNED_BYTE:
return Uint8Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes);
case UNSIGNED_SHORT:
return Uint16Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes / 2);
case INT:
return Int32Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes / 4);
case SHORT:
return Int16Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes / 2);
case BYTE:
return Int8Array.create(webGLArray.getBuffer(), byteOffset,
remainingBytes);
}
throw new IllegalArgumentException();
}
public void glGenerateMipmap(int t) {
gl.generateMipmap(t);
checkError("genMipmap");
}
}