package org.geogebra.web.geogebra3D.web.euclidian3D.openGL; import java.util.ArrayList; import org.geogebra.common.geogebra3D.euclidian3D.EuclidianView3D; import org.geogebra.common.geogebra3D.euclidian3D.draw.DrawLabel3D; import org.geogebra.common.geogebra3D.euclidian3D.openGL.GLBuffer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.GLBufferIndices; import org.geogebra.common.geogebra3D.euclidian3D.openGL.GPUBuffer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Manager; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Manager.Type; import org.geogebra.common.geogebra3D.euclidian3D.openGL.ManagerShadersElementsGlobalBuffer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.Renderer; import org.geogebra.common.geogebra3D.euclidian3D.openGL.RendererImplShaders; import org.geogebra.common.util.debug.Log; import org.geogebra.web.geogebra3D.web.euclidian3D.openGL.shaders.GpuBlacklist; import org.geogebra.web.geogebra3D.web.euclidian3D.openGL.shaders.ShaderProvider; import org.geogebra.web.html5.gawt.GBufferedImageW; import com.google.gwt.canvas.dom.client.ImageData; import com.google.gwt.dom.client.ImageElement; import com.googlecode.gwtgl.binding.WebGLFramebuffer; import com.googlecode.gwtgl.binding.WebGLProgram; import com.googlecode.gwtgl.binding.WebGLRenderbuffer; import com.googlecode.gwtgl.binding.WebGLRenderingContext; import com.googlecode.gwtgl.binding.WebGLShader; import com.googlecode.gwtgl.binding.WebGLTexture; import com.googlecode.gwtgl.binding.WebGLUniformLocation; /** * Renderer using shaders * * @author mathieu * */ public class RendererImplShadersW extends RendererImplShaders { private WebGLRenderingContext glContext; /** * Constructor * * @param renderer * GL renderer * * @param view * view * @param jogl * java openGL implementation */ public RendererImplShadersW(Renderer renderer, EuclidianView3D view) { super(renderer, view); Log.debug("============== RendererImplShadersW: Renderer with shaders created"); } /** * set GL context * * @param glContext * GL context */ public void setGL(WebGLRenderingContext glContext) { this.glContext = glContext; Log.debug("============== RendererImplShadersW: set GL context"); } private WebGLRenderingContext getGL() { return glContext; } private WebGLShader getShader(int type, String source) { WebGLShader shader = glContext.createShader(type); glContext.shaderSource(shader, source); glContext.compileShader(shader); if (!glContext.getShaderParameterb(shader, WebGLRenderingContext.COMPILE_STATUS)) { Log.debug("ERROR COMPILING SHADER: " + glContext.getShaderInfoLog(shader)); throw new RuntimeException(glContext.getShaderInfoLog(shader)); } return shader; } @Override final protected void compileShadersProgram() { boolean needsSmallFragmentShader = GpuBlacklist .isCurrentGpuBlacklisted(glContext); fragShader = getShader( WebGLRenderingContext.FRAGMENT_SHADER, ShaderProvider.getFragmentShader(needsSmallFragmentShader, true)); vertShader = getShader( WebGLRenderingContext.VERTEX_SHADER, ShaderProvider.getVertexShader(needsSmallFragmentShader, true)); } @Override final protected Object glCreateProgram() { return glContext.createProgram(); } @Override final protected void glAttachShader(Object shader) { glContext.attachShader((WebGLProgram) shaderProgram, (WebGLShader) shader); } @Override final protected void glBindAttribLocation(int index, String name) { glContext.bindAttribLocation((WebGLProgram) shaderProgram, index, name); } @Override final protected void glLinkProgram() { glContext.linkProgram((WebGLProgram) shaderProgram); if (!glContext.getProgramParameterb((WebGLProgram) shaderProgram, WebGLRenderingContext.LINK_STATUS)) { throw new RuntimeException("Could not initialise shaders"); } // use the program glContext.useProgram((WebGLProgram) shaderProgram); } @Override final protected Object glGetUniformLocation(String name) { return glContext.getUniformLocation((WebGLProgram) shaderProgram, name); } @Override final protected void createVBOs() { vboColors = new GPUBufferW(); vboVertices = new GPUBufferW(); vboNormals = new GPUBufferW(); vboTextureCoords = new GPUBufferW(); vboIndices = new GPUBufferW(); vboColors.set(glContext.createBuffer()); vboVertices.set(glContext.createBuffer()); vboNormals.set(glContext.createBuffer()); vboTextureCoords.set(glContext.createBuffer()); vboIndices.set(glContext.createBuffer()); } @Override final protected void createBufferFor(GPUBuffer buffer) { buffer.set(glContext.createBuffer()); } @Override final protected int getStoreBufferNumBytes(int length, int size) { return length * size * 4; // 4 bytes per float } final static private int GL_TYPE_DRAW_TO_BUFFER = WebGLRenderingContext.STREAM_DRAW; @Override public void storeElementBuffer(short[] fb, int length, GPUBuffer buffers) { // Select the VBO, GPU memory data bindBufferForIndices(buffers); // transfer data to VBO, this perform the copy of data from CPU -> GPU // memory glContext.bufferData(WebGLRenderingContext.ELEMENT_ARRAY_BUFFER, MyInt16Array.create(fb), GL_TYPE_DRAW_TO_BUFFER); } @Override final protected void bindBuffer(int bufferType, GPUBuffer buffer) { glContext.bindBuffer(bufferType, ((GPUBufferW) buffer).get()); } @Override final protected int getGL_ELEMENT_ARRAY_BUFFER() { return WebGLRenderingContext.ELEMENT_ARRAY_BUFFER; } @Override final protected int getGL_ARRAY_BUFFER() { return WebGLRenderingContext.ARRAY_BUFFER; } @Override protected void vertexAttribPointer(int attrib, int size) { glContext.vertexAttribPointer(attrib, size, WebGLRenderingContext.FLOAT, false, 0, 0); } @Override protected void glUniform3fv(Object location, float[] values) { glContext.uniform3fv((WebGLUniformLocation) location, values); } @Override protected void glUniform3f(Object location, float x, float y, float z){ glContext.uniform3f((WebGLUniformLocation) location, x, y, z); } @Override protected void glEnableVertexAttribArray(int attrib) { glContext.enableVertexAttribArray(attrib); } @Override protected void glBufferData(int numBytes, GLBuffer fb) { glContext.bufferData(WebGLRenderingContext.ARRAY_BUFFER, ((GLBufferW) fb).getBuffer(), GL_TYPE_DRAW_TO_BUFFER); } @Override protected void glBufferDataIndices(int numBytes, GLBufferIndices arrayI) { glContext .bufferData(WebGLRenderingContext.ELEMENT_ARRAY_BUFFER, ((GLBufferIndicesW) arrayI).getBuffer(), GL_TYPE_DRAW_TO_BUFFER); } @Override public void draw(Manager.Type type, int length) { glContext.drawElements(getGLType(type), length, WebGLRenderingContext.UNSIGNED_SHORT, 0); } @Override protected int getGLType(Type type) { switch (type) { case TRIANGLE_STRIP: return WebGLRenderingContext.TRIANGLE_STRIP; case TRIANGLE_FAN: // if (Browser.supportsWebGLTriangleFan()){ // no TRIANGLE_FAN for // internet explorer // return WebGLRenderingContext.TRIANGLE_FAN; // } // wait for fix : detect webGL support correctly return WebGLRenderingContext.TRIANGLE_STRIP; case TRIANGLES: return WebGLRenderingContext.TRIANGLES; case LINE_LOOP: return WebGLRenderingContext.LINE_LOOP; case LINE_STRIP: return WebGLRenderingContext.LINE_STRIP; } return WebGLRenderingContext.TRIANGLES; } @Override protected final void glUniformMatrix4fv(Object location, float[] values) { glContext.uniformMatrix4fv((WebGLUniformLocation) location, false, values); } @Override final protected void glUseProgram(Object program) { glContext.useProgram((WebGLProgram) program); } @Override final protected void glDisableVertexAttribArray(int attrib) { glContext.disableVertexAttribArray(attrib); } @Override final protected void glDetachAndDeleteShader(Object program, Object shader) { glContext.detachShader((WebGLProgram) program, (WebGLShader) shader); glContext.deleteShader((WebGLShader) shader); } @Override final protected void glDeleteProgram(Object program) { glContext.deleteProgram((WebGLProgram) program); } @Override final protected void glUniform4f(Object location, float a, float b, float c, float d) { glContext.uniform4f((WebGLUniformLocation) location, a, b, c, d); } @Override final protected void glUniform4fv(Object location, float[] values) { glContext.uniform4fv((WebGLUniformLocation) location, values); } @Override final protected void glUniform2fv(Object location, float[] values) { glContext.uniform2fv((WebGLUniformLocation) location, values); } @Override public void setColorMaterial() { // not used in WebGL } @Override protected void glViewPort(int width, int height) { glContext.viewport(0, 0, width, height); } @Override public Manager createManager() { return new ManagerShadersElementsGlobalBuffer(renderer, view3D); } @Override protected void glUniform1i(Object location, int value) { glContext.uniform1i((WebGLUniformLocation) location, value); } @Override protected void glUniform1fv(Object location, int length, float[] values) { glContext.uniform1fv((WebGLUniformLocation) location, values); } @Override public float[] getLightPosition() { return Renderer.LIGHT_POSITION_W; } @Override final protected void glCullFace(int flag) { getGL().cullFace(flag); } @Override final protected int getGL_FRONT() { return WebGLRenderingContext.FRONT; } @Override final protected int getGL_BACK() { return WebGLRenderingContext.BACK; } @Override public void setBufferLeft() { // not used in web } @Override public void setBufferRight() { // not used in web } @Override public void setStencilFunc(int value) { // not used in web } @Override final protected void glDepthMask(boolean flag) { getGL().depthMask(flag); } @Override public void setColorMask(boolean r, boolean g, boolean b, boolean a) { getGL().colorMask(r, g, b, a); } @Override public void setClearColor(float r, float g, float b, float a) { getGL().clearColor(r, g, b, a); } @Override public void setPolygonOffset(float factor, float units) { getGL().polygonOffset(factor, units); } @Override public void genTextures2D(int number, int[] index) { int size = texturesArray.size(); for (int i = 0; i < number; i++) { // add new textures index[i] = size + i; texturesArray.add(glContext.createTexture()); } } private ArrayList<WebGLTexture> texturesArray = new ArrayList<WebGLTexture>(); @Override public void bindTexture(int index) { glContext.bindTexture(WebGLRenderingContext.TEXTURE_2D, texturesArray.get(index)); } @Override public void glEnable(int flag) { getGL().enable(flag); } @Override public void glDisable(int flag) { getGL().disable(flag); } @Override public final void enableMultisample() { // not used in web } @Override public final void disableMultisample() { // not used in web } @Override public int getGL_BLEND() { return WebGLRenderingContext.BLEND; } @Override public int getGL_CULL_FACE() { return WebGLRenderingContext.CULL_FACE; } @Override public void glClear(int flag) { getGL().clear(flag); } @Override public int getGL_COLOR_BUFFER_BIT() { return WebGLRenderingContext.COLOR_BUFFER_BIT; } @Override public int getGL_DEPTH_BUFFER_BIT() { return WebGLRenderingContext.DEPTH_BUFFER_BIT; } @Override public int getGL_DEPTH_TEST() { return WebGLRenderingContext.DEPTH_TEST; } /** * create alpha texture from image for the label * * @param label * label * @param image * image * @param bimg * buffered image */ public void createAlphaTexture(DrawLabel3D label, ImageElement image, GBufferedImageW bimg) { if (label.isPickable()) { // values for picking (ignore transparent bytes) ImageData data = bimg.getImageData(); int xmin = label.getWidth(), xmax = 0, ymin = label.getHeight(), ymax = 0; for (int y = 0; y < label.getHeight(); y++) { for (int x = 0; x < label.getWidth(); x++) { int alpha = data.getAlphaAt(x, y); if (alpha != 0) { if (x < xmin) { xmin = x; } if (x > xmax) { xmax = x; } if (y < ymin) { ymin = y; } if (y > ymax) { ymax = y; } } } } label.setPickingDimension(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); } // create texture WebGLTexture texture; int textureIndex = label.getTextureIndex(); if (textureIndex == -1) { textureIndex = texturesArray.size(); texture = glContext.createTexture(); texturesArray.add(texture); } else { texture = texturesArray.get(textureIndex); } glContext.bindTexture(WebGLRenderingContext.TEXTURE_2D, texture); glContext.texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, image); glContext.generateMipmap(WebGLRenderingContext.TEXTURE_2D); glContext.bindTexture(WebGLRenderingContext.TEXTURE_2D, null); label.setTextureIndex(textureIndex); } @Override protected void bindFramebuffer(Object id) { getGL().bindFramebuffer(WebGLRenderingContext.FRAMEBUFFER, (WebGLFramebuffer) id); } @Override protected void bindRenderbuffer(Object id) { getGL().bindRenderbuffer(WebGLRenderingContext.RENDERBUFFER, (WebGLRenderbuffer) id); } @Override protected void unbindFramebuffer() { bindFramebuffer(null); } @Override protected void unbindRenderbuffer() { bindRenderbuffer(null); } @Override protected void textureParametersNearest() { getGL().texParameterf(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MAG_FILTER, WebGLRenderingContext.NEAREST); getGL().texParameterf(WebGLRenderingContext.TEXTURE_2D, WebGLRenderingContext.TEXTURE_MIN_FILTER, WebGLRenderingContext.NEAREST); } @Override protected void textureImage2DForBuffer(int width, int height) { getGL().texImage2D(WebGLRenderingContext.TEXTURE_2D, 0, WebGLRenderingContext.RGBA, width, height, 0, WebGLRenderingContext.RGBA, WebGLRenderingContext.UNSIGNED_BYTE, null); } @Override protected void renderbufferStorage(int width, int height) { getGL().renderbufferStorage(WebGLRenderingContext.RENDERBUFFER, WebGLRenderingContext.DEPTH_COMPONENT, width, height); } protected Object genTexture() { return getGL().createTexture(); } @Override protected Object genRenderbuffer() { return getGL().createRenderbuffer(); } @Override protected Object genFramebuffer() { return getGL().createFramebuffer(); } @Override protected void framebuffer(Object colorId, Object depthId) { getGL().framebufferTexture2D(WebGLRenderingContext.FRAMEBUFFER, WebGLRenderingContext.COLOR_ATTACHMENT0, WebGLRenderingContext.TEXTURE_2D, (WebGLTexture) colorId, 0); getGL().framebufferRenderbuffer(WebGLRenderingContext.FRAMEBUFFER, WebGLRenderingContext.DEPTH_ATTACHMENT, WebGLRenderingContext.RENDERBUFFER, (WebGLRenderbuffer) depthId); } @Override protected boolean checkFramebufferStatus() { return getGL() .checkFramebufferStatus( WebGLRenderingContext.FRAMEBUFFER) == WebGLRenderingContext.FRAMEBUFFER_COMPLETE; } }